mirror of
https://github.com/Blah-IM/typescript-core.git
synced 2025-04-30 16:21:10 +00:00
refactor: id / act key support
This commit is contained in:
parent
74f1c59566
commit
c5f6659d8f
6 changed files with 41 additions and 26 deletions
9
.zed/tasks.json
Normal file
9
.zed/tasks.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Static tasks configuration.
|
||||
//
|
||||
// Example:
|
||||
[
|
||||
{
|
||||
"label": "Deno Test",
|
||||
"command": "deno test -A"
|
||||
}
|
||||
]
|
|
@ -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);
|
||||
|
|
|
@ -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<EncodedBlahKeyPair> {
|
||||
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<P>(
|
||||
payload: P,
|
||||
date: Date = new Date(),
|
||||
identityKeyId?: string,
|
||||
): Promise<BlahSignedPayload<P>> {
|
||||
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(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export * from "./keypair.ts";
|
||||
export * from "./publicIdentity.ts";
|
||||
export * from "./publicKey.ts";
|
||||
export * from "./signedPayload.ts";
|
||||
export * from "./utils.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<BlahPublicIdentity> {
|
||||
): Promise<BlahPublicKey> {
|
||||
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<BlahPublicIdentity> {
|
||||
static async fromID(id: string): Promise<BlahPublicKey> {
|
||||
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<P>(
|
||||
signedPayload: BlahSignedPayload<P>,
|
||||
): 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<P>(signedPayload: BlahSignedPayload<P>): Promise<P> {
|
||||
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));
|
|
@ -2,7 +2,7 @@ export type BlahPayloadSignee<P> = {
|
|||
nonce: number;
|
||||
payload: P;
|
||||
timestamp: number;
|
||||
user: string;
|
||||
id_key: string;
|
||||
act_key?: string;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue