chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// A KEM key for an account under test, and the syncer's side of a MACed read.
import { compressPublicKey } from '@pdsjs/core/crypto';
import { publicKeyToDidKey } from '@pdsjs/core/plc';
import { encapsulate } from '../../src/kem.js';
import { formatWantMacHeader, parseMacHeader } from '../../src/mac.js';
/**
* @returns {Promise<{privateKey: Uint8Array, didKey: string}>}
*/
export async function makeKemKey() {
const pair = /** @type {CryptoKeyPair} */ (
await crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256' },
true,
['deriveBits'],
)
);
const jwk = await crypto.subtle.exportKey('jwk', pair.privateKey);
const d = /** @type {string} */ (jwk.d).replace(/-/g, '+').replace(/_/g, '/');
const privateKey = Uint8Array.from(
atob(d.padEnd(Math.ceil(d.length / 4) * 4, '=')),
(c) => c.charCodeAt(0),
);
const raw = new Uint8Array(
await crypto.subtle.exportKey('raw', pair.publicKey),
);
return {
privateKey,
didKey: publicKeyToDidKey(compressPublicKey(raw), 'p256'),
};
}
/**
* A fresh Want-Mac header against an author's KEM key, and the secret the
* response will be MACed under.
* @param {'atproto-space-repo'|'atproto-space-record'} type
* @param {string} authorKemKey
* @returns {Promise<{header: string, sharedSecret: Uint8Array}>}
*/
export async function wantMac(type, authorKemKey) {
const { keyShare, sharedSecret } = await encapsulate(authorKemKey);
return { header: formatWantMacHeader(type, keyShare), sharedSecret };
}
/**
* The MAC a response carried.
* @param {Response} res
* @param {'atproto-space-repo'|'atproto-space-record'} type
* @returns {Uint8Array}
*/
export function responseMac(res, type) {
const value = res.headers.get('atproto-space-mac');
if (!value) throw new Error('response carried no MAC');
return parseMacHeader(value, type);
}