mirror of
https://codeberg.org/keyoxide/doipjs.git
synced 2024-12-22 14:39:28 -07:00
fix: legacy signature functionality
This commit is contained in:
parent
bc5fe110a7
commit
c3f7df2113
1 changed files with 50 additions and 61 deletions
|
@ -16,35 +16,23 @@ limitations under the License.
|
||||||
import { readCleartextMessage, verify } from 'openpgp'
|
import { readCleartextMessage, verify } from 'openpgp'
|
||||||
import { Claim } from './claim.js'
|
import { Claim } from './claim.js'
|
||||||
import { fetchURI } from './openpgp.js'
|
import { fetchURI } from './openpgp.js'
|
||||||
|
import { Profile } from './profile.js'
|
||||||
|
import { ProfileType, PublicKeyEncoding, PublicKeyType } from './enums.js'
|
||||||
|
import { Persona } from './persona.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module signatures
|
* @module signatures
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract data from a signature and fetch the associated key
|
* Extract the profile from a signature and fetch the associated key
|
||||||
* @async
|
* @async
|
||||||
* @param {string} signature - The plaintext signature to process
|
* @param {string} signature - The plaintext signature to parse
|
||||||
* @returns {Promise<object>}
|
* @returns {Promise<import('./profile.js').Profile>}
|
||||||
*/
|
*/
|
||||||
export async function process (signature) {
|
export async function parse (signature) {
|
||||||
/** @type {import('openpgp').CleartextMessage} */
|
/** @type {import('openpgp').CleartextMessage} */
|
||||||
let sigData
|
let sigData
|
||||||
const result = {
|
|
||||||
fingerprint: null,
|
|
||||||
users: [
|
|
||||||
{
|
|
||||||
userData: {},
|
|
||||||
claims: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
primaryUserIndex: null,
|
|
||||||
key: {
|
|
||||||
data: null,
|
|
||||||
fetchMethod: null,
|
|
||||||
uri: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the signature
|
// Read the signature
|
||||||
try {
|
try {
|
||||||
|
@ -65,6 +53,7 @@ export async function process (signature) {
|
||||||
'https://keys.openpgp.org/'
|
'https://keys.openpgp.org/'
|
||||||
const text = sigData.getText()
|
const text = sigData.getText()
|
||||||
const sigKeys = []
|
const sigKeys = []
|
||||||
|
const claims = []
|
||||||
|
|
||||||
text.split('\n').forEach((line, i) => {
|
text.split('\n').forEach((line, i) => {
|
||||||
const match = line.match(/^([a-zA-Z0-9]*)=(.*)$/i)
|
const match = line.match(/^([a-zA-Z0-9]*)=(.*)$/i)
|
||||||
|
@ -77,7 +66,7 @@ export async function process (signature) {
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'proof':
|
case 'proof':
|
||||||
result.users[0].claims.push(new Claim(match[2]))
|
claims.push(new Claim(match[2]))
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -85,39 +74,49 @@ export async function process (signature) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Try overruling key
|
const obtainedKey = {
|
||||||
|
query: null,
|
||||||
|
data: null,
|
||||||
|
method: null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try key identifier found in the signature
|
||||||
if (sigKeys.length > 0) {
|
if (sigKeys.length > 0) {
|
||||||
try {
|
try {
|
||||||
result.key.uri = sigKeys[0]
|
obtainedKey.query = sigKeys[0]
|
||||||
result.key.data = (await fetchURI(result.key.uri)).publicKey.key
|
/** @type {import('openpgp').PublicKey} */
|
||||||
result.key.fetchMethod = result.key.uri.split(':')[0]
|
obtainedKey.data = (await fetchURI(obtainedKey.query)).publicKey.key
|
||||||
|
obtainedKey.method = obtainedKey.query.split(':')[0]
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
// Try WKD
|
// Try WKD
|
||||||
if (!result.key.data && signersUserID) {
|
if (!obtainedKey.data && signersUserID) {
|
||||||
try {
|
try {
|
||||||
result.key.uri = `wkd:${signersUserID}`
|
obtainedKey.query = signersUserID
|
||||||
result.key.data = (await fetchURI(result.key.uri)).publicKey.key
|
obtainedKey.data = (await fetchURI(`wkd:${signersUserID}`)).publicKey.key
|
||||||
result.key.fetchMethod = 'wkd'
|
obtainedKey.method = 'wkd'
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
// Try HKP
|
// Try HKP
|
||||||
if (!result.key.data) {
|
if (!obtainedKey.data) {
|
||||||
try {
|
try {
|
||||||
const match = preferredKeyServer.match(/^(.*:\/\/)?([^/]*)(?:\/)?$/i)
|
const match = preferredKeyServer.match(/^(.*:\/\/)?([^/]*)(?:\/)?$/i)
|
||||||
result.key.uri = `hkp:${match[2]}:${issuerKeyID || signersUserID}`
|
obtainedKey.query = issuerKeyID || signersUserID
|
||||||
result.key.data = (await fetchURI(result.key.uri)).publicKey.key
|
obtainedKey.data = (await fetchURI(`hkp:${match[2]}:${obtainedKey.query}`)).publicKey.key
|
||||||
result.key.fetchMethod = 'hkp'
|
obtainedKey.method = 'hkp'
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('Public key not found')
|
throw new Error('Public key not found')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const primaryUserData = await obtainedKey.data.getPrimaryUser()
|
||||||
|
const fingerprint = obtainedKey.data.getFingerprint()
|
||||||
|
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
const verificationResult = await verify({
|
const verificationResult = await verify({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
message: sigData,
|
message: sigData,
|
||||||
verificationKeys: result.key.data
|
verificationKeys: obtainedKey.data
|
||||||
})
|
})
|
||||||
const { verified } = verificationResult.signatures[0]
|
const { verified } = verificationResult.signatures[0]
|
||||||
try {
|
try {
|
||||||
|
@ -126,35 +125,25 @@ export async function process (signature) {
|
||||||
throw new Error(`Signature could not be verified (${e.message})`)
|
throw new Error(`Signature could not be verified (${e.message})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.fingerprint = result.key.data.keyPacket.getFingerprint()
|
// Build the persona
|
||||||
|
const persona = new Persona(primaryUserData.user.userID.name, [])
|
||||||
|
persona.setIdentifier(primaryUserData.user.userID.userID)
|
||||||
|
persona.setDescription(primaryUserData.user.userID.comment || null)
|
||||||
|
persona.setEmailAddress(primaryUserData.user.userID.email || null)
|
||||||
|
persona.claims = claims
|
||||||
|
.map(
|
||||||
|
({ value }) =>
|
||||||
|
new Claim(new TextDecoder().decode(value), `openpgp4fpr:${fingerprint}`)
|
||||||
|
)
|
||||||
|
|
||||||
result.users[0].claims.forEach((claim) => {
|
const profile = new Profile(ProfileType.OPENPGP, `openpgp4fpr:${fingerprint}`, [persona])
|
||||||
claim.fingerprint = result.fingerprint
|
|
||||||
})
|
|
||||||
|
|
||||||
const primaryUserData = await result.key.data.getPrimaryUser()
|
profile.publicKey.keyType = PublicKeyType.OPENPGP
|
||||||
let userData
|
profile.publicKey.encoding = PublicKeyEncoding.ARMORED_PGP
|
||||||
|
profile.publicKey.encodedKey = obtainedKey.data.armor()
|
||||||
|
profile.publicKey.key = obtainedKey.data
|
||||||
|
profile.publicKey.fetch.method = obtainedKey.method
|
||||||
|
profile.publicKey.fetch.query = obtainedKey.query
|
||||||
|
|
||||||
if (signersUserID) {
|
return profile
|
||||||
result.key.data.users.forEach((/** @type {{ userID: { email: string; }; }} */ user) => {
|
|
||||||
if (user.userID.email === signersUserID) {
|
|
||||||
userData = user
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!userData) {
|
|
||||||
userData = primaryUserData.user
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue