2021-01-07 08:15:50 -07:00
|
|
|
/*
|
2021-01-13 05:20:33 -07:00
|
|
|
Copyright 2021 Yarmo Mackenbach
|
2021-01-07 08:15:50 -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 openpgp = require('openpgp')
|
2021-04-19 03:06:29 -06:00
|
|
|
const Claim = require('./claim')
|
2021-01-07 08:15:50 -07:00
|
|
|
const keys = require('./keys')
|
|
|
|
|
2021-04-22 07:14:21 -06:00
|
|
|
/**
|
2021-04-22 08:00:37 -06:00
|
|
|
* @module signatures
|
2021-04-22 07:14:21 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract data from a signature and fetch the associated key
|
|
|
|
* @async
|
|
|
|
* @param {string} signature - The plaintext signature to process
|
|
|
|
* @returns {Promise<object>}
|
|
|
|
*/
|
2021-04-19 03:06:29 -06:00
|
|
|
const process = (signature) => {
|
2021-01-07 08:15:50 -07:00
|
|
|
return new Promise(async (resolve, reject) => {
|
2021-04-19 03:06:29 -06:00
|
|
|
let sigData,
|
|
|
|
result = {
|
|
|
|
fingerprint: null,
|
2021-04-19 03:44:30 -06:00
|
|
|
users: [
|
|
|
|
{
|
|
|
|
userData: {},
|
|
|
|
claims: [],
|
|
|
|
},
|
|
|
|
],
|
2021-04-19 03:06:29 -06:00
|
|
|
primaryUserIndex: null,
|
|
|
|
key: {
|
|
|
|
data: null,
|
|
|
|
fetchMethod: null,
|
|
|
|
uri: null,
|
|
|
|
},
|
|
|
|
}
|
2021-01-09 07:17:53 -07:00
|
|
|
|
2021-01-07 08:15:50 -07:00
|
|
|
try {
|
|
|
|
sigData = await openpgp.cleartext.readArmored(signature)
|
|
|
|
} catch (error) {
|
2021-04-22 07:38:42 -06:00
|
|
|
reject(new Error('invalid_signature'))
|
2021-01-09 07:17:53 -07:00
|
|
|
return
|
2021-01-07 08:15:50 -07:00
|
|
|
}
|
|
|
|
|
2021-01-09 07:17:53 -07:00
|
|
|
const issuerKeyId = sigData.signature.packets[0].issuerKeyId.toHex()
|
|
|
|
const signersUserId = sigData.signature.packets[0].signersUserId
|
|
|
|
const preferredKeyServer =
|
|
|
|
sigData.signature.packets[0].preferredKeyServer ||
|
2021-01-10 09:56:59 -07:00
|
|
|
'https://keys.openpgp.org/'
|
2021-01-07 08:15:50 -07:00
|
|
|
const text = sigData.getText()
|
|
|
|
let sigKeys = []
|
2021-01-09 07:17:53 -07:00
|
|
|
|
2021-01-07 08:15:50 -07:00
|
|
|
text.split('\n').forEach((line, i) => {
|
2021-01-10 04:26:18 -07:00
|
|
|
const match = line.match(/^([a-zA-Z0-9]*)\=(.*)$/i)
|
2021-01-07 08:15:50 -07:00
|
|
|
if (!match) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch (match[1].toLowerCase()) {
|
|
|
|
case 'key':
|
|
|
|
sigKeys.push(match[2])
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'proof':
|
2021-04-19 03:06:29 -06:00
|
|
|
result.users[0].claims.push(new Claim(match[2]))
|
2021-01-07 08:15:50 -07:00
|
|
|
break
|
2021-01-07 08:17:24 -07:00
|
|
|
|
2021-01-07 08:15:50 -07:00
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-01-09 07:17:53 -07:00
|
|
|
// Try overruling key
|
|
|
|
if (sigKeys.length > 0) {
|
|
|
|
try {
|
2021-04-19 03:06:29 -06:00
|
|
|
result.key.uri = sigKeys[0]
|
2021-04-22 07:38:42 -06:00
|
|
|
result.key.data = await keys.fetchURI(result.key.uri)
|
2021-04-19 03:06:29 -06:00
|
|
|
result.key.fetchMethod = result.key.uri.split(':')[0]
|
2021-03-01 10:27:29 -07:00
|
|
|
} catch (e) {}
|
2021-01-09 07:17:53 -07:00
|
|
|
}
|
|
|
|
// Try WKD
|
2021-04-19 03:06:29 -06:00
|
|
|
if (!result.key.data && signersUserId) {
|
2021-01-09 07:17:53 -07:00
|
|
|
try {
|
2021-04-19 03:06:29 -06:00
|
|
|
result.key.uri = `wkd:${signersUserId}`
|
2021-04-22 07:38:42 -06:00
|
|
|
result.key.data = await keys.fetchURI(result.key.uri)
|
2021-04-19 03:06:29 -06:00
|
|
|
result.key.fetchMethod = 'wkd'
|
2021-03-01 10:27:29 -07:00
|
|
|
} catch (e) {}
|
2021-01-09 07:17:53 -07:00
|
|
|
}
|
|
|
|
// Try HKP
|
2021-04-19 03:06:29 -06:00
|
|
|
if (!result.key.data) {
|
2021-01-09 07:17:53 -07:00
|
|
|
try {
|
|
|
|
const match = preferredKeyServer.match(/^(.*\:\/\/)?([^/]*)(?:\/)?$/i)
|
2021-04-19 03:44:30 -06:00
|
|
|
result.key.uri = `hkp:${match[2]}:${
|
|
|
|
issuerKeyId ? issuerKeyId : signersUserId
|
|
|
|
}`
|
2021-04-22 07:38:42 -06:00
|
|
|
result.key.data = await keys.fetchURI(result.key.uri)
|
2021-04-19 03:06:29 -06:00
|
|
|
result.key.fetchMethod = 'hkp'
|
2021-03-01 10:27:29 -07:00
|
|
|
} catch (e) {
|
2021-04-22 07:38:42 -06:00
|
|
|
reject(new Error('key_not_found'))
|
2021-01-09 07:17:53 -07:00
|
|
|
return
|
|
|
|
}
|
2021-01-07 08:15:50 -07:00
|
|
|
}
|
2021-01-07 08:17:24 -07:00
|
|
|
|
2021-04-19 03:06:29 -06:00
|
|
|
result.fingerprint = result.key.data.keyPacket.getFingerprint()
|
2021-01-07 08:17:24 -07:00
|
|
|
|
2021-04-19 03:44:30 -06:00
|
|
|
result.users[0].claims.forEach((claim) => {
|
2021-04-19 03:06:29 -06:00
|
|
|
claim.fingerprint = result.fingerprint
|
|
|
|
})
|
|
|
|
|
|
|
|
const primaryUserData = await result.key.data.getPrimaryUser()
|
|
|
|
let userData
|
|
|
|
|
|
|
|
if (signersUserId) {
|
2021-04-19 03:44:30 -06:00
|
|
|
result.key.data.users.forEach((user) => {
|
2021-04-19 03:06:29 -06:00
|
|
|
if (user.userId.email == signersUserId) {
|
|
|
|
userData = user
|
|
|
|
}
|
|
|
|
})
|
2021-04-19 03:44:30 -06:00
|
|
|
}
|
2021-04-19 03:06:29 -06:00
|
|
|
if (!userData) {
|
|
|
|
userData = primaryUserData.user
|
2021-01-07 08:15:50 -07:00
|
|
|
}
|
|
|
|
|
2021-04-19 03:06:29 -06:00
|
|
|
result.users[0].userData = {
|
|
|
|
id: userData.userId ? userData.userId.userid : null,
|
|
|
|
name: userData.userId ? userData.userId.name : null,
|
|
|
|
email: userData.userId ? userData.userId.email : null,
|
|
|
|
comment: userData.userId ? userData.userId.comment : null,
|
|
|
|
isPrimary: primaryUserData.user.userId.userid === userData.userId.userid,
|
|
|
|
}
|
|
|
|
|
|
|
|
result.primaryUserIndex = result.users[0].userData.isPrimary ? 0 : null
|
|
|
|
|
|
|
|
resolve(result)
|
2021-01-07 08:15:50 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-19 03:06:29 -06:00
|
|
|
exports.process = process
|