mdip-agent-kit
Version:
Give any AI agent its own MDIP (did:mdip) identity on the SELF / Keychain hyperswarm network: keymaster wallet, DID, SELF Public Profile molecule, backups, challenges and delegated-authority credentials.
46 lines (42 loc) • 3.27 kB
JavaScript
// Second, independent-code check of the vectors using @mdip/cipher (what @mdip/keymaster and the
// gatekeeper use): recomputes JCS bytes from the JSON files, compares to the committed .bin files,
// and verifies each signature. Also checks the MDIP binding fields of the challenge fixture.
// Usage: node vectors/verify-with-kit.js (after npm install; no network)
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import CipherNode from '@mdip/cipher/node';
const dir = path.dirname(fileURLToPath(import.meta.url));
const c = new CipherNode();
const rd = f => fs.readFileSync(path.join(dir, f), 'utf8');
const js = f => JSON.parse(rd(f));
let fail = 0;
const check = (cond, msg) => { console.log(`${cond ? 'ok ' : 'FAIL'} ${msg}`); if (!cond) fail = 1; };
const unsigned = o => { const x = JSON.parse(JSON.stringify(o)); delete x.signature; return x; };
// kav-1
const signed = js('kav-1/signed.json');
const key = js('kav-1/test-key.json').publicJwk;
check(c.canonicalizeJSON(unsigned(signed)) === rd('kav-1/message.bin'), 'kav-1 message.bin == JCS(signed.json minus signature)');
check(c.hashJSON(unsigned(signed)) === signed.signature.hash, 'kav-1 signature.hash == sha256(JCS)');
check(c.verifySig(signed.signature.hash, signed.signature.value, key), 'kav-1 verifySig');
check(rd('kav-1/signature.hex').trim() === signed.signature.value, 'kav-1 signature.hex == signature.value');
// mdip-challenge-1
const dids = js('mdip-challenge-1/dids.json');
const op = k => js(`mdip-challenge-1/${k}.op.json`);
const keys = { [dids['verifier-agent']]: op('verifier-agent').publicJwk, [dids['responder-agent']]: op('responder-agent').publicJwk };
for (const k of ['verifier-agent', 'challenge', 'responder-agent', 'response']) {
const o = op(k);
check(c.canonicalizeJSON(unsigned(o)) === rd(`mdip-challenge-1/${k}.message.bin`), `${k} message.bin == JCS(op minus signature)`);
check(c.hashJSON(unsigned(o)) === o.signature.hash, `${k} signature.hash`);
// agent create ops are self-signed with their own publicJwk (no signer DID yet); assets by their controller
const signer = o.mdip.type === 'agent' ? (k === 'verifier-agent' ? dids['verifier-agent'] : dids['responder-agent']) : o.controller;
check(c.verifySig(o.signature.hash, o.signature.value, o.mdip.type === 'agent' ? o.publicJwk : keys[signer]), `${k} signed by ${signer === dids['verifier-agent'] ? 'verifier' : 'responder'} key`);
}
const ch = op('challenge'), re = op('response'), vr = js('mdip-challenge-1/verify-response.json');
check(ch.controller === dids['verifier-agent'], 'challenge controller == verifier DID (binds verifier)');
check(re.controller === dids['responder-agent'] && re.data.encrypted.sender === re.controller, 'response controller == encrypted.sender == responder DID');
check(Date.parse(re.mdip.validUntil) <= Date.parse(ch.mdip.validUntil), 'response validUntil <= challenge validUntil (expiry)');
check(vr.challenge === dids.challenge && vr.responder === dids['responder-agent'] && vr.match === true && !!vr.responseNonce, 'decrypted response names the challenge DID, responder, match=true, has responseNonce');
console.log(fail ? 'VECTORS FAILED' : 'ALL VECTORS OK');
process.exit(fail);