diff --git a/.zed/tasks.json b/.zed/tasks.json new file mode 100644 index 0000000..78bdfaf --- /dev/null +++ b/.zed/tasks.json @@ -0,0 +1,9 @@ +// Static tasks configuration. +// +// Example: +[ + { + "label": "Deno Test", + "command": "deno test -A" + } +] diff --git a/crypto/crypto.test.ts b/crypto/crypto.test.ts index d41867d..ca036f1 100644 --- a/crypto/crypto.test.ts +++ b/crypto/crypto.test.ts @@ -17,7 +17,7 @@ Deno.test("encode & decode keypair", async () => { Deno.test("sign & verify payload", async () => { const payload = { foo: "bar", baz: 123 }; const signedPayload = await keypair.signPayload(payload); - const verifiedPayload = await keypair.publicIdentity.verifyPayload( + const verifiedPayload = await keypair.publicKey.verifyPayload( signedPayload, ); @@ -28,7 +28,7 @@ Deno.test("sign & verify payload with wrong keypair", async () => { const keypair2 = await BlahKeyPair.generate(); const payload = { foo: "bar", baz: 123 }; const signedPayload = await keypair.signPayload(payload); - expect(keypair2.publicIdentity.verifyPayload(signedPayload)) + expect(keypair2.publicKey.verifyPayload(signedPayload)) .rejects.toMatch(/sign/); }); @@ -39,12 +39,12 @@ Deno.test("sign & verify payload with wrong key order but should still work", as sig: signedPayload.sig, signee: { payload: { baz: 123, foo: "bar" }, - user: signedPayload.signee.user, + id_key: signedPayload.signee.id_key, nonce: signedPayload.signee.nonce, timestamp: signedPayload.signee.timestamp, }, }; - const verifiedPayload = await keypair.publicIdentity.verifyPayload( + const verifiedPayload = await keypair.publicKey.verifyPayload( signedPayload2, ); expect(verifiedPayload).toEqual(payload); diff --git a/crypto/keypair.ts b/crypto/keypair.ts index 3190c33..acefb40 100644 --- a/crypto/keypair.ts +++ b/crypto/keypair.ts @@ -1,5 +1,5 @@ import canonicalize from "./canonicalize.ts"; -import { BlahPublicIdentity } from "./publicIdentity.ts"; +import { BlahPublicKey } from "./publicKey.ts"; import type { BlahPayloadSignee, BlahSignedPayload } from "./signedPayload.ts"; import { bufToHex } from "./utils.ts"; @@ -10,21 +10,21 @@ export type EncodedBlahKeyPair = { }; export class BlahKeyPair { - publicIdentity: BlahPublicIdentity; + publicKey: BlahPublicKey; private privateKey: CryptoKey; get id() { - return this.publicIdentity.id; + return this.publicKey.id; } get name() { - return this.publicIdentity.name; + return this.publicKey.name; } private constructor( - publicIdentity: BlahPublicIdentity, + publicIdentity: BlahPublicKey, privateKey: CryptoKey, ) { - this.publicIdentity = publicIdentity; + this.publicKey = publicIdentity; this.privateKey = privateKey; } @@ -37,7 +37,7 @@ export class BlahKeyPair { "verify", ], ) as CryptoKeyPair; - const publicIdentity = await BlahPublicIdentity.fromPublicKey(publicKey); + const publicIdentity = await BlahPublicKey.fromPublicKey(publicKey); return new BlahKeyPair(publicIdentity, privateKey); } @@ -45,7 +45,7 @@ export class BlahKeyPair { if (encoded.v !== "0") { throw new Error("Unsupported version"); } - const publicIdentity = await BlahPublicIdentity.fromID(encoded.id); + const publicIdentity = await BlahPublicKey.fromID(encoded.id); const privateKey = await crypto.subtle.importKey( "jwk", encoded.privateKey, @@ -60,7 +60,7 @@ export class BlahKeyPair { async encode(): Promise { return { v: "0", - id: this.publicIdentity.id, + id: this.publicKey.id, privateKey: await crypto.subtle.exportKey("jwk", this.privateKey), }; } @@ -68,6 +68,7 @@ export class BlahKeyPair { async signPayload

