UNPKG

signify-ts

Version:

Signing at the edge for KERI, ACDC, and KERIA

582 lines (525 loc) 15.6 kB
import { b, concat, Dict, Ident, Ilks, Serials, versify, Version, Versionage, } from './core'; import { Tholder } from './tholder'; import { CesrNumber } from './number'; import { Prefixer } from './prefixer'; import { Serder } from './serder'; import { MtrDex, NonTransDex } from './matter'; import { Saider } from './saider'; import { Siger } from './siger'; import { Cigar } from './cigar'; import { Counter, CtrDex } from './counter'; import { Seqner } from './seqner'; const MaxIntThold = 2 ** 32 - 1; export interface RotateArgs { pre?: string; keys: Array<string>; dig?: string; ilk?: string; sn?: number; isith?: number | string | Array<string>; ndigs?: Array<string>; nsith?: number | string | Array<string>; toad?: number; wits?: Array<string>; cuts?: Array<string>; adds?: Array<string>; cnfg?: Array<string>; data?: Array<object>; version?: Version; kind?: Serials; size?: number; intive?: boolean; } export function rotate({ pre = undefined, keys, dig = undefined, ilk = Ilks.rot, sn = 1, isith = undefined, ndigs = undefined, nsith = undefined, wits = undefined, cuts = undefined, adds = undefined, toad = undefined, data = undefined, version = undefined, kind = undefined, intive = true, }: RotateArgs) { const vs = versify(Ident.KERI, version, kind, 0); const _ilk = ilk; if (_ilk != Ilks.rot && _ilk != Ilks.drt) { throw new Error(`Invalid ilk = ${ilk} for rot or drt.`); } const sner = new CesrNumber({}, sn); if (sner.num < 1) { throw new Error(`Invalid sn = 0x${sner.numh} for rot or drt.`); } let _isit: number; if (isith == undefined) { _isit = Math.max(1, Math.ceil(keys.length / 2)); } else { _isit = isith as number; } const tholder = new Tholder({ sith: _isit }); if (tholder.num != undefined && tholder.num < 1) { throw new Error(`Invalid sith = ${tholder.num} less than 1.`); } if (tholder.size > keys.length) { throw new Error(`Invalid sith = ${tholder.num} for keys = ${keys}`); } let _ndigs: Array<string>; if (ndigs === undefined) { _ndigs = []; } else { _ndigs = ndigs; } let _nsith; if (nsith === undefined) { _nsith = Math.max(1, Math.ceil(_ndigs.length / 2)); } else { _nsith = nsith; } const ntholder = new Tholder({ sith: _nsith }); if (ntholder.num != undefined && ntholder.num < 1) { throw new Error(`Invalid sith = ${ntholder.num} less than 1.`); } if (ntholder.size > _ndigs.length) { throw new Error(`Invalid sith = ${ntholder.num} for ndigs = ${ndigs}`); } let _wits: Array<string>; if (wits === undefined) { _wits = []; } else { _wits = wits; } const witset = new Set(_wits); if (witset.size != _wits.length) { throw new Error(`Invalid wits = ${wits}, has duplicates.`); } let _cuts: Array<string>; if (cuts === undefined) { _cuts = []; } else { _cuts = cuts; } const cutset = new Set(_cuts); if (cutset.size != _cuts.length) { throw new Error(`Invalid cuts = ${cuts}, has duplicates.`); } let _adds: Array<string>; if (adds === undefined) { _adds = []; } else { _adds = adds; } const addset = new Set(_adds); //non empty intersection of witset and addset const witaddset = new Set([...witset].filter((x) => addset.has(x))); if (witaddset.size > 0) { throw new Error( `Invalid member combination among wits = ${wits}, and adds = ${adds}.` ); } // non empty intersection of cutset and addset const cutaddset = new Set([...cutset].filter((x) => addset.has(x))); if (cutaddset.size > 0) { throw new Error( `Invalid member combination among cuts = ${cuts}, and adds = ${adds}.` ); } const newitsetdiff = new Set(_wits); _cuts.forEach(function (v) { newitsetdiff.delete(v); }); const newitset = new Set( (function* () { yield* newitsetdiff; yield* addset; })() ); if (newitset.size != witset.size - cutset.size + addset.size) { throw new Error( `Invalid member combination among wits = ${wits}, cuts = ${cuts}, and adds = ${adds}.` ); } let _toad: number; if (toad === undefined) { if (newitset.size == 0) { _toad = 0; } else { _toad = ample(newitset.size); } } else { _toad = toad; } if (newitset.size > 0) { if (_toad < 1 || _toad > newitset.size) { throw new Error(`Invalid toad = ${_toad} for wit = ${wits}`); } } else { if (_toad != 0) { throw new Error(`Invalid toad = ${_toad} for wit = ${wits}`); } } const _ked = { v: vs, t: _ilk, d: '', i: pre, s: sner.numh, p: dig, kt: tholder.num && intive && tholder.num !== undefined && tholder.num <= MaxIntThold ? tholder.num.toString(16) : tholder.sith, k: keys, nt: ntholder.num && intive && ntholder.num !== undefined && ntholder.num <= MaxIntThold ? ntholder.num.toString(16) : ntholder.sith, n: _ndigs, bt: _toad && intive && _toad !== undefined && _toad <= MaxIntThold ? _toad : _toad.toString(16), br: cuts, ba: adds, a: data != undefined ? data : [], }; const [, ked] = Saider.saidify(_ked); return new Serder(ked); } export function ample(n: number, f?: number, weak = true) { n = Math.max(0, n); // no negatives let f1; if (f == undefined) { f1 = Math.max(1, Math.floor(Math.max(0, n - 1) / 3)); // least floor f subject to n >= 3*f+1 const f2 = Math.max(1, Math.ceil(Math.max(0, n - 1) / 3)); // most Math.ceil f subject to n >= 3*f+1 if (weak) { // try both fs to see which one has lowest m return Math.min( n, Math.ceil((n + f1 + 1) / 2), Math.ceil((n + f2 + 1) / 2) ); } else { return Math.min( n, Math.max(0, n - f1, Math.ceil((n + f1 + 1) / 2)) ); } } else { f = Math.max(0, f); const m1 = Math.ceil((n + f + 1) / 2); const m2 = Math.max(0, n - f); if (m2 < m1 && n > 0) { throw new Error(`Invalid f=${f} is too big for n=${n}.`); } if (weak) { return Math.min(n, m1, m2); } else { return Math.min(n, Math.max(m1, m2)); } } } export interface InceptArgs { keys: Array<string>; isith?: number | string | Array<string>; ndigs?: Array<string>; nsith?: number | string | Array<string>; toad?: number | string; wits?: Array<string>; cnfg?: Array<string>; data?: Array<object>; version?: Version; kind?: Serials; code?: string; intive?: boolean; delpre?: string; } export function incept({ keys, isith, ndigs, nsith, toad, wits, cnfg, data, version = Versionage, kind = Serials.JSON, code, intive = false, delpre, }: InceptArgs) { const vs = versify(Ident.KERI, version, kind, 0); const ilk = delpre == undefined ? Ilks.icp : Ilks.dip; const sner = new CesrNumber({}, 0); if (isith == undefined) { isith = Math.max(1, Math.ceil(keys.length / 2)); } const tholder = new Tholder({ sith: isith }); if (tholder.num != undefined && tholder.num < 1) { throw new Error(`Invalid sith = ${tholder.num} less than 1.`); } if (tholder.size > keys.length) { throw new Error(`Invalid sith = ${tholder.num} for keys ${keys}`); } if (ndigs == undefined) { ndigs = new Array<string>(); } if (nsith == undefined) { nsith = Math.max(0, Math.ceil(ndigs.length / 2)); } const ntholder = new Tholder({ sith: nsith }); if (ntholder.num != undefined && ntholder.num < 0) { throw new Error(`Invalid nsith = ${ntholder.num} less than 0.`); } if (ntholder.size > keys.length) { throw new Error(`Invalid nsith = ${ntholder.num} for keys ${ndigs}`); } wits = wits == undefined ? [] : wits; if (new Set(wits).size != wits.length) { throw new Error(`Invalid wits = ${wits}, has duplicates.`); } if (toad == undefined) { if (wits.length == 0) { toad = 0; } else { toad = ample(wits.length); } } const toader = new CesrNumber({}, toad); if (wits.length > 0) { if (toader.num < 1 || toader.num > wits.length) { throw new Error(`Invalid toad = ${toader.num} for wits = ${wits}`); } } else { if (toader.num != 0) { throw new Error(`Invalid toad = ${toader.num} for wits = ${wits}`); } } cnfg = cnfg == undefined ? new Array<string>() : cnfg; data = data == undefined ? new Array<object>() : data; let ked = { v: vs, t: ilk, d: '', i: '', s: sner.numh, kt: intive && tholder.num != undefined ? tholder.num : tholder.sith, k: keys, nt: intive && tholder.num != undefined ? ntholder.num : ntholder.sith, n: ndigs, bt: intive ? toader.num : toader.numh, b: wits, c: cnfg, a: data, } as Dict<any>; if (delpre != undefined) { ked['di'] = delpre; if (code == undefined) { code = MtrDex.Blake3_256; } } let prefixer; if (delpre == undefined && code == undefined && keys.length == 1) { prefixer = new Prefixer({ qb64: keys[0] }); if (prefixer.digestive) { throw new Error( `Invalid code, digestive=${prefixer.code}, must be derived from ked.` ); } } else { prefixer = new Prefixer({ code: code }, ked); if (delpre != undefined) { if (!prefixer.digestive) { throw new Error( `Invalid derivation code = ${prefixer.code} for delegation. Must be digestive` ); } } } ked['i'] = prefixer.qb64; if (prefixer.digestive) { ked['d'] = prefixer.qb64; } else { [, ked] = Saider.saidify(ked); } return new Serder(ked); } export function messagize( serder: Serder, sigers?: Array<Siger>, seal?: any, wigers?: Array<Cigar>, cigars?: Array<Cigar>, pipelined: boolean = false ): Uint8Array { let msg = new Uint8Array(b(serder.raw)); let atc = new Uint8Array(); if (sigers == undefined && wigers == undefined && cigars == undefined) { throw new Error( `Missing attached signatures on message = ${serder.ked}.` ); } if (sigers != undefined) { if (seal != undefined) { if (seal[0] == 'SealEvent') { atc = concat( atc, new Counter({ code: CtrDex.TransIdxSigGroups, count: 1 }) .qb64b ); atc = concat(atc, new TextEncoder().encode(seal[1].i)); atc = concat( atc, new Seqner({ sn: parseInt(seal[1].s) }).qb64b ); atc = concat(atc, new TextEncoder().encode(seal[1].d)); } else if (seal[0] == 'SealLast') { atc = concat( atc, new Counter({ code: CtrDex.TransLastIdxSigGroups, count: 1, }).qb64b ); atc = concat(atc, new TextEncoder().encode(seal[1].i)); } } atc = concat( atc, new Counter({ code: CtrDex.ControllerIdxSigs, count: sigers.length, }).qb64b ); sigers.forEach((siger) => { atc = concat(atc, siger.qb64b); }); } if (wigers != undefined) { atc = concat( atc, new Counter({ code: CtrDex.ControllerIdxSigs, count: wigers.length, }).qb64b ); wigers.forEach((wiger) => { if (wiger.verfer && !(wiger.verfer.code in NonTransDex)) { throw new Error( `Attempt to use tranferable prefix=${wiger.verfer.qb64} for receipt.` ); } atc = concat(atc, wiger.qb64b); }); } if (cigars != undefined) { atc = concat( atc, new Counter({ code: CtrDex.ControllerIdxSigs, count: cigars.length, }).qb64b ); cigars.forEach((cigar) => { if (cigar.verfer && !(cigar.verfer.code in NonTransDex)) { throw new Error( `Attempt to use tranferable prefix=${cigar.verfer.qb64} for receipt.` ); } atc = concat(atc, cigar.qb64b); }); } if (pipelined) { if (atc.length % 4 != 0) { throw new Error( `Invalid attachments size=${atc.length}, nonintegral quadlets.` ); } msg = concat( msg, new Counter({ code: CtrDex.AttachedMaterialQuadlets, count: Math.floor(atc.length / 4), }).qb64b ); } msg = concat(msg, atc); return msg; } interface InteractArgs { pre: string; dig: string; sn: number; data: Array<any>; version: Version | undefined; kind: Serials | undefined; } export function interact(args: InteractArgs): Serder { let { pre, dig, sn, data, version, kind } = args; const vs = versify(Ident.KERI, version, kind, 0); const ilk = Ilks.ixn; const sner = new CesrNumber({}, sn); if (sner.num < 1) { throw new Error(`Invalid sn = 0x${sner.numh} for ixn.`); } data = data == undefined ? new Array<any>() : data; let ked = { v: vs, t: ilk, d: '', i: pre, s: sner.numh, p: dig, a: data, } as Dict<any>; [, ked] = Saider.saidify(ked); return new Serder(ked); } export function reply( route: string = '', data: any | undefined, stamp: string | undefined, version: Version | undefined, kind: Serials = Serials.JSON ) { const vs = versify(Ident.KERI, version, kind, 0); if (data == undefined) { data = {}; } const _sad = { v: vs, t: Ilks.rpy, d: '', dt: stamp ?? new Date().toISOString().replace('Z', '000+00:00'), r: route, a: data, }; const [, sad] = Saider.saidify(_sad); const saider = new Saider({ qb64: sad['d'] }); if (!saider.verify(sad, true, true, kind, 'd')) throw new Error(`Invalid said = ${saider.qb64} for reply msg=${sad}.`); return new Serder(sad); }