~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs

0
0
main
111 lines · 7.2 KB Raw History
import assert from "node:assert/strict";
import { createPublicKey, verify } from "node:crypto";
import { test } from "node:test";

import { createDidKey, didkeyCall, GET_SERVICE_AUTH, ingressHost, SIGN, serviceAud } from "./index.js";

const N = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n;
const key = await createDidKey();
const pub = createPublicKey({ key: { kty: "EC", crv: "P-256", x: key.jwk.x, y: key.jwk.y }, format: "jwk" });
const verifies = (data, sig) => verify("sha256", data, { key: pub, dsaEncoding: "ieee-p1363" }, sig);
const unb64url = (text) => Buffer.from(text, "base64url");

test("the did is a P-256 did:key, stable for the same JWK", async () => {
	assert.match(key.did, /^did:key:zDn[1-9A-HJ-NP-Za-km-z]{44,46}$/);
	assert.equal((await createDidKey(key.jwk)).did, key.did);
	assert.notEqual((await createDidKey()).did, key.did);
	assert.equal(ingressHost(key.did), `${key.did.replaceAll(":", "-").toLowerCase()}.xrpc.fedproxy.com`);
});

test("sign is sha256 ECDSA, compact and low-S", async () => {
	for (let i = 0; i < 20; i++) {
		const data = crypto.getRandomValues(new Uint8Array(64));
		const sig = await key.sign(data);
		assert.equal(sig.length, 64);
		assert.ok(BigInt(`0x${Buffer.from(sig.subarray(32)).toString("hex")}`) <= N / 2n);
		assert.ok(verifies(data, sig));
	}
});

test("serviceAuth is an ES256 JWT the did:key signs for itself, exp capped at an hour", async () => {
	const now = Math.floor(Date.now() / 1000);
	const token = await key.serviceAuth({ aud: "did:web:xrpc.fedproxy.com", lxm: "com.fedproxy.temp.xrpc.subscribe", exp: now + 99999 });
	const [header, payload, sig] = token.split(".");
	assert.deepEqual(JSON.parse(unb64url(header)), { typ: "JWT", alg: "ES256" });
	const claims = JSON.parse(unb64url(payload));
	assert.equal(claims.iss, key.did);
	assert.equal(claims.aud, "did:web:xrpc.fedproxy.com");
	assert.equal(claims.lxm, "com.fedproxy.temp.xrpc.subscribe");
	assert.ok(claims.exp <= now + 3601 && claims.exp >= now + 3599);
	assert.ok(verifies(Buffer.from(`${header}.${payload}`), unb64url(sig)));
	const short = JSON.parse(unb64url((await key.serviceAuth({ aud: "did:web:x", lxm: "a.b.c" })).split(".")[1]));
	assert.ok(short.exp - short.iat === 300);
});

