From a2f8d7e7df8c8bd6e44019a6a40367bc8a9a942e Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 12 Jun 2024 14:35:59 +0200 Subject: [PATCH] sign both ECDH as well as ECDSA keys --- frontend/src/common/wot.ts | 28 ++++++++++++++++++------ frontend/src/components/TrustDetails.vue | 4 ++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/frontend/src/common/wot.ts b/frontend/src/common/wot.ts index d6153619..58fc1e14 100644 --- a/frontend/src/common/wot.ts +++ b/frontend/src/common/wot.ts @@ -4,15 +4,29 @@ import { UserKeys, asPublicKey } from './crypto'; import { JWT, JWTHeader } from './jwt'; import userdata from './userdata'; +export type SignedKeys = { + ecdhPublicKey: string; + ecdsaPublicKey: string; +} + +function deeplyEqual(a: SignedKeys, b: SignedKeys) { + return a.ecdhPublicKey === b.ecdhPublicKey + && a.ecdsaPublicKey === b.ecdsaPublicKey; +} + /** * Signs the public key of a user with my private key and sends the signature to the backend. * @param user The user whose keys to sign * @returns The new trust object created during the signing process */ async function sign(user: UserDto): Promise { - if (!user.ecdsaPublicKey) { + if (!user.ecdhPublicKey || !user.ecdsaPublicKey) { throw new Error('No public key to sign'); } + const toSign: SignedKeys = { + ecdhPublicKey: user.ecdhPublicKey, + ecdsaPublicKey: user.ecdsaPublicKey + }; const me = await userdata.me; const userKeys = await userdata.decryptUserKeysWithBrowser(); const signature = await JWT.build({ @@ -22,7 +36,7 @@ async function sign(user: UserDto): Promise { iss: me.id, sub: user.id, iat: Math.floor(Date.now() / 1000) - }, user.ecdsaPublicKey, userKeys.ecdsaKeyPair.privateKey); + }, toSign, userKeys.ecdsaKeyPair.privateKey); await backend.trust.trustUser(user.id, signature); const trust = await backend.trust.get(user.id); return trust!; @@ -33,7 +47,7 @@ async function sign(user: UserDto): Promise { * @param signatureChain The signature chain, where the first element is signed by me * @param allegedSignedKey The public key that should be signed by the last signature in the chain */ -async function verify(signatureChain: string[], allegedSignedKey: string) { +async function verify(signatureChain: string[], allegedSignedKey: SignedKeys) { let signerPublicKey = await userdata.decryptUserKeysWithBrowser().then(keys => keys.ecdsaKeyPair.publicKey); await verifyRescursive(signatureChain, signerPublicKey, allegedSignedKey); } @@ -45,18 +59,18 @@ async function verify(signatureChain: string[], allegedSignedKey: string) { * @param allegedSignedKey The public key that should be signed by the last signature in the chain * @throws Error if the signature chain is invalid */ -async function verifyRescursive(signatureChain: string[], signerPublicKey: CryptoKey, allegedSignedKey: string) { +async function verifyRescursive(signatureChain: string[], signerPublicKey: CryptoKey, allegedSignedKey: SignedKeys) { // get first element of signature chain: const [signature, ...remainingChain] = signatureChain; - const [_, signedPublicKey] = await JWT.parse(signature, signerPublicKey) as [JWTHeader, string]; + const [_, signedKeys] = await JWT.parse(signature, signerPublicKey) as [JWTHeader, SignedKeys]; if (remainingChain.length === 0) { // last element in chain should match signed public key - if (signedPublicKey !== allegedSignedKey) { + if (!deeplyEqual(signedKeys, allegedSignedKey)) { throw new Error('Alleged public key does not match signed public key'); } } else { // otherwise, the payload is an intermediate public key used to sign the next element - const nextTrustedPublicKey = await asPublicKey(base64.parse(signedPublicKey), UserKeys.ECDSA_KEY_DESIGNATION, UserKeys.ECDSA_PUB_KEY_USAGES); + const nextTrustedPublicKey = await asPublicKey(base64.parse(signedKeys.ecdsaPublicKey), UserKeys.ECDSA_KEY_DESIGNATION, UserKeys.ECDSA_PUB_KEY_USAGES); await verifyRescursive(remainingChain, nextTrustedPublicKey, allegedSignedKey); } } diff --git a/frontend/src/components/TrustDetails.vue b/frontend/src/components/TrustDetails.vue index 35846213..842cfe37 100644 --- a/frontend/src/components/TrustDetails.vue +++ b/frontend/src/components/TrustDetails.vue @@ -67,9 +67,9 @@ async function computeTrustLevel(trust?: TrustDto) { const me = await userdata.me; if (me.id === props.trustedUser.id) { return 0; // Self - } else if (trust && props.trustedUser.ecdsaPublicKey) { + } else if (trust && props.trustedUser.ecdhPublicKey && props.trustedUser.ecdsaPublicKey) { try { - await wot.verify(trust.signatureChain, props.trustedUser.ecdsaPublicKey); + await wot.verify(trust.signatureChain, { ecdhPublicKey: props.trustedUser.ecdhPublicKey, ecdsaPublicKey: props.trustedUser.ecdsaPublicKey }); return trust.signatureChain.length; } catch (error) { console.error('WoT signature verification failed.', error);