// 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); }