We are very proud to announce the release of PGPainless-WOT, an implementation of the OpenPGP Web of Trust specification using PGPainless.

The release is available on the Maven Central repository.

The work on this project begun a bit over a year ago as an NLnet project which received funding through the European Commission’s NGI Assure program. Unfortunately, somewhere along the way I lost motivation to work on the project, as I failed to see any concrete users. Other projects seemed more exciting at the time.

NLnet Logo
NGI Assure Logo

Fast forward to end of May when Wiktor reached out and connected me with Heiko, who was interested in the project. We two decided to work together on the project and I quickly rebased my – at this point ancient and outdated – feature branch onto the latest PGPainless release. At the end of June, we started the joint work and roughly a month later today, we can release a first version ๐Ÿ™‚

Big thanks to Heiko for his valuable contributions and the great boost in motivation working together gave me ๐Ÿ™‚
Also big thanks to NLnet for sponsoring this project in such a flexible way.
Lastly, thanks to Wiktor for his talent to connect people ๐Ÿ˜€

The Implementation

We decided to write the implementation in Kotlin. I had attempted to learn Kotlin multiple times before, but had quickly given up each time without an actual project to work on. This time I stayed persistent and now I’m a convinced Kotlin fan ๐Ÿ˜€ Rewriting the existing codebase was a breeze and the line count drastically reduced while the amount of syntactic sugar which was suddenly available blow me away! Now I’m considering to steadily port PGPainless to Kotlin. But back to the Web-of-Trust.

Our implementation is split into 4 modules:

  • pgpainless-wot parses OpenPGP certificates into a generalized form and builds a flow network by verifying third-party signatures. It also provides a plugin for pgpainless-core.
  • wot-dijkstra implements a query algorithm that finds paths on a network. This module has no OpenPGP dependencies whatsoever, so it could also be used for other protocols with similar requirements.
  • pgpainless-wot-cli provides a CLI frontend for pgpainless-wot
  • wot-test-suite contains test vectors from Sequoia PGP’s WoT implementation

The code in pgpainless-wot can either be used standalone via a neat little API, or it can be used as a plugin for pgpainless-core to enhance the encryption / verification API:

/* Standalone */
Network network = PGPNetworkParser(store).buildNetwork();
WebOfTrustAPI api = new WebOfTrustAPI(network, trustRoots, false, false, 120, refTime);

// Authenticate a binding
    api.authenticate(fingerprint, userId, isEmail).isAcceptable());

// Identify users of a certificate via the fingerprint
    "Alice <alice@example.org>",

// Lookup certificates of users via userId
LookupAPI.Result result = api.lookup(
    "Alice <alice@example.org>", isEmail);

// Identify all authentic bindings (all trustworthy certificates)
ListAPI.Result result = api.list();

/* Or enhancing the PGPainless API */
CertificateAuthorityImpl wot = CertificateAuthorityImpl
    .webOfTrustFromCertificateStore(store, trustRoots, refTime)

// Encryption
EncryptionStream encStream = PGPainless.encryptAndOrSign()
    // Add only recipients we can authenticate
    .addAuthenticatableRecipients(userId, isEmail, wot)

// Verification
DecryptionStream decStream = [...]
[...]  // finish decryption
MessageMetadata metadata = decStream.getMetadata();
assertTrue(metadata.isAuthenticatablySignedBy(userId, isEmail, wot));

The CLI application pgpainless-wot-cli mimics Sequoia PGP’s neat sq-wot tool, both in argument signature and output format. This has been done in an attempt to enable testing of both applications using the same test suite.

pgpainless-wot-cli can read GnuPGs keyring, can fetch certificates from the Shared OpenPGP Certificate Directory (using pgpainless-cert-d of course :P) and ingest arbitrary .pgp keyring files.