( payload: P, date: Date = new Date(), + identityKeyId?: string, ): Promise> { const nonceBuf = new Uint32Array(1); crypto.getRandomValues(nonceBuf); @@ -78,8 +79,12 @@ export class BlahKeyPair { nonce: nonceBuf[0], payload, timestamp, - user: this.id, + id_key: identityKeyId ?? this.id, }; + if (identityKeyId) { + signee.act_key = this.id; + } + const signeeBytes = new TextEncoder().encode(canonicalize(signee)); const rawSig = await crypto.subtle.sign( diff --git a/crypto/mod.ts b/crypto/mod.ts index a5f640a..5a3ba3c 100644 --- a/crypto/mod.ts +++ b/crypto/mod.ts @@ -1,4 +1,4 @@ export * from "./keypair.ts"; -export * from "./publicIdentity.ts"; +export * from "./publicKey.ts"; export * from "./signedPayload.ts"; export * from "./utils.ts"; diff --git a/crypto/publicIdentity.ts b/crypto/publicKey.ts similarity index 73% rename from crypto/publicIdentity.ts rename to crypto/publicKey.ts index efb0d04..b69afc7 100644 --- a/crypto/publicIdentity.ts +++ b/crypto/publicKey.ts @@ -2,7 +2,7 @@ import canonicalize from "./canonicalize.ts"; import type { BlahSignedPayload } from "./signedPayload.ts"; import { bufToHex, hexToBuf } from "./utils.ts"; -export class BlahPublicIdentity { +export class BlahPublicKey { private publicKey: CryptoKey; id: string; name: string; @@ -16,13 +16,13 @@ export class BlahPublicIdentity { static async fromPublicKey( publicKey: CryptoKey, - ): Promise { + ): Promise { const rawKey = await crypto.subtle.exportKey("raw", publicKey); const id = bufToHex(rawKey); - return new BlahPublicIdentity(publicKey, id); + return new BlahPublicKey(publicKey, id); } - static async fromID(id: string): Promise { + static async fromID(id: string): Promise { const rawKey = hexToBuf(id); const publicKey = await crypto.subtle.importKey( "raw", @@ -33,22 +33,23 @@ export class BlahPublicIdentity { "verify", ], ); - return new BlahPublicIdentity(publicKey, id); + return new BlahPublicKey(publicKey, id); } static async verifyPayload

( signedPayload: BlahSignedPayload

, - ): Promise<{ payload: P; identity: BlahPublicIdentity }> { + ): Promise<{ payload: P; key: BlahPublicKey }> { const { signee } = signedPayload; - const identity = await BlahPublicIdentity.fromID(signee.user); - return { payload: await identity.verifyPayload(signedPayload), identity }; + const key = await BlahPublicKey.fromID(signee.act_key ?? signee.id_key); + return { payload: await key.verifyPayload(signedPayload), key }; } async verifyPayload

(signedPayload: BlahSignedPayload

): Promise

{ const { sig, signee } = signedPayload; - if (signee.user !== this.id) { + const signingKey = signee.act_key ?? signee.id_key; + if (signingKey !== this.id) { throw new Error( - `Payload is not signed by this identity. Was signed by ${signee.user}.`, + `Payload is not signed by this identity. Was signed by ${signingKey}.`, ); } const signeeBytes = new TextEncoder().encode(canonicalize(signee)); diff --git a/crypto/signedPayload.ts b/crypto/signedPayload.ts index b3c0ec2..52aee39 100644 --- a/crypto/signedPayload.ts +++ b/crypto/signedPayload.ts @@ -2,7 +2,7 @@ export type BlahPayloadSignee

= { nonce: number; payload: P; timestamp: number; - user: string; + id_key: string; act_key?: string; };