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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
85 lines · 2.7 KB Raw History
import { compressPublicKey, sign } from '@pdsjs/core/crypto';
import { publicKeyToDidKey } from '@pdsjs/core/plc';
import { describe, expect, it } from 'vitest';
import { createVerifier, parseDidKey } from '../src/verifier.js';
import { vectors } from './helpers/vectors.js';

async function p256() {
  const keys = await crypto.subtle.generateKey(
    { name: 'ECDSA', namedCurve: 'P-256' },
    true,
    ['sign', 'verify'],
  );
  const raw = new Uint8Array(
    await crypto.subtle.exportKey('raw', keys.publicKey),
  );
  return {
    didKey: publicKeyToDidKey(compressPublicKey(raw), 'p256'),
    /** @param {Uint8Array} data */
    sign: (data) => sign(keys.privateKey, data),
  };
}

describe('parseDidKey', () => {
  it('recognises the P-256 multicodec', () => {
    const parsed = parseDidKey(vectors.kem.authorDidKey);
    expect(parsed.curve).toBe('p256');
    expect(parsed.publicKey.length).toBe(33);
  });

  it('rejects a non-did:key string', () => {
    expect(() => parseDidKey('did:plc:aaaa')).toThrow(/did:key/);
  });
});

describe('createVerifier', () => {
  it('verifies a P-256 signature', async () => {
    const key = await p256();
    const data = new Uint8Array([1, 2, 3]);
    expect(
      await createVerifier().verify(key.didKey, data, await key.sign(data)),
    ).toBe(true);
  });

  it('rejects a tampered signature or data', async () => {
    const key = await p256();
    const data = new Uint8Array([1, 2, 3]);
    const sig = await key.sign(data);
    const badSig = sig.slice();
    badSig[0] ^= 0xff;
    expect(await createVerifier().verify(key.didKey, data, badSig)).toBe(false);
    expect(
      await createVerifier().verify(key.didKey, new Uint8Array([1, 2, 4]), sig),
    ).toBe(false);
  });

  it('rejects secp256k1 when no verifier is injected', async () => {
    const verifier = createVerifier();
    // did:key with the secp256k1 multicodec, the well-known example from the
    // did:key spec (https://w3c-ccg.github.io/did-method-key/#secp256k1).
    await expect(
      verifier.verify(
        'zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme',
        new Uint8Array(1),
        new Uint8Array(64),
      ),
    ).rejects.toThrow(/secp256k1/);
  });

  it('delegates secp256k1 to an injected verifier', async () => {
    /** @type {any} */
    let called = null;
    const verifier = createVerifier({
      secp256k1: async (publicKey, data, sig) => {
        called = { publicKey, data, sig };
        return true;
      },
    });
    const didKey = 'did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme';
    const data = new Uint8Array([1, 2, 3]);
    const sig = new Uint8Array(64);
    expect(await verifier.verify(didKey, data, sig)).toBe(true);
    expect(called.data).toEqual(data);
    expect(called.publicKey.length).toBe(33);
  });
});