/** * DTP-19 — CdnUploadToken issuance and verification. */ import { describe, expect, it } from "@suaveplan/testing/runner"; import type { CdnUploadToken } from "../src/index.js"; import { canonicalSerializeUploadToken, type DriveTemplateError, DTP_CODES, signUploadToken, UPLOAD_TOKEN_TTL_DEFAULT_MS, UPLOAD_TOKEN_TTL_MAX_MS, verifyUploadToken, } from "../src/index.js"; import { makeKeys } from "./_helpers.js"; const NOW = 2_000_000_000; function baseToken(): Omit { return { fileId: "f".repeat(64), chunkId: 12, chunkHash: "blake3:abcdef", uploaderPubkey: "pubkey-hex", }; } describe("DTP-19 — CdnUploadToken", () => { it("default TTL pinned at 60s; max at 5m", { timeout: 5000 }, () => { expect(UPLOAD_TOKEN_TTL_DEFAULT_MS).toBe(60_000); expect(UPLOAD_TOKEN_TTL_MAX_MS).toBe(300_000); }); it("valid token accepted", { timeout: 5000 }, () => { const keys = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: NOW + 30_000 }, keys.secret, { clock: () => NOW } ); expect(verifyUploadToken(tok, keys.publicHex, () => NOW + 1_000)).toBe( true ); }); it("expired token rejected", { timeout: 5000 }, () => { const keys = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: NOW + 30_000 }, keys.secret, { clock: () => NOW } ); try { verifyUploadToken(tok, keys.publicHex, () => NOW + 100_000); throw new Error("expected expired"); } catch (err) { expect((err as DriveTemplateError).code).toBe( DTP_CODES.CDN_BAD_SIGNATURE ); } }); it("tampered token rejected", { timeout: 5000 }, () => { const keys = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: NOW + 30_000 }, keys.secret, { clock: () => NOW } ); const tampered = { ...tok, chunkId: tok.chunkId + 1 }; try { verifyUploadToken(tampered, keys.publicHex, () => NOW); throw new Error("expected sig failure"); } catch (err) { expect((err as DriveTemplateError).code).toBe( DTP_CODES.CDN_BAD_SIGNATURE ); } }); it("token signed by wrong key rejected", { timeout: 5000 }, () => { const k1 = makeKeys(); const k2 = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: NOW + 30_000 }, k1.secret, { clock: () => NOW } ); try { verifyUploadToken(tok, k2.publicHex, () => NOW); throw new Error("expected sig failure"); } catch (err) { expect((err as DriveTemplateError).code).toBe( DTP_CODES.CDN_BAD_SIGNATURE ); } }); it("invalid server pubkey hex rejected", { timeout: 5000 }, () => { const keys = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: NOW + 30_000 }, keys.secret, { clock: () => NOW } ); try { verifyUploadToken(tok, "not-hex", () => NOW); throw new Error("expected pubkey rejection"); } catch (err) { expect((err as DriveTemplateError).code).toBe( DTP_CODES.CDN_BAD_SIGNATURE ); } }); it("ttlMs out of range rejected at sign", { timeout: 5000 }, () => { const keys = makeKeys(); expect(() => signUploadToken({ ...baseToken(), expiresAt: 0 }, keys.secret, { clock: () => NOW, ttlMs: 0, }) ).toThrow(); expect(() => signUploadToken({ ...baseToken(), expiresAt: 0 }, keys.secret, { clock: () => NOW, ttlMs: UPLOAD_TOKEN_TTL_MAX_MS + 1, }) ).toThrow(); }); it("canonicalSerializeUploadToken is deterministic", { timeout: 5000, }, () => { const tok = { ...baseToken(), expiresAt: NOW + 30_000 }; const a = canonicalSerializeUploadToken(tok); const b = canonicalSerializeUploadToken(tok); expect(a).toEqual(b); }); it("default options use Date.now() as clock", { timeout: 5000 }, () => { const keys = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: 0 }, keys.secret ); expect(tok.expiresAt).toBeGreaterThan(0); }); it("malformed signature buffer triggers verify catch arm", { timeout: 5000, }, () => { const keys = makeKeys(); const tok = signUploadToken( { ...baseToken(), expiresAt: NOW + 30_000 }, keys.secret, { clock: () => NOW } ); const broken = { ...tok, serverSignature: new Uint8Array(8) }; try { verifyUploadToken(broken, keys.publicHex, () => NOW); throw new Error("expected rejection"); } catch (err) { expect((err as DriveTemplateError).code).toBe( DTP_CODES.CDN_BAD_SIGNATURE ); } }); });