doipjs/src/keys.js

265 lines
7.2 KiB
JavaScript
Raw Normal View History

2020-11-16 18:13:56 -07:00
/*
2021-01-13 05:20:33 -07:00
Copyright 2021 Yarmo Mackenbach
2020-11-16 18:13:56 -07:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const bent = require('bent')
const req = bent('GET')
const validUrl = require('valid-url')
2020-12-09 17:56:34 -07:00
const openpgp = require('openpgp')
2021-04-19 03:06:29 -06:00
const Claim = require('./claim')
2020-11-16 18:13:56 -07:00
2021-04-22 07:14:21 -06:00
/**
* Functions related to the fetching and handling of keys
* @module keys
*/
/**
* Fetch a public key using keyservers
* @function
* @param {string} identifier - Fingerprint or email address
* @param {string} [keyserverDomain=keys.openpgp.org] - Domain of the keyserver
* @returns {openpgp.key.Key}
* @example
* const key1 = doip.keys.fetchHKP('alice@domain.tld');
* const key2 = doip.keys.fetchHKP('123abc123abc');
*/
exports.fetchHKP = (identifier, keyserverDomain) => {
2020-12-05 15:13:44 -07:00
return new Promise(async (resolve, reject) => {
2021-04-22 07:14:21 -06:00
const keyserverBaseUrl = keyserverDomain
? `https://${keyserverDomain}`
: 'https://keys.openpgp.org'
2020-11-16 18:13:56 -07:00
const hkp = new openpgp.HKP(keyserverBaseUrl)
const lookupOpts = {
query: identifier,
}
2021-03-01 10:27:29 -07:00
let publicKey = await hkp.lookup(lookupOpts).catch((error) => {
reject('Key does not exist or could not be fetched')
})
2021-03-01 10:27:29 -07:00
publicKey = await openpgp.key
.readArmored(publicKey)
.then((result) => {
return result.keys[0]
})
.catch((error) => {
return null
})
if (publicKey) {
resolve(publicKey)
} else {
2020-12-05 15:13:44 -07:00
reject('Key does not exist or could not be fetched')
}
})
2020-11-16 18:13:56 -07:00
}
2021-04-22 07:14:21 -06:00
/**
* Fetch a public key using Web Key Directory
* @function
* @param {string} identifier - Identifier of format 'username@domain.tld`
* @returns {openpgp.key.Key}
* @example
* const key = doip.keys.fetchWKD('alice@domain.tld');
*/
exports.fetchWKD = (identifier) => {
2020-12-05 15:13:44 -07:00
return new Promise(async (resolve, reject) => {
2020-11-16 18:13:56 -07:00
const wkd = new openpgp.WKD()
const lookupOpts = {
email: identifier,
}
2021-03-01 10:27:29 -07:00
const publicKey = await wkd
.lookup(lookupOpts)
.then((result) => {
return result.keys[0]
})
.catch((error) => {
return null
})
if (publicKey) {
resolve(publicKey)
} else {
2020-12-05 15:13:44 -07:00
reject('Key does not exist or could not be fetched')
}
})
2020-11-16 18:13:56 -07:00
}
2021-04-22 07:14:21 -06:00
/**
* Fetch a public key from Keybase
* @function
* @param {string} username - Keybase username
* @param {string} fingerprint - Fingerprint of key
* @returns {openpgp.key.Key}
* @example
* const key = doip.keys.fetchKeybase('alice', '123abc123abc');
*/
exports.fetchKeybase = (username, fingerprint) => {
2020-12-05 15:13:44 -07:00
return new Promise(async (resolve, reject) => {
2020-11-16 18:13:56 -07:00
const keyLink = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}`
try {
const rawKeyContent = await req(opts.keyLink)
2020-12-05 15:13:44 -07:00
.then((response) => {
2020-11-16 18:13:56 -07:00
if (response.status === 200) {
return response
}
})
.then((response) => response.text())
} catch (e) {
2020-12-05 15:13:44 -07:00
reject(`Error fetching Keybase key: ${e.message}`)
2020-11-16 18:13:56 -07:00
}
2021-03-01 10:27:29 -07:00
const publicKey = await openpgp.key
.readArmored(rawKeyContent)
.then((result) => {
return result.keys[0]
})
.catch((error) => {
return null
})
if (publicKey) {
resolve(publicKey)
} else {
2020-12-05 15:13:44 -07:00
reject('Key does not exist or could not be fetched')
}
})
2020-11-16 18:13:56 -07:00
}
2021-04-22 07:14:21 -06:00
/**
* Get a public key from plaintext data
* @function
* @param {string} rawKeyContent - Plaintext ASCII-formatted public key data
* @returns {openpgp.key.Key}
* @example
* const plainkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
*
* mQINBF0mIsIBEADacleiyiV+z6FIunvLWrO6ZETxGNVpqM+WbBQKdW1BVrJBBolg
* [...]
* =6lib
* -----END PGP PUBLIC KEY BLOCK-----`
* const key = doip.keys.fetchPlaintext(plainkey);
*/
exports.fetchPlaintext = (rawKeyContent) => {
2020-12-05 15:13:44 -07:00
return new Promise(async (resolve, reject) => {
2020-11-16 18:13:56 -07:00
const publicKey = (await openpgp.key.readArmored(rawKeyContent)).keys[0]
2020-12-05 15:13:44 -07:00
resolve(publicKey)
})
2020-11-16 18:13:56 -07:00
}
2021-04-22 07:14:21 -06:00
/**
* Fetch a public key using an URI
* @function
* @param {string} uri - URI that defines the location of the key
* @returns {openpgp.key.Key}
* @example
* const key1 = doip.keys.fetchURI('hkp:alice@domain.tld');
* const key2 = doip.keys.fetchURI('hkp:123abc123abc');
* const key3 = doip.keys.fetchURI('wkd:alice@domain.tld');
*/
exports.fetchURI = (uri) => {
2020-12-05 15:13:44 -07:00
return new Promise(async (resolve, reject) => {
2020-11-16 18:13:56 -07:00
if (!validUrl.isUri(uri)) {
2020-12-05 15:13:44 -07:00
reject('Invalid URI')
2020-11-16 18:13:56 -07:00
}
2020-12-07 18:56:54 -07:00
const re = /([a-zA-Z0-9]*):([a-zA-Z0-9@._=+\-]*)(?:\:([a-zA-Z0-9@._=+\-]*))?/
2020-11-16 18:13:56 -07:00
const match = uri.match(re)
if (!match[1]) {
2020-12-05 15:13:44 -07:00
reject('Invalid URI')
2020-11-16 18:13:56 -07:00
}
switch (match[1]) {
case 'hkp':
2020-12-20 15:19:07 -07:00
resolve(
2021-04-22 07:38:42 -06:00
exports.fetchHKP(match[3] ? match[3] : match[2], match[3] ? match[2] : null)
2020-12-20 15:19:07 -07:00
)
2020-11-16 18:13:56 -07:00
break
case 'wkd':
2021-04-22 07:38:42 -06:00
resolve(exports.fetchWKD(match[2]))
2020-11-16 18:13:56 -07:00
break
case 'kb':
2021-04-22 07:38:42 -06:00
resolve(exports.fetchKeybase(match[2], match.length >= 4 ? match[3] : null))
2020-11-16 18:13:56 -07:00
break
default:
2020-12-05 15:13:44 -07:00
reject('Invalid URI protocol')
2020-11-16 18:13:56 -07:00
break
}
2020-12-05 15:13:44 -07:00
})
2020-11-16 18:13:56 -07:00
}
2021-04-22 07:14:21 -06:00
/**
* Process a public key to get user data and claims
* @function
* @param {openpgp.key.Key} publicKey - The public key to process
* @returns {object}
* @example
* const key = doip.keys.fetchURI('hkp:alice@domain.tld');
* const data = doip.keys.process(key);
* data.users[0].claims.forEach(claim => {
* console.log(claim.uri);
* });
*/
exports.process = (publicKey) => {
2020-12-05 15:13:44 -07:00
return new Promise(async (resolve, reject) => {
2021-04-19 03:06:29 -06:00
if (!publicKey || !(publicKey instanceof openpgp.key.Key)) {
2020-12-05 15:13:44 -07:00
reject('Invalid public key')
}
2021-04-19 03:06:29 -06:00
2020-11-16 18:13:56 -07:00
const fingerprint = await publicKey.primaryKey.getFingerprint()
const primaryUser = await publicKey.getPrimaryUser()
const users = publicKey.users
2021-04-19 03:06:29 -06:00
let usersOutput = []
users.forEach((user, i) => {
usersOutput[i] = {
userData: {
2020-12-26 06:08:21 -07:00
id: user.userId ? user.userId.userid : null,
name: user.userId ? user.userId.name : null,
email: user.userId ? user.userId.email : null,
comment: user.userId ? user.userId.comment : null,
2020-12-05 15:13:44 -07:00
isPrimary: primaryUser.index === i,
},
2021-04-19 03:06:29 -06:00
claims: [],
}
2020-12-26 06:08:21 -07:00
if ('selfCertifications' in user && user.selfCertifications.length > 0) {
const notations = user.selfCertifications[0].rawNotations
2021-04-19 03:06:29 -06:00
usersOutput[i].claims = notations.map(
({ name, value, humanReadable }) => {
if (humanReadable && name === 'proof@metacode.biz') {
2021-04-19 03:06:29 -06:00
const notation = openpgp.util.decode_utf8(value)
return new Claim(notation, fingerprint)
}
2020-12-05 15:13:44 -07:00
}
)
}
})
2020-11-16 18:13:56 -07:00
2020-12-05 15:13:44 -07:00
resolve({
2020-11-16 18:13:56 -07:00
fingerprint: fingerprint,
users: usersOutput,
primaryUserIndex: primaryUser.index,
2021-04-19 03:06:29 -06:00
key: {
data: publicKey,
fetchMethod: null,
2021-04-19 03:44:30 -06:00
uri: null,
},
2020-12-05 15:13:44 -07:00
})
})
2021-04-22 07:14:21 -06:00
}