Skip to content
Snippets Groups Projects
Select Git revision
  • update_versions
  • master default
  • remove_email
  • trash_email
  • trash_email_2
5 results

bitmask_help

  • Clone with SSH
  • Clone with HTTPS
  • Name Last commit Last update
    monkeysphere
    .gitignore
    README.md
    setup.cfg

    Monkeysphere in Python

    This is a scratch area to do some tests with the PGPy library in order to address certain limitations in Monkeysphere. The first of those is the lack of ECC support which may be resolved with a rewrite of the keytransfer component in Python.

    This shouldn't be taken as an attempt to take over or rewrite Monkeysphere, but simply as a space to experiment with new technologies and share them with the world.

    Core concepts

    The core concept of Monkeysphere is to leverage the OpenPGP web of trust to authenticate user and server public keys, both for SSH servers but eventually other services like HTTPS.

    The monkeysphere(7) manpage describes those fundamental concepts and outlines key acceptability criteria:

    • capability: we check the authentication usage flag is set

    • validity: we check that the key is not expired and not revoked, and PGPy takes care of the key being well-formed. we do not enforce other requirements like key length or algorithms. There are also limitations in the key types supported by PGPy, see "Limitations" below.

    • certification: we do NOT check that keys are signed by a trusted identity certifier, see "Limitations"

    Monkeysphere architecture

    Monkeysphere is made out of those 3 core programs which cover all the functionality offered to users by the project. Entries marked with a checkmark (✓) are implemented here.

    • monkeysphere(1) - "client user interface"

      • takes care of updating the monkeysphere-managed known_hosts and authorized_keys file for a user

      • generates an authentication subkey

      • act as a "proxy server" for SSH to authenticate hosts

      • transfer subkeys to ssh-agent

      • ✓ generate SSH public keys from OpenPGP authentication keys, implemented by the openpgp2ssh programs which is a symlink to the more elaborate keytransfer program. this is partially implemented in Python with PGPy, see below.

    • monkeysphere-host(8) - host key administration tool

      • import a PEM key as a new OpenPGP key attached to a UID based on the host URI chosen by the user

      • manage expiration date, UIDs, revocations and revokers

      • publishes keys on keyservers

    • monkeysphere-authentication(8) - authentication admin tool

      • similar to the client user interface (monkeysphere(1)) but can operate on all users at once

      • refresh keys from the keyserver

      • manage certifier keys

    OpenPGP to SSH

    The first part of this effort is the openpgp2ssh script, which currently only takes a public OpenPGP key and transforms it in a series of authorized_keys lines.

    It does nothing more: it doesn't check key trust or validity (beyond expiration date and revocation) and blindly transforms any key given on the commandline or stdin. It also doesn't process private keys, nor does it convert OpenSSH keys back into OpenPGP keys. It also does not output PEM-formatted keys, although that can probably be implemented easily. See below for more details on those limitations.

    Limitations

    PGPy cannot (currently) talk to keyservers to fetch key updates, but this should be fairly easy to implement. A core issue there is to avoid trusting the key material sent by the keyservers, but certification checks implemented below should take care of that. See also keyserver search and get support and update status of network interactions protocols.

    PGPy doesn't have an easy way to check if a key is revoked (issue #225). I made a crude patch to enable revocation checks with a is_revoked() function, but it doesn't do its job properly, because it actually needs to have access to the public key material of the revoker to function properly. Again, if keyserver fetch is implemented this becomes possible.

    ECC keys

    It also requires two patches (#221 and #222), to be shipped with 0.4.4, to properly read ECC keys. Furthermore, only the development version of OpenSSL (from June 2017 at least) supports those curves, so PGPy (and the underlying Cryptography library both need to be linked against such a version for ECC curves to work. As such, there's no release of PGPy that correctly supports ECC at the time of writing.

    Also note there are concerns with the actual implementation, see for example this PR which patches the implementation to correctly pad some fields as required by the RFC...

    PEM and SSH to OpenPGP

    The other key part of Monkeysphere is to do the reverse conversion: take a SSH (or PEM certificate) key and turn it into an OpenPGP keypair that can then be integrated in the Web of Trust. There seems to be some functionality missing in PGPy for this to happen, unfortunately.

    While the Cryptography library can probably read SSH and PEM key material without too much trouble, the PGPy lacks a way to synthesize OpenPGP keys from scratch (see issue #220). There may be a way to do it, but it is not obvious from the API right now.

    Certification checks

    We do not check certificates right now: any keyring passed to openpgp2ssh is assumed to be fully trusted and certification checks are currently considered "out of scope". The way Monkeysphere does those checks is described in the monkeysphere(7) manpage, but essentially boils down to those certification methods:

    • ultimate certification: a user identifier (UID, e.g. the user's email address or the host URI) is signed by a single, ultimately trusted key. authentication keys bound to this UID is then considered certified. this could be easily implemented by checking UID signatures in PGPy.

    • marginal certification: a UID needs to be signed by multiple marginally trusted keys before it is considered certified. it is unclear how Monkeysphere does this, but it may be replicated by counting the number of valid certification from trusted keys.

    • scoped certification: an overlap with the previous two. certifying keys are restricted to sign identities only within a given domain name.

    In practice, Monkeysphere leverages GPG's validity field as provided by its --with-colons output, in the process_user_id() function stored in the src/share/common. That function in turns calls the gpg2ssh function which is a simple wrapper around openpgp2ssh, called on the output of gpg --export $fingerprint, essentially.

    All this is bound by a ltsign command which makes a non-exportable "trusted signature" using an automatically generated public/private keypair named the "Monkeysphere authentication trust core UID (random string +...)". Scoped certifications are implemented with the "regular expressions" described in RFC4880 section 5.2.3.13, but it seems that never quite worked, see bug T2923. Key validity is deduced form there by GnuPG.

    With PGPy, it's possible to inspect UIDs and look at their signatures. For example:

    In [40]: u = pgpy.PGPKey.from_file('anarcat.gpg')[0].userids[0]
    
    In [41]: u
    Out[41]: <PGPUID [UserID] at 0x7FF370CF5400>
    
    In [42]: u.__sig__
    Out[42]: [<PGPSignature [Positive_Cert] object at 0x7ff370cf55c0>]
    
    In [43]: u.signers
    Out[43]: {'792152527B75921E'}

    One concern here is that only the 8-byte key ID is shown here. This is part of the specification (RFC4880 section 5.2.3.5) but it could be a security issue. The proper way of doing this verification seems to be by loading the certifier's OpenPGP key material and using that to verify the key. For example, to check the signatures I have made on dkg's key:

    In [66]: k, _ = pgpy.PGPKey.from_file('dkg.gpg')
    
    In [67]: a, _ = pgpy.PGPKey.from_file('anarcat.gpg')
    
    In [81]: for uid in k.userids:
        ...:     if uid.name == 'Daniel Kahn Gillmor' and uid.email == 'dkg@fifthhorseman.net':
        ...:         print(list(a.verify(uid).good_signatures))
        ...:     else:
        ...:         print('uid no match: ', uid, uid.name, uid.email)
        ...:         
    [sigsubj(verified=True, by='792152527B75921E', signature=<PGPSignature [Generic_Cert] object at 0x7ff370bad470>, subject=<PGPUID [UserID][2016-12-21 17:44:57] at 0x7FF370D444E0>)]
    uid no match:  <PGPUID [UserID][2016-12-21 17:45:07] at 0x7FF370A7AEB8> Daniel Kahn Gillmor dkg@aclu.org
    uid no match:  <PGPUID [UserID][2016-12-21 17:45:05] at 0x7FF370B65400> Daniel Kahn Gillmor dkg@debian.org
    uid no match:  <PGPUID [UserID][2016-12-21 17:45:00] at 0x7FF370D44550> Daniel Kahn Gillmor dkg@openflows.com
    uid no match:  <PGPUID [UserID][2009-06-02 17:45:53] at 0x7FF370B3B048> Daniel Kahn Gillmor dkg-debian.org@fifthhorseman.net
    uid no match:  <PGPUID [UserID][2009-06-02 17:44:32] at 0x7FF370BFCAC8> Daniel Kahn Gillmor dkg@astro.columbia.edu

    So implementing this filtering in the code should be simple enough, provided that we know what restrictions we want to impose. An added benefit here is that could pass a complete OpenPGP key material to the program instead of an unreliable key ID or a fingerprint, and directly verify the signature cryptographically instead of having to do keyring management.

    Obviously, this approach would be different than the current Monkeysphere approach: we wouldn't have an automatically-generated "trust anchor" and would only rely on the integrity of the public key material provided to establish trust, which we would compute using an algorithm like the above, directly with PGPy and without resorting to GPG's algorithms. This would make domain-scoped certifications more difficult, however, as there would need to be out-of-band storage for that information somehow, if it is not stored in a signature.

    Credits

    Anarcat wrote this, but Michael Greene wrote the awesome PGPy library.