2020-11-16 18:13:56 -07:00
|
|
|
/*
|
|
|
|
Copyright 2020 Yarmo Mackenbach
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2020-11-20 01:13:08 -07:00
|
|
|
const path = require('path')
|
2020-11-16 18:13:56 -07:00
|
|
|
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')
|
2020-11-16 18:13:56 -07:00
|
|
|
const mergeOptions = require('merge-options')
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const fetchHKP = (identifier, keyserverBaseUrl) => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
2020-11-16 18:13:56 -07:00
|
|
|
keyserverBaseUrl = keyserverBaseUrl
|
2020-12-07 18:29:10 -07:00
|
|
|
? `https://${keyserverBaseUrl}/`
|
2020-11-16 18:13:56 -07:00
|
|
|
: 'https://keys.openpgp.org/'
|
|
|
|
|
|
|
|
const hkp = new openpgp.HKP(keyserverBaseUrl)
|
|
|
|
const lookupOpts = {
|
|
|
|
query: identifier,
|
|
|
|
}
|
|
|
|
let publicKey = await hkp.lookup(lookupOpts)
|
|
|
|
publicKey = (await openpgp.key.readArmored(publicKey)).keys[0]
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
if (publicKey == undefined) {
|
|
|
|
reject('Key does not exist or could not be fetched')
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(publicKey)
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const fetchWKD = (identifier) => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
2020-11-16 18:13:56 -07:00
|
|
|
const wkd = new openpgp.WKD()
|
|
|
|
const lookupOpts = {
|
|
|
|
email: identifier,
|
|
|
|
}
|
|
|
|
const publicKey = (await wkd.lookup(lookupOpts)).keys[0]
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
if (publicKey == undefined) {
|
|
|
|
reject('Key does not exist or could not be fetched')
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(publicKey)
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const fetchKeybase = (username, fingerprint) => {
|
|
|
|
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
|
|
|
}
|
|
|
|
const publicKey = (await openpgp.key.readArmored(rawKeyContent)).keys[0]
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
if (publicKey == undefined) {
|
|
|
|
reject('Key does not exist or could not be fetched')
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(publicKey)
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const fetchPlaintext = (rawKeyContent) => {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const fetchSignature = (rawSignatureContent, keyserverBaseUrl) => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
2020-11-16 18:13:56 -07:00
|
|
|
let sig = await openpgp.signature.readArmored(rawSignatureContent)
|
|
|
|
if ('compressed' in sig.packets[0]) {
|
|
|
|
sig = sig.packets[0]
|
|
|
|
let sigContent = await openpgp.stream.readToEnd(
|
|
|
|
await sig.packets[1].getText()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
const sigUserId = sig.packets[0].signersUserId
|
|
|
|
const sigKeyId = await sig.packets[0].issuerKeyId.toHex()
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
resolve(fetchHKP(sigUserId ? sigUserId : sigKeyId, keyserverBaseUrl))
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const fetchURI = (uri) => {
|
|
|
|
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(
|
|
|
|
fetchHKP(match[3] ? match[3] : match[2], match[3] ? match[2] : null)
|
|
|
|
)
|
2020-11-16 18:13:56 -07:00
|
|
|
break
|
|
|
|
case 'wkd':
|
2020-12-05 15:13:44 -07:00
|
|
|
resolve(fetchWKD(match[2]))
|
2020-11-16 18:13:56 -07:00
|
|
|
break
|
|
|
|
case 'kb':
|
2020-12-05 15:13:44 -07:00
|
|
|
resolve(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
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const process = (publicKey) => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
|
|
|
if (!publicKey) {
|
|
|
|
reject('Invalid public key')
|
|
|
|
}
|
2020-11-16 18:13:56 -07:00
|
|
|
const fingerprint = await publicKey.primaryKey.getFingerprint()
|
2020-11-20 14:53:56 -07:00
|
|
|
const primaryUser = await publicKey.getPrimaryUser()
|
|
|
|
const users = publicKey.users
|
2020-12-05 15:13:44 -07:00
|
|
|
let primaryUserIndex,
|
|
|
|
usersOutput = []
|
2020-11-20 14:53:56 -07:00
|
|
|
|
|
|
|
users.forEach((user, i) => {
|
|
|
|
usersOutput[i] = {
|
|
|
|
userData: {
|
|
|
|
id: user.userId.userid,
|
|
|
|
name: user.userId.name,
|
|
|
|
email: user.userId.email,
|
|
|
|
comment: user.userId.comment,
|
2020-12-05 15:13:44 -07:00
|
|
|
isPrimary: primaryUser.index === i,
|
|
|
|
},
|
2020-11-20 14:53:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const notations = user.selfCertifications[0].rawNotations
|
2020-12-05 15:13:44 -07:00
|
|
|
usersOutput[i].notations = notations.map(
|
|
|
|
({ name, value, humanReadable }) => {
|
|
|
|
if (humanReadable && name === 'proof@metacode.biz') {
|
|
|
|
return openpgp.util.decode_utf8(value)
|
|
|
|
}
|
2020-11-20 14:53:56 -07:00
|
|
|
}
|
2020-12-05 15:13:44 -07:00
|
|
|
)
|
2020-11-20 14:53:56 -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,
|
2020-11-20 14:53:56 -07:00
|
|
|
users: usersOutput,
|
|
|
|
primaryUserIndex: primaryUser.index,
|
2020-12-05 15:13:44 -07:00
|
|
|
})
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const getUserData = (publicKey) => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
2020-11-16 18:13:56 -07:00
|
|
|
const keyData = await process(publicKey)
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
resolve(keyData.users)
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
const getFingerprint = (publicKey) => {
|
|
|
|
return new Promise(async (resolve, reject) => {
|
2020-11-16 18:13:56 -07:00
|
|
|
const keyData = await process(publicKey)
|
|
|
|
|
2020-12-05 15:13:44 -07:00
|
|
|
resolve(keyData.fingerprint)
|
|
|
|
})
|
2020-11-16 18:13:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.fetch = {
|
|
|
|
uri: fetchURI,
|
|
|
|
hkp: fetchHKP,
|
|
|
|
wkd: fetchWKD,
|
|
|
|
keybase: fetchKeybase,
|
|
|
|
plaintext: fetchPlaintext,
|
|
|
|
signature: fetchSignature,
|
|
|
|
}
|
|
|
|
exports.process = process
|
2020-11-20 14:53:56 -07:00
|
|
|
exports.getUserData = getUserData
|
2020-11-16 18:13:56 -07:00
|
|
|
exports.getFingerprint = getFingerprint
|