From 20145d3a113931001fd76868e6d99bff97488470 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Thu, 2 Jun 2016 19:34:24 +0200 Subject: [PATCH] Implement hkp index/mr --- README.md | 18 +++++++++--------- src/route/hkp.js | 33 +++++++++++++++++++++++++++++---- src/service/public-key.js | 14 +++++++++++--- test/integration/app-test.js | 22 ++++++++++++---------- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 2625541..0aebaac 100644 --- a/README.md +++ b/README.md @@ -55,26 +55,26 @@ keytext=-----BEGIN PGP PUBLIC KEY BLOCK----- ... -----END PGP PUBLIC KEY BLOCK-- ### Lookup a key -Currently only the `get` operation is implemented. Other operations will return a `501` (not implemented) http error code. +Currently only the `get` and `index` (with options=mr) operations are implemented. Other operations will return a `501` (not implemented) http error code. -#### By key id +#### By key id (get) ``` GET /pks/lookup?op=get&search=0x0123456789ABCDEF ``` -#### By email address - -``` -GET /pks/lookup?op=get&search=user@example.com -``` - -#### Machine readable +#### By email address (get, mr) ``` GET /pks/lookup?op=get&options=mr&search=user@example.com ``` +#### By key id (index, mr) + +``` +GET /pks/lookup?op=index&options=mr&search=0x0123456789ABCDEF +``` + ## REST api diff --git a/src/route/hkp.js b/src/route/hkp.js index 2c30f8b..9f11118 100644 --- a/src/route/hkp.js +++ b/src/route/hkp.js @@ -57,7 +57,7 @@ class HKP { let params = this.parseQueryString(ctx); let key = yield this._publicKey.get(params); this.setGetHeaders(ctx, params); - ctx.body = key.publicKeyArmored; + this.setGetBody(ctx, params, key); } /** @@ -77,7 +77,7 @@ class HKP { params.email = ctx.query.search; } - if (params.op !== 'get') { + if ((params.op !== 'get' && params.op !== 'index') || (params.op === 'index' && !params.mr)) { ctx.throw(501, 'Not implemented!'); } else if (!params.keyid && !params.email) { ctx.throw(400, 'Invalid request!'); @@ -105,12 +105,37 @@ class HKP { * @param {Object} params The parsed query string parameters */ setGetHeaders(ctx, params) { - if (params.mr) { - ctx.set('Content-Type', 'application/pgp-keys; charset=UTF-8'); + if (params.op === 'get' && params.mr) { + ctx.set('Content-Type', 'application/pgp-keys; charset=utf-8'); ctx.set('Content-Disposition', 'attachment; filename=openpgpkey.asc'); } } + /** + * Format the body accordingly. + * See https://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5 + * @param {Object} ctx The koa request/response context + * @param {Object} params The parsed query string parameters + * @param {Object} key The public key document + */ + setGetBody(ctx, params, key) { + if (params.op === 'get') { + ctx.body = key.publicKeyArmored; + } else if (params.op === 'index' && params.mr) { + const VERSION = 1; + const COUNT = 1; // number of keys + let algo = (key.algorithm.indexOf('rsa') !== -1) ? 1 : ''; + let created = key.created ? (key.created.getTime() / 1000) : ''; + let uid = key.userIds.map(u => u.name + ' <' + u.email + '>').join(', '); + + ctx.body = + 'info:' + VERSION + ':' + COUNT + '\n' + + 'pub:' + key.keyid + ':' + algo + ':' + key.keylen + ':' + created + '::\n' + + 'uid:' + encodeURIComponent(uid) + ':' + created + '::\n' + + key.publicKeyArmored; + } + } + } module.exports = HKP; \ No newline at end of file diff --git a/src/service/public-key.js b/src/service/public-key.js index d28c02a..4bb2721 100644 --- a/src/service/public-key.js +++ b/src/service/public-key.js @@ -92,9 +92,14 @@ class PublicKey { keys.forEach(key => userIds = userIds.concat(key.getUserIds())); userIds = util.deDup(userIds); // get key id + let primKey = keys[0].primaryKey; return { - keyid: keys[0].primaryKey.getKeyId().toHex().toUpperCase(), - userIds: util.parseUserIds(userIds) + keyid: primKey.getKeyId().toHex().toUpperCase(), + userIds: util.parseUserIds(userIds), + fingerprint: primKey.fingerprint.toUpperCase(), + created: primKey.created, + algorithm: primKey.algorithm, + keylen: primKey.getBitSize() }; } @@ -155,7 +160,10 @@ class PublicKey { if (!verified) { util.throw(404, 'Key not found'); } - return yield this._mongo.get({ _id:verified.keyid }, DB_TYPE); + let key = yield this._mongo.get({ _id:verified.keyid }, DB_TYPE); + let params = this._parseKey(key.publicKeyArmored); + params.publicKeyArmored = key.publicKeyArmored; + return params; } /** diff --git a/test/integration/app-test.js b/test/integration/app-test.js index a0e7741..1f3b490 100644 --- a/test/integration/app-test.js +++ b/test/integration/app-test.js @@ -168,20 +168,14 @@ describe('Koa App (HTTP Server) Integration Tests', function() { it('should return 200 and get key by id', done => { request(app.listen()) .get('/api/v1/key?keyid=' + emailParams.keyid) - .expect(200, { - _id: emailParams.keyid, - publicKeyArmored - }) + .expect(200) .end(done); }); it('should return 200 and get key email address', done => { request(app.listen()) .get('/api/v1/key?email=' + primaryEmail) - .expect(200, { - _id: emailParams.keyid, - publicKeyArmored - }) + .expect(200) .end(done); }); @@ -394,15 +388,23 @@ describe('Koa App (HTTP Server) Integration Tests', function() { .end(done); }); - it('should return 200 for "mr" (machine readable) option', done => { + it('should return 200 for "mr" option', done => { request(app.listen()) .get('/pks/lookup?op=get&options=mr&search=' + primaryEmail) - .expect('Content-Type', 'application/pgp-keys; charset=UTF-8') + .expect('Content-Type', 'application/pgp-keys; charset=utf-8') .expect('Content-Disposition', 'attachment; filename=openpgpkey.asc') .expect(200, publicKeyArmored) .end(done); }); + it('should return 200 for "index" with "mr" option', done => { + request(app.listen()) + .get('/pks/lookup?op=index&options=mr&search=0x' + emailParams.keyid) + .expect('Content-Type', 'text/plain; charset=utf-8') + .expect(200) + .end(done); + }); + it('should return 400 for invalid email', done => { request(app.listen()) .get('/pks/lookup?op=get&search=a@bco')