|
1 | 1 | import assert from "node:assert/strict"; |
2 | 2 | import { DEFAULT_MODEL_KEY, DEFAULT_REASONING_EFFORT_KEY } from "../src/defaults.ts"; |
| 3 | +import { sha256Base64Url } from "../src/utils.ts"; |
3 | 4 |
|
4 | 5 | const keyToString = (key: Deno.KvKey): string => JSON.stringify(key); |
5 | 6 |
|
@@ -61,6 +62,23 @@ const { handleChatCompletions, handleResponses } = await import("../src/openai.t |
61 | 62 |
|
62 | 63 | const TEXT_ENCODER = new TextEncoder(); |
63 | 64 |
|
| 65 | +const encodeBase64 = (bytes: Uint8Array): string => { |
| 66 | + let binary = ""; |
| 67 | + for (const b of bytes) binary += String.fromCharCode(b); |
| 68 | + return btoa(binary); |
| 69 | +}; |
| 70 | + |
| 71 | +const encodeBase64Url = (bytes: Uint8Array): string => |
| 72 | + encodeBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); |
| 73 | + |
| 74 | +const encodeJsonBase64Url = (value: unknown): string => encodeBase64Url(TEXT_ENCODER.encode(JSON.stringify(value))); |
| 75 | + |
| 76 | +const toPublicKeyPem = (spki: Uint8Array): string => { |
| 77 | + const b64 = encodeBase64(spki); |
| 78 | + const lines = b64.match(/.{1,64}/g) ?? []; |
| 79 | + return `-----BEGIN PUBLIC KEY-----\n${lines.join("\n")}\n-----END PUBLIC KEY-----`; |
| 80 | +}; |
| 81 | + |
64 | 82 | const sseResponse = (chunks: string[]): Response => { |
65 | 83 | const stream = new ReadableStream<Uint8Array>({ |
66 | 84 | start(controller) { |
@@ -281,6 +299,61 @@ Deno.test("openai: responses accept non-message input items", async () => { |
281 | 299 | assert.ok(types.includes("function_call_output")); |
282 | 300 | }); |
283 | 301 |
|
| 302 | +Deno.test("auth: kernel attestation tokens are reusable within TTL", async () => { |
| 303 | + const keyPair = await crypto.subtle.generateKey( |
| 304 | + { |
| 305 | + name: "RSASSA-PKCS1-v1_5", |
| 306 | + modulusLength: 2048, |
| 307 | + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), |
| 308 | + hash: "SHA-256", |
| 309 | + }, |
| 310 | + true, |
| 311 | + ["sign", "verify"], |
| 312 | + ); |
| 313 | + |
| 314 | + const spki = new Uint8Array(await crypto.subtle.exportKey("spki", keyPair.publicKey)); |
| 315 | + const publicPem = toPublicKeyPem(spki); |
| 316 | + kvStore.set(keyToString(["uos_ai", "kernel_pubkeys"]), [{ pem: publicPem }]); |
| 317 | + |
| 318 | + const bearerToken = "ghs_test_token"; |
| 319 | + const nowSeconds = Math.floor(Date.now() / 1000); |
| 320 | + const payload = { |
| 321 | + iss: "ubiquity-os-kernel", |
| 322 | + aud: "ai.ubq.fi", |
| 323 | + iat: nowSeconds, |
| 324 | + exp: nowSeconds + 600, |
| 325 | + jti: `jti_${crypto.randomUUID()}`, |
| 326 | + owner: "acme", |
| 327 | + repo: "demo", |
| 328 | + installation_id: null, |
| 329 | + auth_token_sha256: await sha256Base64Url(bearerToken), |
| 330 | + state_id: "state_test", |
| 331 | + }; |
| 332 | + |
| 333 | + const header = { alg: "RS256", typ: "JWT" }; |
| 334 | + const headerB64 = encodeJsonBase64Url(header); |
| 335 | + const payloadB64 = encodeJsonBase64Url(payload); |
| 336 | + const signingInput = `${headerB64}.${payloadB64}`; |
| 337 | + const signature = new Uint8Array( |
| 338 | + await crypto.subtle.sign("RSASSA-PKCS1-v1_5", keyPair.privateKey, TEXT_ENCODER.encode(signingInput)), |
| 339 | + ); |
| 340 | + const kernelToken = `${signingInput}.${encodeBase64Url(signature)}`; |
| 341 | + |
| 342 | + const { getKernelAttestationContext } = await import("../src/auth.ts"); |
| 343 | + |
| 344 | + const req = new Request("https://ai.ubq.fi/v1/responses", { |
| 345 | + method: "POST", |
| 346 | + headers: { "X-Ubiquity-Kernel-Token": kernelToken }, |
| 347 | + body: "{}", |
| 348 | + }); |
| 349 | + |
| 350 | + const first = await getKernelAttestationContext(req, bearerToken); |
| 351 | + const second = await getKernelAttestationContext(req, bearerToken); |
| 352 | + assert.ok(first); |
| 353 | + assert.ok(second); |
| 354 | + assert.deepEqual(second, first); |
| 355 | +}); |
| 356 | + |
284 | 357 | addEventListener("unload", () => { |
285 | 358 | (Deno as unknown as { openKv?: () => Promise<Deno.Kv> }).openKv = originalOpenKv; |
286 | 359 | }); |
0 commit comments