diff --git a/package.json b/package.json index 11c8fa4..8e22e18 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", + "cache": "^3.0.0", "dialog-polyfill": "^0.5.6", "doipjs": "^0.16.3", "dotenv": "^8.2.0", diff --git a/server/keys.js b/server/keys.js index 9f09db5..281704c 100644 --- a/server/keys.js +++ b/server/keys.js @@ -31,6 +31,10 @@ import got from 'got' import * as doipjs from 'doipjs' import { readKey, readCleartextMessage, verify } from 'openpgp' import { computeWKDLocalPart } from './utils.js' +import { createHash } from 'crypto' +import cache from 'cache' + +let c = process.env.ENABLE_EXPERIMENTAL_CACHE ? new cache(60 * 1000) : null const fetchWKD = (id) => { return new Promise(async (resolve, reject) => { @@ -51,33 +55,45 @@ const fetchWKD = (id) => { const urlAdvanced = `https://openpgpkey.${domain}/.well-known/openpgpkey/${domain}/hu/${localEncoded}` const urlDirect = `https://${domain}/.well-known/openpgpkey/hu/${localEncoded}` let plaintext + + const hash = createHash('md5').update(id).digest('hex') - try { - plaintext = await got(urlAdvanced).then((response) => { - if (response.statusCode === 200) { - output.fetchURL = urlAdvanced - return new Uint8Array(response.rawBody) - } else { - return null - } - }) - } catch (e) { + if (c && c.get(hash)) { + plaintext = c.get(hash) + } + + if (!plaintext) { try { - plaintext = await got(urlDirect).then((response) => { + plaintext = await got(urlAdvanced).then((response) => { if (response.statusCode === 200) { - output.fetchURL = urlDirect + output.fetchURL = urlAdvanced return new Uint8Array(response.rawBody) } else { return null } }) - } catch (error) { + } catch (e) { + try { + plaintext = await got(urlDirect).then((response) => { + if (response.statusCode === 200) { + output.fetchURL = urlDirect + return new Uint8Array(response.rawBody) + } else { + return null + } + }) + } catch (error) { + reject(new Error(`No public keys could be fetched using WKD`)) + } + } + + if (!plaintext) { reject(new Error(`No public keys could be fetched using WKD`)) } - } - if (!plaintext) { - reject(new Error(`No public keys could be fetched using WKD`)) + if (c) { + c.put(hash, plaintext) + } } try { @@ -112,17 +128,27 @@ const fetchHKP = (id, keyserverDomain) => { query = `0x${id}` } - try { - output.publicKey = await doipjs.keys.fetchHKP(id, keyserverDomain) - output.fetchURL = `https://${keyserverDomain}/pks/lookup?op=get&options=mr&search=${query}` - } catch(error) { - reject(new Error(`No public keys could be fetched using HKP`)) + const hash = createHash('md5').update(`${id}__${keyserverDomain}`).digest('hex') + + if (c && c.get(hash)) { + output = c.get(hash) + } else { + try { + output.publicKey = await doipjs.keys.fetchHKP(id, keyserverDomain) + output.fetchURL = `https://${keyserverDomain}/pks/lookup?op=get&options=mr&search=${query}` + } catch(error) { + reject(new Error(`No public keys could be fetched using HKP`)) + } } if (!output.publicKey) { reject(new Error(`No public keys could be fetched using HKP`)) } + if (c) { + c.put(hash, output) + } + resolve(output) }) } diff --git a/template.env b/template.env index 4b07118..e2ab72b 100644 --- a/template.env +++ b/template.env @@ -33,3 +33,7 @@ #KX_HIGHLIGHTS_3_NAME= #KX_HIGHLIGHTS_3_DESCRIPTION= #KX_HIGHLIGHTS_3_FINGERPRINT= + +# Enable caching of keys (experimental) +# Opt-in; to disable, omit the environment variable +#ENABLE_EXPERIMENTAL_CACHE= diff --git a/yarn.lock b/yarn.lock index 55cad1c..803cec8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1272,6 +1272,13 @@ bytesish@^0.4.1: resolved "https://registry.npmjs.org/bytesish/-/bytesish-0.4.4.tgz" integrity sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ== +cache@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cache/-/cache-3.0.0.tgz#1c5857e874f7064be641114a605c7e2ae8a80880" + integrity sha512-sNoM5jithfalxIceo/uFFm5bOlGjux2y8jEvjNb0F/cACWQaMmWuEPTLl6GzLHdFcNsbWBBdqkBd9NyefZ5UQQ== + dependencies: + ds "^1.4.2" + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" @@ -1816,6 +1823,11 @@ dotenv@^8.2.0: resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== +ds@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/ds/-/ds-1.4.2.tgz#0857aa213790a4fb3abb365b9cec0e9ba8569393" + integrity sha512-d5nMCjfod+srvE/1Bnt/u+L++6N8KJx3ZAi95AGp0g6RtfuGDNlGciWL/iiwKHsFVBVnA3/HEFUq5SW1NgTQ3Q== + duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"