$ ./pgpainless-wot-cli help     
Usage: pgpainless-wot [--certification-network] [--gossip] [--gpg-ownertrust]
                      [--time=TIMESTAMP] [--known-notation=NOTATION NAME]...
                      [-r=FINGERPRINT]... [-a=AMOUNT | --partial | --full |
                      --double] (-k=FILE [-k=FILE]... | --cert-d[=PATH] |
                      --gpg) [COMMAND]
  -a, --trust-amount=AMOUNT
                         The required amount of trust.
      --cert-d[=PATH]    Specify a pgp-cert-d base directory. Leave empty to
                           fallback to the default pgp-cert-d location.
                         Treat the web of trust as a certification network
                           instead of an authentication network.
      --double           Equivalent to -a 240.
      --full             Equivalent to -a 120.
      --gossip           Find arbitrary paths by treating all certificates as
                           trust-roots with zero trust.
      --gpg              Read trust roots and keyring from GnuPG.
      --gpg-ownertrust   Read trust-roots from GnuPGs ownertrust.
  -k, --keyring=FILE     Specify a keyring file.
      --known-notation=NOTATION NAME
                         Add a notation to the list of known notations.
      --partial          Equivalent to -a 40.
  -r, --trust-root=FINGERPRINT
                         One or more certificates to use as trust-roots.
      --time=TIMESTAMP   Reference time.
  authenticate  Authenticate the binding between a certificate and user ID.
  identify      Identify a certificate via its fingerprint by determining the
                  authenticity of its user IDs.
  list          Find all bindings that can be authenticated for all
  lookup        Lookup authentic certificates by finding bindings for a given
                  user ID.
  path          Verify and lint a path.
  help          Displays help information about the specified command

The README file of the pgpainless-wot-cli module contains instructions on how to build the executable.

Future Improvements

The current implementation still has potential for improvements and optimizations. For one, the Network object containing the result of many costly signature verifications is currently ephemeral and cannot be cached. In the future it would be desirable to change the network parsing code to be agnostic of reference time, including any verifiable signatures as edges of the network, even if those signatures are not yet – or no longer valid. This would allow us to implement some caching logic that could write out the network to disk, ready for future web of trust operations.

That way, the network would only need to be re-created whenever the underlying certificate store is updated with new or changed certificates (which could also be optimized to only update relevant parts of the network). The query algorithm would need to filter out any inactive edges with each query, depending on the queries reference time. This would be far more efficient than re-creating the network with each application start.

But why the Web of Trust?

End-to-end encryption suffers from one major challenge: When sending a message to another user, how do you know that you are using the correct key? How can you prevent an active attacker from handing you fake recipient keys, impersonating your peer? Such a scenario is called Machine-in-the-Middle (MitM) attack.

On the web, the most common countermeasure against MitM attacks are certificate authorities, which certify the TLS certificates of website owners, requiring them to first prove their identity to some extent. Let’s Encrypt for example first verifies, that you control the machine that serves a domain before issuing a certificate for it. Browsers trust Let’s Encrypt, so users can now authenticate your website by validating the certificate chain from the Let’s Encrypt CA key down to your website’s certificate.

The Web-of-Trust follows a similar model, with the difference, that you are your own trust-root and decide, which CA’s you want to trust (which in some sense makes you your own “meta-CA”). The Web-of-Trust is therefore far more decentralized than the fixed set of TLS trust-roots baked into web browsers. You can use your own key to issue trust signatures on keys of contacts that you know are authentic. For example, you might have met Bob in person and he handed you a business card containing his key’s fingerprint. Or you helped a friend set up their encrypted communications and in the process you two exchanged fingerprints manually.

In all these cases, in order to initiate a secure communication channel, you needed to exchange the fingerprint via an out-of-band channel. The real magic only happens, once you take into consideration that your close contacts could also do the same for their close contacts, which makes them CAs too. This way, you could authenticate Charlie via your friend Bob, of whom you know that he is trustworthy, because – come on, it’s Bob! Everybody loves Bob!

An example OpenPGP Web-of-Trust Network diagram.
An example for an OpenPGP Web-of-Trust. Simply by delegating trust to the Neutron Mail CA and to Vincenzo, Aaron is able to authenticate a number of certificates.

The Web-of-Trust becomes really useful if you work with people that share the same goal. Your workplace might be one of them, your favorite Linux distribution’s maintainer team, or that non-Profit organization/activist collective that is fighting for a better tomorrow. At work for example, your employer’s IT department might use a local CA (such as an instance of the OpenPGP CA) to help employees to communicate safely. You trust your workplace’s CA, which then introduces you safely to your colleagues’ authentic key material. It even works across business’ boundaries, e.g. if your workplace has a cooperation with ACME and you need to establish a safe communication channel to an ACME employee. In this scenario, your company’s CA might delegate to the ACME CA, allowing you to authenticate ACME employees.

As you can see, the Web-of-Trust becomes more useful the more people are using it. Providing accessible tooling is therefore essential to improve the overall ecosystem. In the future, I hope that OpenPGP clients such as MUAs (e.g. Thunderbird) will embrace the Web-of-Trust.

Leave a Reply

Your email address will not be published. Required fields are marked *