test("didkeyCall answers the pseudo-calls, signs service calls it is told to, passes the rest on", async () => {
	const fetched = [];
	const passed = [];
	const call = didkeyCall(key, async (nsid, request) => (passed.push([nsid, request]), { inner: true }), {
		services: ["https://xrpc.fedproxy.com"],
		fetch: async (url, init) => (fetched.push([url, init]), new Response(JSON.stringify({ nonce: "bm9uY2U=" }))),
	});

	const auth = await call(GET_SERVICE_AUTH, { params: { aud: "did:web:xrpc.fedproxy.com", lxm: "com.fedproxy.temp.xrpc.getRegistrationNonce" } });
	assert.equal(auth.did, key.did);
	assert.equal(JSON.parse(unb64url(auth.token.split(".")[1])).lxm, "com.fedproxy.temp.xrpc.getRegistrationNonce");
	await assert.rejects(call(GET_SERVICE_AUTH, { params: { aud: "nope", lxm: "a.b.c" } }), /aud/);

	const nonce = crypto.getRandomValues(new Uint8Array(64));
	const signed = await call(SIGN, { input: { data: Buffer.from(nonce).toString("base64") } });
	assert.equal(signed.did, key.did);
	assert.ok(verifies(nonce, Buffer.from(signed.signature, "base64")));
	await assert.rejects(call(SIGN, { input: {} }), /data/);
	// only the relay nonce shape: a module must not get a JWS signing input signed (its own JWT)
	const jws = (await key.serviceAuth({ aud: "did:web:x", lxm: "a.b.c" })).split(".").slice(0, 2).join(".");
	for (const bad of [Buffer.from("hello"), Buffer.from(jws), Buffer.from(jws.slice(0, 64)), Buffer.concat([nonce, nonce])])
		await assert.rejects(call(SIGN, { input: { data: bad.toString("base64") } }), /only a relay nonce/);

	assert.deepEqual(await call("com.fedproxy.temp.xrpc.getRegistrationNonce", { service: "https://xrpc.fedproxy.com", input: { key: key.did, signatures: [] } }), { nonce: "bm9uY2U=" });
	const [url, init] = fetched[0];
	assert.equal(url, "https://xrpc.fedproxy.com/xrpc/com.fedproxy.temp.xrpc.getRegistrationNonce");
	assert.equal(init.method, "POST");
	assert.deepEqual(JSON.parse(init.body), { key: key.did, signatures: [] });
	const claims = JSON.parse(unb64url(init.headers.authorization.slice("Bearer ".length).split(".")[1]));
	assert.deepEqual([claims.iss, claims.aud, claims.lxm], [key.did, "did:web:xrpc.fedproxy.com", "com.fedproxy.temp.xrpc.getRegistrationNonce"]);

	// Subscriptions, other services and plain calls are the inner call's.
	await call("com.fedproxy.temp.xrpc.subscribe", { service: "https://xrpc.fedproxy.com", subscribe: true, params: {} });
	await call("a.b.c", { service: "https://elsewhere.example" });
	await call("com.atproto.repo.describeRepo", { service: "did:plc:abc", params: { repo: "did:plc:abc" } });
	assert.deepEqual(passed.map(([nsid]) => nsid), ["com.fedproxy.temp.xrpc.subscribe", "a.b.c", "com.atproto.repo.describeRepo"]);
	assert.equal(fetched.length, 1);
});

test("serviceAud: did:web of the service host, a port as %3A<port> (the Rust host's service_aud)", () => {
	assert.equal(serviceAud("https://svc.example/x"), "did:web:svc.example");
	assert.equal(serviceAud("https://svc.example:443/"), "did:web:svc.example");
	assert.equal(serviceAud("https://svc.example:8443/xrpc"), "did:web:svc.example%3A8443");
});

test("didkeyCall: a subscribe with serviceAuth to a listed service carries a token the did:key signs", async () => {
	const key = await createDidKey();
	const seen = [];
	const inner = async (nsid, request) => (seen.push(request), { async *[Symbol.asyncIterator]() { yield "message"; }, close() {} });
	const call = didkeyCall(key, inner, { services: ["https://xrpc.fedproxy.com"] });
	const NSID = "com.fedproxy.temp.xrpc.subscribe";
	const first = await call(NSID, { subscribe: true, service: "https://xrpc.fedproxy.com", params: { did: key.did }, serviceAuth: { param: "service_auth" } });
	// one serving instance per did:key: another relay subscribe is refused while this one is open
	await assert.rejects(call(NSID, { subscribe: true, service: "https://xrpc.fedproxy.com", serviceAuth: {} }), /already serving in another run/);
	first.close();
	const second = await call(NSID, { subscribe: true, service: "https://xrpc.fedproxy.com", serviceAuth: {} });
	for await (const message of second) assert.equal(message, "message"); // the stream ending gives the lease back too
	(await call(NSID, { subscribe: true, service: "https://xrpc.fedproxy.com", serviceAuth: {} })).close();
	await call(NSID, { subscribe: true, service: "https://other.example", serviceAuth: {} });
	const claims = (jwt) => JSON.parse(Buffer.from(jwt.split(".")[1], "base64url"));
	assert.equal(seen[0].serviceAuth, undefined);
	assert.deepEqual({ ...claims(seen[0].params.service_auth), exp: 0, iat: 0, jti: 0 }, { ...claims(seen[0].params.service_auth), iss: key.did, aud: "did:web:xrpc.fedproxy.com", lxm: NSID, exp: 0, iat: 0, jti: 0 });
	assert.match(seen[1].headers.authorization, /^Bearer ey/);
	assert.deepEqual(seen[3].serviceAuth, {}, "not a listed service: left for the host (the session's token)");
	const browser = didkeyCall(key, inner, { services: ["https://xrpc.fedproxy.com"], canHeader: false });
	await assert.rejects(browser(NSID, { subscribe: true, service: "https://xrpc.fedproxy.com", serviceAuth: {} }), /browsers cannot set WebSocket headers/);
});