Skip to content

Commit

Permalink
sign both ECDH as well as ECDSA keys
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Jun 12, 2024
1 parent 216e2f9 commit a2f8d7e
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 9 deletions.
28 changes: 21 additions & 7 deletions frontend/src/common/wot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TrustDto> {
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({
Expand All @@ -22,7 +36,7 @@ async function sign(user: UserDto): Promise<TrustDto> {
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!;
Expand All @@ -33,7 +47,7 @@ async function sign(user: UserDto): Promise<TrustDto> {
* @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);
}
Expand All @@ -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);
}
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/TrustDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit a2f8d7e

Please sign in to comment.