Skip to content

Web key directory #121

Open

Description

@LecrisUT

Can this expose the public keys according to GPG Web Key Directory?

It would also be helpful to guide on what DNS records need to be set and other such setups in order to get the keyserver to be auto-detected, and which clients/gpg versions support which setup.

Activity

  1. strausmann commented on Feb 20, 2023

    @strausmann

    i just stumbled across this question, too.

  2. pravi commented on May 28, 2024

    @pravi
    Contributor

    There is a python script to generate .well-known/openpgpkey/hu/ from a keyring - https://hg.intevation.de/gnupg/wkd-tools/file/tip/generate-openpgpkey-hu, but we should probably use the existing database and map the wkd requests to hkps search requests.

  3. pravi commented on May 28, 2024

    @pravi
    Contributor

    The format that WKD expects,
    https://intevation.de/.well-known/openpgpkey/hu/it5sewh54rxz33fwmr8u6dy4bbz8itz4 is the direct method URL for "bernhard.reiter@intevation.de".
    and mailvelope keyserver's format is,
    https://keys.puri.sm/pks/lookup?op=get&search=praveen%40debian.org (though this gives a html page and not raw key, so we will need to add another option to get raw keys).

    It might be possible to do this url rewriting in web server as well. decode the request to email address and then rewrite the url to search by email address with an option to return raw data (probably in binary since wkd expects binary data - at least the example url returned binary data).

  4. pravi commented on May 28, 2024

    @pravi
    Contributor

    if we can get the hash generation logic figured out, then it can be simple I think.

    1. generate the hash from email and store in the db when a key is uploaded
    2. add a search option for this hash
    3. do a url rewriting to map between wkd url format and mailvelope search url (in web server)
    4. and add an option to return raw key in addition to current html page

    Update: we can already pass options=mr https://keys.puri.sm/pks/lookup?op=get&search=praveen@puri.sm&options=mr

  5. pravi commented on May 29, 2024

    @pravi
    Contributor

    https://gist.github.com/kafene/0a6e259996862d35845784e6e5dbfc79 this is a useful resource. It shows how to generate the wkd-hash with gpg command itself. We need to find a way to do the same in Javascript.

  6. pravi commented on May 29, 2024

    @pravi
    Contributor
  7. pravi commented on May 29, 2024

    @pravi
    Contributor

    Based on wkd-client code, I was able to generate this hash using this code snippet.

    const email = "bernhard.reiter@intevation.de"
        if (!email) {
          throw new Error('You must provide an email parameter!');
        }
    
        if (typeof email !== 'string' || !email.includes('@')) {
          throw new Error('Invalid e-mail address.');
        }
    
        const [localPart, domain] = email.split('@');
        const localPartEncoded = new TextEncoder().encode(localPart.toLowerCase());
        const localPartHashed = new Uint8Array(await crypto.subtle.digest('SHA-1', localPartEncoded));
        const localPartBase32 = encodeZBase32(localPartHashed);
        console.log(localPartBase32);
    /**
     * Encode input buffer using Z-Base32 encoding.
     * See: https://tools.ietf.org/html/rfc6189#section-5.1.6
     *
     * @param {Uint8Array} data - The binary data to encode
     * @returns {String} Binary data encoded using Z-Base32.
     */
    function encodeZBase32(data) {
      if (data.length === 0) {
        return "";
      }
      const ALPHABET = "ybndrfg8ejkmcpqxot1uwisza345h769";
      const SHIFT = 5;
      const MASK = 31;
      let buffer = data[0];
      let index = 1;
      let bitsLeft = 8;
      let result = '';
      while (bitsLeft > 0 || index < data.length) {
        if (bitsLeft < SHIFT) {
          if (index < data.length) {
            buffer <<= 8;
            buffer |= data[index++] & 0xff;
            bitsLeft += 8;
          } else {
            const pad = SHIFT - bitsLeft;
            buffer <<= pad;
            bitsLeft += pad;
          }
        }
        bitsLeft -= SHIFT;
        result += ALPHABET[MASK & (buffer >> bitsLeft)];
      }
      return result;
    }
    
    
  8. pravi commented on May 30, 2024

    @pravi
    Contributor

    I manged to get the hash generated, but unable to pass the value back from util.js to public-key.js. Need to figure out doing this with proper async/await.

    diff --git a/src/lib/util.js b/src/lib/util.js
    index b871fba..271f0aa 100644
    --- a/src/lib/util.js
    +++ b/src/lib/util.js
    @@ -87,6 +87,53 @@ exports.normalizeEmail = function(email) {
       return email;
     };
     
    +exports.genWKDHash = function(email, wkdhash = '') {
    +  const [localPart, domain] = email.split('@');
    +  crypto.subtle.digest({
    +          name: "SHA-1",
    +      },
    +      new TextEncoder().encode(localPart.toLowerCase())
    +  )
    +  .then(function(hash){
    +     const localPartBase32 = encodeZBase32(new Uint8Array(hash));
    +     wkdhash = localPartBase32 + '@' + domain;
    +  })
    +  .catch(function(err){
    +      console.error(err);
    +  });
    +  console.log(wkdhash); 
    +     return wkdhash;
    +}
    +
    +function encodeZBase32(data) {
    +  if (data.length === 0) {
    +    return "";
    +  }
    +  const ALPHABET = "ybndrfg8ejkmcpqxot1uwisza345h769";
    +  const SHIFT = 5;
    +  const MASK = 31;
    +  let buffer = data[0];
    +  let index = 1;
    +  let bitsLeft = 8;
    +  let result = '';
    +  while (bitsLeft > 0 || index < data.length) {
    +    if (bitsLeft < SHIFT) {
    +      if (index < data.length) {
    +        buffer <<= 8;
    +        buffer |= data[index++] & 0xff;
    +        bitsLeft += 8;
    +      } else {
    +        const pad = SHIFT - bitsLeft;
    +        buffer <<= pad;
    +        bitsLeft += pad;
    +      }
    +    }
    +    bitsLeft -= SHIFT;
    +    result += ALPHABET[MASK & (buffer >> bitsLeft)];
    +  }
    +  return result;
    +}
    +
     /**
      * Generate a cryptographically secure random hex string. If no length is
      * provided a 32 char hex string will be generated by default.
    diff --git a/src/modules/public-key.js b/src/modules/public-key.js
    index bb0b2b1..0fe68f6 100644
    --- a/src/modules/public-key.js
    +++ b/src/modules/public-key.js
    @@ -160,6 +160,10 @@ class PublicKey {
           if (userId.notify && userId.notify === true) {
             // generate nonce for verification
             userId.nonce = util.random();
    +        // generate nonce for verification
    +        console.log(util.genWKDHash(userId.email));
    +        userId.wkdhash = util.genWKDHash(userId.email,userId.wkdhash);
    +console.log("wkdhash" + userId.wkdhash);
             await this._email.send({template: tpl.verifyKey, userId, keyId, origin, publicKeyArmored: userId.publicKeyArmored, i18n});
           }
         }
    
  9. asdofindia commented on May 30, 2024

    @asdofindia

    @pravi

    exports.genWKDHash = function(email, wkdhash = '') {
      const [localPart, domain] = email.split('@');
      crypto.subtle.digest({
              name: "SHA-1",
          },
          new TextEncoder().encode(localPart.toLowerCase())
      )
      .then(function(hash){
         const localPartBase32 = encodeZBase32(new Uint8Array(hash));
         wkdhash = localPartBase32 + '@' + domain;
      })
      .catch(function(err){
          console.error(err);
      });
         return wkdhash;
    }
    

    In the above, crypto.subtle.digest( function starts a new branch of execution (asynchronous) while the main branch continues from that line and jumps to return wkdhash;. At this point in time, wkdhash has not changed, and will be an empty string.

    What needs to be done is to return the promise of crypto.subtle.digest itself, like this:

    exports.genWKDHash = function(email, wkdhash = '') {
      const [localPart, domain] = email.split('@');
      return crypto.subtle.digest({
              name: "SHA-1",
          },
          new TextEncoder().encode(localPart.toLowerCase())
      )
      .then(function(hash){
         const localPartBase32 = encodeZBase32(new Uint8Array(hash));
         wkdhash = localPartBase32 + '@' + domain;
         return wkdhash;    // this is what the promise resolves to if there's no error
      })
      .catch(function(err){
          console.error(err);
      });
    }
    

    And then when you call it, you've to wait for the promise to resolve, like this:

    userId.wkdhash = await util.genWKDHash(userId.email,userId.wkdhash);
    

    This hashing operation seems to be destructive in that we cannot get the email address back from the hash. Therefore your latter comment about having to store the hash in the database seems right. Meaning for existing keys, there'll have to be a separate function to calculate the hashes and save to DB. And for new keys being uploaded, the hash can be calculated along with fingerprint, etc.

    You could add a wkd endpoint like the hkp endpoint:

    'use strict';
    
    const Boom = require('@hapi/boom');
    const util = require('../lib/util');
    const openpgp = require('openpgp');
    
    /**
     * An implementation of Web Key Directory
     * See https://wiki.gnupg.org/WKD
     */
    class WKD {
      /**
       * Create an instance of the WKD server
       * @param  {Object} publicKey - an instance of the public key service
       */
      constructor(publicKey) {
        this._publicKey = publicKey;
      }
    
      /**
       * Public key lookup via http GET
       * @param {Object} request - hapi request object
       * @param {Object} h - hapi response toolkit
       */
      async lookup(request, h) {
        const hash = request.params.hash;
        // implement lookup
          return h.response(body).type('text/plain');
        }
      }
    }
    
    exports.plugin = {
      name: 'WKD',
      async register(server, options) {
        const wkd = new WKD(server.app.publicKey);
    
        const routeOptions = {
          bind: wkd,
          cors: options.server.cors === 'true',
          security: options.server.security === 'true',
          ext: {
            onPreResponse: {
              method({response}, h) {
                if (!response.isBoom) {
                  return h.continue;
                }
                return h.response(response.message).code(response.output.statusCode).type('text/plain');
              }
            }
          }
        };
    
        server.route({
          method: 'GET',
          path: '/.well-known/openpgpkey/hu/{hash}',
          handler: wkd.lookup,
          options: routeOptions
        });
      }
    };
    

    But of course, implementing the lookup requires the hash to be saved.

  10. pravi commented on May 31, 2024

    @pravi
    Contributor

    @asdofindia thanks, let me try this.

    Thanks also to Ananthu CV who also helped making this a promise.

    diff --git a/src/lib/util.js b/src/lib/util.js
    index b871fba..60d28eb 100644
    --- a/src/lib/util.js
    +++ b/src/lib/util.js
    @@ -87,6 +87,47 @@ exports.normalizeEmail = function(email) {
       return email;
     };
     
    +exports.genWKDHash = function(email, wkdhash = '') {
    + return new Promise((resolve, reject) => {
    +    const [localPart, domain] = email.split('@');
    +    const hash = crypto.createHash('sha1')
    +      .update(localPart.toLowerCase())
    +      .digest();
    +    const localPartBase32 = encodeZBase32(hash);
    +    const wkdhash = localPartBase32 + '@' + domain;
    +    resolve(wkdhash);
    +  });
    +}
    +
    +function encodeZBase32(data) {
    +  if (data.length === 0) {
    +    return "";
    +  }
    +  const ALPHABET = "ybndrfg8ejkmcpqxot1uwisza345h769";
    +  const SHIFT = 5;
    +  const MASK = 31;
    +  let buffer = data[0];
    +  let index = 1;
    +  let bitsLeft = 8;
    +  let result = '';
    +  while (bitsLeft > 0 || index < data.length) {
    +    if (bitsLeft < SHIFT) {
    +      if (index < data.length) {
    +        buffer <<= 8;
    +        buffer |= data[index++] & 0xff;
    +        bitsLeft += 8;
    +      } else {
    +        const pad = SHIFT - bitsLeft;
    +        buffer <<= pad;
    +        bitsLeft += pad;
    +      }
    +    }
    +    bitsLeft -= SHIFT;
    +    result += ALPHABET[MASK & (buffer >> bitsLeft)];
    +  }
    +  return result;
    +}
    +
     /**
      * Generate a cryptographically secure random hex string. If no length is
      * provided a 32 char hex string will be generated by default.
    diff --git a/src/modules/public-key.js b/src/modules/public-key.js
    index bb0b2b1..bd04b5f 100644
    --- a/src/modules/public-key.js
    +++ b/src/modules/public-key.js
    @@ -160,6 +160,8 @@ class PublicKey {
           if (userId.notify && userId.notify === true) {
             // generate nonce for verification
             userId.nonce = util.random();
    +        // generate wkd hash
    +        userId.wkdhash = await util.genWKDHash(userId.email,userId.wkdhash);
             await this._email.send({template: tpl.verifyKey, userId, keyId, origin, publicKeyArmored: userId.publicKeyArmored, i18n});
           }
         }
    
    
  11. pravi commented on May 31, 2024

    @pravi
    Contributor

    @asdofindia I tried adding src/route/wkd.js and visiting the .well-known/openpgpkey/hu/g4rf118d16sbj3k77d5txywpm879pszg which resulted in a 404. I think we should try to get a key not found/another debug message printed to get started before implementing the lookup part.

  12. pravi commented on Jun 1, 2024

    @pravi
    Contributor

    With the following code,

    diff --git a/src/modules/public-key.js b/src/modules/public-key.js
    index bb0b2b1..8fb8381 100644
    --- a/src/modules/public-key.js
    +++ b/src/modules/public-key.js
    @@ -249,7 +252,7 @@ class PublicKey {
        * @param {string} keyId         (optional) The public key id
        * @return {Object}               The verified key document
        */
    -  async getVerified({userIds, fingerprint, keyId}) {
    +  async getVerified({userIds, fingerprint, keyId, wkd}) {
         let queries = [];
         // query by fingerprint
         if (fingerprint) {
    @@ -265,8 +268,18 @@ class PublicKey {
             'userIds.verified': true
           });
         }
    +    // query by wkd hash
    +    if (userIds && wkd) {
    +      queries = queries.concat(userIds.map(uid => ({
    +        'userIds.wkdhash': {
    +            $eq: uid.email},
    +        'userIds.verified': {
    +            $eq: true
    +        }
    +      })));
    +    }
         // query by user id
    -    if (userIds) {
    +    if (userIds && !wkd) {
           queries = queries.concat(userIds.map(uid => ({
             'userIds.email': {
                 $eq: util.normalizeEmail(uid.email)},
    @@ -287,10 +300,10 @@ class PublicKey {
        * @param {Object} i18n          i18n object
        * @return {Object}               The public key document
        */
    -  async get({fingerprint, keyId, email, i18n}) {
    +  async get({fingerprint, keyId, email, wkd, i18n}) {
         // look for verified key
         const userIds = email ? [{email}] : undefined;
    -    const key = await this.getVerified({keyId, fingerprint, userIds});
    +    const key = await this.getVerified({keyId, fingerprint, userIds, wkd});
         if (!key) {
           throw Boom.notFound(i18n.__('key_not_found'));
         }
    diff --git a/src/route/hkp.js b/src/route/hkp.js
    index 7f90136..1604702 100644
    --- a/src/route/hkp.js
    +++ b/src/route/hkp.js
    @@ -46,7 +46,7 @@ class HKP {
         const params = this.parseQueryString(request);
         const key = await this._publicKey.get({...params, i18n: request.i18n});
         if (params.op === 'get') {
    -      if (params.mr) {
    +      if (params.mr || params.wkd) {
             return h.response(key.publicKeyArmored)
             .header('Content-Type', 'application/pgp-keys; charset=utf-8')
             .header('Content-Disposition', 'attachment; filename=openpgp-key.asc');
    @@ -84,7 +84,8 @@ class HKP {
       parseQueryString({query}) {
         const params = {
           op: query.op, // operation ... only 'get' is supported
    -      mr: query.options === 'mr' // machine readable
    +      mr: query.options === 'mr', // machine readable
    +      wkd: query.options === 'wkd' // wkd hash
         };
         if (!['get', 'index', 'vindex'].includes(params.op)) {
           throw Boom.notImplemented('Method not implemented');
    

    I'm able to get the raw ascii armored key when looking for /pks/lookup?op=get&search=g4rf118d16sbj3k77d5txywpm879pszg%40puri.sm&options=wkd

    Now we just need to either add a wkd route or a url rewriting scheme in web server that will redirect to this url from the wkd direct url.

  13. pravi commented on Jun 1, 2024

    @pravi
    Contributor

    /pks/lookup?op=get&search=g4rf118d16sbj3k77d5txywpm879pszg%40puri.sm&options=wkd is what is working correctly now, what is expected is /.well-known/openpgpkey/hu/g4rf118d16sbj3k77d5txywpm879pszg

    So we have two options to do this mapping,

    1. Add a new route (previous try did not work)
    2. Rewrite the url at web server

    Domain name will need to dynamically selected based on the request url since the local part of hash is not unique.

  14. pravi commented on Jun 1, 2024

    @pravi
    Contributor
    openpgpkey.puri.sm {
            route /.well-known/openpgpkey/puri.sm/hu/* {
                    uri strip_prefix /.well-known/openpgpkey/puri.sm/hu/
                    rewrite * /pks/lookup?op=get&search={uri}%40puri.sm&options=wkd
                    reverse_proxy localhost:3000
            }
    }
    
    

    with this in CaddyFile, I can get the key from https://openpgpkey.puri.sm/.well-known/openpgpkey/puri.sm/hu/g4rf118d16sbj3k77d5txywpm879pszg (this is a restricted url for now).

  15. pravi commented on Jun 1, 2024

    @pravi
    Contributor

    Though gpg --locate-keys praveen@puri.sm seems to be just waiting, no error nor timeout for a long time.

    
    $ gpg --verbose --locate-keys praveen@puri.sm
    gpg: enabled compatibility flags:
    gpg: using pgp trust model
    gpg: error retrieving 'praveen@puri.sm' via Local: No public key
    
    
  16. 5 remaining items

  17. pravi commented on Jun 1, 2024

    @pravi
    Contributor

    I could not find a way to dearmor with openpgp.js so I used gpg command to dearmor

    diff --git a/src/route/hkp.js b/src/route/hkp.js
    index 7f90136..836fdda 100644
    --- a/src/route/hkp.js
    +++ b/src/route/hkp.js
    @@ -46,7 +46,23 @@ class HKP {
         const params = this.parseQueryString(request);
         const key = await this._publicKey.get({...params, i18n: request.i18n});
         if (params.op === 'get') {
    -      if (params.mr) {
    +      if (params.wkd) {
    +       const fs = require('fs');
    +       try {
    +               fs.writeFileSync('/tmp/' + key.fingerprint + '.asc', key.publicKeyArmored);
    +               } catch (err) {
    +                 console.error(err);
    +               }
    +       const {execSync} = require('child_process');
    +       let output = execSync('gpg --dearmor ' + '/tmp/' + key.fingerprint + '.asc');
    +       try {
    +               const publicKeyBinary = fs.readFileSync('/tmp/' + key.fingerprint + '.asc.gpg');
    +               return h.response(publicKeyBinary)
    +        .header('Content-Type', 'application/octet-stream');
    +               } catch (err) {
    +                 console.error(err);
    +               }
    +      } else if (params.mr) {
             return h.response(key.publicKeyArmored)
             .header('Content-Type', 'application/pgp-keys; charset=utf-8')
             .header('Content-Disposition', 'attachment; filename=openpgp-key.asc');
    
    

    But even then gpg fails to get the key,

    
    $ gpg --verbose --locate-keys praveen@puri.sm
    gpg: enabled compatibility flags:
    gpg: using pgp trust model
    gpg: error retrieving 'praveen@puri.sm' via Local: No public key
    gpg: error retrieving 'praveen@puri.sm' via WKD: No data
    gpg: error reading key: No data
    
    
  18. wiktor-k commented on Jun 2, 2024

    @wiktor-k

    Hmm... I'd advise to use the Web Key Directory checker but I just sunsetted it last month! 😬

    GnuPG may cache the result and return error even if you fixed everything. I'd suggest creating the WKD URL manually and visiting it with curl -i -sSL.

    I can do more thorough checks but only on Monday.

  19. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    I have removed the firewall to allow access to everyone and this is the curl output.

    $ curl -i -sSL https://openpgpkey.puri.sm/.well-known/openpgpkey/puri.sm/hu/g4rf118d16sbj3k77d5txywpm879pszg
    HTTP/2 200 
    accept-ranges: bytes
    access-control-expose-headers: WWW-Authenticate,Server-Authorization
    alt-svc: h3=":443"; ma=2592000
    cache-control: no-cache
    content-security-policy: default-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; form-action 'self'; base-uri 'self';
    content-type: application/octet-stream
    date: Sun, 02 Jun 2024 11:10:12 GMT
    server: Caddy
    strict-transport-security: max-age=15768000
    vary: origin
    x-content-type-options: nosniff
    x-download-options: noopen
    x-frame-options: DENY
    x-xss-protection: 0
    content-length: 2291
    
    
  20. pravi commented on Jun 2, 2024

    @pravi
    Contributor
    mukta@mahishi:~$ gpg-wks-client --check -v --debug=ipc praveen@puri.sm
    gpg-wks-client: DBG: chan_3 <- # Home: /home/mukta/.gnupg
    gpg-wks-client: DBG: chan_3 <- # Config: /home/mukta/.gnupg/dirmngr.conf
    gpg-wks-client: DBG: chan_3 <- OK Dirmngr 2.4.5 at your service
    gpg-wks-client: DBG: connection to the dirmngr established
    gpg-wks-client: DBG: chan_3 -> WKD_GET -- praveen@puri.sm
    gpg-wks-client: DBG: chan_3 <- S SOURCE https://openpgpkey.puri.sm
    gpg-wks-client: DBG: chan_3 <- S PROGRESS tick ? 0 0
    gpg-wks-client: DBG: chan_3 <- ERR 167772218 No data <Dirmngr>
    gpg-wks-client: DBG: chan_3 -> BYE
    gpg-wks-client: public key for 'praveen@puri.sm' NOT found via WKD
    
    
  21. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    Earlier it was waiting for terminal input before overwriting the .gpg file, I changed it to check for the file before running gpg --dearmor but still no luck with gpg client.

    diff --git a/src/route/hkp.js b/src/route/hkp.js
    index 7f90136..2376f5a 100644
    --- a/src/route/hkp.js
    +++ b/src/route/hkp.js
    @@ -46,7 +46,29 @@ class HKP {
         const params = this.parseQueryString(request);
         const key = await this._publicKey.get({...params, i18n: request.i18n});
         if (params.op === 'get') {
    -      if (params.mr) {
    +      if (params.wkd) {
    +       const fs = require('fs');
    +       const publicKeyArmoredFile = '/tmp/' + key.fingerprint + '.asc';
    +       const publicKeyBinaryFile = '/tmp/' + key.fingerprint + '.asc.gpg';
    +       if (!fs.existsSync(publicKeyArmoredFile)) {
    +               try {
    +                       fs.writeFileSync(publicKeyArmoredFile, key.publicKeyArmored);
    +               } catch (err) {
    +                       console.error(err);
    +               }
    +       }
    +       if (!fs.existsSync(publicKeyBinaryFile)) {
    +               const {execSync} = require('child_process');
    +               let output = execSync('gpg --dearmor ' + publicKeyArmoredFile);
    +       }
    +       try {
    +               const publicKeyBinary = fs.readFileSync(publicKeyBinaryFile);
    +               return h.response(publicKeyBinary)
    +        .header('Content-Type', 'application/octet-stream');
    +               } catch (err) {
    +                 console.error(err);
    +               }
    +      } else if (params.mr) {
             return h.response(key.publicKeyArmored)
             .header('Content-Type', 'application/pgp-keys; charset=utf-8')
             .header('Content-Disposition', 'attachment; filename=openpgp-key.asc');
    @@ -84,7 +106,8 @@ class HKP {
       parseQueryString({query}) {
         const params = {
           op: query.op, // operation ... only 'get' is supported
    -      mr: query.options === 'mr' // machine readable
    +      mr: query.options === 'mr', // machine readable
    +      wkd: query.options === 'wkd' // wkd hash
         };
         if (!['get', 'index', 'vindex'].includes(params.op)) {
           throw Boom.notImplemented('Method not implemented');
    
    
  22. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    Using https://sequoia-pgp.org/tools/wkd/

    This gave,

    Error while checking the key: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

    Update: this is probably broken since the same error is shown for bernhard.reiter@intevation.de which has a working WKD implementation.

  23. wiktor-k commented on Jun 2, 2024

    @wiktor-k

    Well, Sequoia's WKD checker really uses mine and since I shut down mine it's not working 🤷‍♂️

  24. asdofindia commented on Jun 2, 2024

    @asdofindia

    @pravi

    ❯ gpg-wks-client --print-wkd-url praveen@puri.sm 
    https://openpgpkey.puri.sm/.well-known/openpgpkey/puri.sm/hu/g4rf118d16sbj3k77d5txywpm879pszg?l=praveen

    It seems like ?l=praveen is (?) expected in the latest spec: https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/

    Anyhow, gpg seems to be looking there which in this case returns Not found.

  25. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    @asdofindia yay! That was the really missing secret sauce!

    After adding rewrite * {path}? to strip the query parameter before the previous rewrite rule, gpg now finds the keys correctly!.

    mukta@mahishi:~$ gpg-wks-client --check -v --debug=ipc praveen@puri.sm
    gpg-wks-client: DBG: chan_3 <- # Home: /home/mukta/.gnupg
    gpg-wks-client: DBG: chan_3 <- # Config: /home/mukta/.gnupg/dirmngr.conf
    gpg-wks-client: DBG: chan_3 <- OK Dirmngr 2.4.5 at your service
    gpg-wks-client: DBG: connection to the dirmngr established
    gpg-wks-client: DBG: chan_3 -> WKD_GET -- praveen@puri.sm
    gpg-wks-client: DBG: chan_3 <- S SOURCE https://openpgpkey.puri.sm
    gpg-wks-client: DBG: chan_3 <- S PROGRESS tick ? 0 0
    gpg-wks-client: DBG: chan_3 <- [ 44 20 c6 c0 cd 04 66 46 20 1c 01 0c 00 9c 40 d1 ...(982 byte(s) skipped) ]
    gpg-wks-client: DBG: chan_3 <- [ 44 20 69 6c 40 70 75 72 69 2e 73 6d 3e c2 c1 14 ...(982 byte(s) skipped) ]
    gpg-wks-client: DBG: chan_3 <- [ 44 20 b5 6f 40 b1 c1 4c ca b3 b6 43 36 4b 7f a9 ...(369 byte(s) skipped) ]
    gpg-wks-client: DBG: chan_3 <- OK
    gpg-wks-client: DBG: chan_3 -> BYE
    gpg-wks-client: public key for 'praveen@puri.sm' found via WKD
    gpg-wks-client: fingerprint: 07EADF93151C322F27C1AD4A2A6164ED0256C63A
    gpg-wks-client:     user-id: Praveen Arimbrathodiyil <praveen.arimbrathodiyil@puri.sm>
    gpg-wks-client:     created: വ്യാഴം 16 മേയ് 2024 08:34:03 വൈകു IST
    gpg-wks-client:   addr-spec: praveen.arimbrathodiyil@puri.sm
    gpg-wks-client:     user-id: Praveen Arimbrathodiyil <praveen@puri.sm>
    gpg-wks-client:     created: വ്യാഴം 16 മേയ് 2024 08:32:52 വൈകു IST
    gpg-wks-client:   addr-spec: praveen@puri.sm
    
    
  26. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    All the WKD documentation/wiki pages and tutorials will need to be updated.

  27. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    #146 is ready for review (I don't have a lot of experience with coding, so I'm sure there can be many improvements).

  28. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    I'm trying to see if we can do this entirely in caddy/web server without generating and storing the wkd hash since we now know the localPart is passed as a query parameter which can be used to search by email address.

    I'm trying this,

    route /.well-known/openpgpkey/puri.sm/hu/* {
                    rewrite * /pks/lookup?op=get&search={query}%40puri.sm&options=mr
            }
    

    But {query} is "l=praveen" so we need a way to strip out "l=" also from that.

    I tried uri replace ?l= ?search= but it is still passing l=praveen.

  29. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    With just a small change, I can get the keys without storing the wkd hash.

    diff --git a/src/route/hkp.js b/src/route/hkp.js
    index 7f90136..79cfda6 100644
    --- a/src/route/hkp.js
    +++ b/src/route/hkp.js
    @@ -89,7 +89,14 @@ class HKP {
         if (!['get', 'index', 'vindex'].includes(params.op)) {
           throw Boom.notImplemented('Method not implemented');
         }
    -    this.parseSearch(query.search, params);
    +    let searchString = query.search;
    +/**
    + * Search string for Web Key Directory lookups is l
    + */
    +    if (query.l) {
    +      searchString = query.l;
    +    }
    +    this.parseSearch(searchString, params);
         if (!params.keyId && !params.fingerprint && !params.email) {
           throw Boom.badRequest('Invalid search parameter');
         }
    
    
  30. pravi commented on Jun 2, 2024

    @pravi
    Contributor

    #147 submitted as a simpler method for wkd support.

  31. strickes commented on Feb 9, 2025

    @strickes

    @pravi thank you very much for your guide. I'm trying to make it work on Nginx, but the Mailvelope Browser Plugin still can't find my PGP Key. But the gpg-wks-client does:

    gpg-wks-client: public key for 'test@example.com' found via WKD
    gpg-wks-client: fingerprint: XXX
    gpg-wks-client:     user-id: XXX <test@example.com>
    gpg-wks-client:     created: Sat 08 Feb 2025 03:47:35 PM UTC
    gpg-wks-client:   addr-spec: test@example.com

    this is the nginx config for this:

    # Serve the OpenPGP policy file (if needed)
    location = /.well-known/openpgpkey/example.com/policy {
        default_type text/plain;
        return 200 "protocol-version 5";
    }
    
    # Rewrite and proxy requests for the key lookup endpoint
    location ^~ /.well-known/openpgpkey/example.com/hu/ {
        # Optionally, check if the 'l' query parameter is provided
        if ($arg_l = "") {
            return 400 "Missing 'l' query parameter";
        }
    
        # Rewrite the URL so that the key lookup is forwarded as:
        #   /pks/lookup?op=get&search=<l>@example.com&options=mr
        rewrite ^/\.well-known/openpgpkey/example\.com/hu/.*$ /pks/lookup?op=get&search=$arg_l%40example.com&options=mr break;
    
        # Reverse proxy to your backend keyserver
        proxy_pass http://10.16.66.10:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # (Add any other necessary proxy headers or settings)
    }

    the Mailvelope Browser Plugin states this in the browser console

    WKD: Did not find key (Errors are expected): Error: WKD: Failed to parse result for 'test@example.com': Error during parsing. This message / key probably does not conform to a valid OpenPGP format.
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions