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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
56 lines · 1.8 KB Raw History
// 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);
}