UNPKG

signify-ts

Version:

Signing at the edge for KERI, ACDC, and KERIA

408 lines (407 loc) 16.4 kB
import { EmptyMaterialError } from "./kering.js"; import { b, b64ToInt, d, intToB64, readInt } from "./core.js"; import { decodeBase64Url, encodeBase64Url } from "./base64.js"; export class IndexerCodex { constructor() { this.Ed25519_Sig = 'A'; // Ed25519 sig appears same in both lists if any. this.Ed25519_Crt_Sig = 'B'; // Ed25519 sig appears in current list only. this.ECDSA_256k1_Sig = 'C'; // ECDSA secp256k1 sig appears same in both lists if any. this.ECDSA_256k1_Crt_Sig = 'D'; // ECDSA secp256k1 sig appears in current list. this.ECDSA_256r1_Sig = 'E'; // ECDSA secp256r1 sig appears same in both lists if any. this.ECDSA_256r1_Crt_Sig = 'F'; // ECDSA secp256r1 sig appears in current list. this.Ed448_Sig = '0A'; // Ed448 signature appears in both lists. this.Ed448_Crt_Sig = '0B'; // Ed448 signature appears in current list only. this.Ed25519_Big_Sig = '2A'; // Ed25519 sig appears in both lists. this.Ed25519_Big_Crt_Sig = '2B'; // Ed25519 sig appears in current list only. this.ECDSA_256k1_Big_Sig = '2C'; // ECDSA secp256k1 sig appears in both lists. this.ECDSA_256k1_Big_Crt_Sig = '2D'; // ECDSA secp256k1 sig appears in current list only. this.ECDSA_256r1_Big_Sig = '2E'; // ECDSA secp256r1 sig appears in both lists. this.ECDSA_256r1_Big_Crt_Sig = '2F'; // ECDSA secp256r1 sig appears in current list only. this.Ed448_Big_Sig = '3A'; // Ed448 signature appears in both lists. this.Ed448_Big_Crt_Sig = '3B'; // Ed448 signature appears in current list only. } } export const IdrDex = new IndexerCodex(); export class IndexedSigCodex { constructor() { this.Ed25519_Sig = 'A'; // Ed25519 sig appears same in both lists if any. this.Ed25519_Crt_Sig = 'B'; // Ed25519 sig appears in current list only. this.ECDSA_256k1_Sig = 'C'; // ECDSA secp256k1 sig appears same in both lists if any. this.ECDSA_256k1_Crt_Sig = 'D'; // ECDSA secp256k1 sig appears in current list. this.ECDSA_256r1_Sig = 'E'; // ECDSA secp256r1 sig appears same in both lists if any. this.ECDSA_256r1_Crt_Sig = 'F'; // ECDSA secp256r1 sig appears in current list. this.Ed448_Sig = '0A'; // Ed448 signature appears in both lists. this.Ed448_Crt_Sig = '0B'; // Ed448 signature appears in current list only. this.Ed25519_Big_Sig = '2A'; // Ed25519 sig appears in both lists. this.Ed25519_Big_Crt_Sig = '2B'; // Ed25519 sig appears in current list only. this.ECDSA_256k1_Big_Sig = '2C'; // ECDSA secp256k1 sig appears in both lists. this.ECDSA_256k1_Big_Crt_Sig = '2D'; // ECDSA secp256k1 sig appears in current list only. this.ECDSA_256r1_Big_Sig = '2E'; // ECDSA secp256r1 sig appears in both lists. this.ECDSA_256r1_Big_Crt_Sig = '2F'; // ECDSA secp256r1 sig appears in current list only. this.Ed448_Big_Sig = '3A'; // Ed448 signature appears in both lists. this.Ed448_Big_Crt_Sig = '3B'; // Ed448 signature appears in current list only. } has(prop) { const m = new Map(Array.from(Object.entries(this), (v) => [v[1], v[0]])); return m.has(prop); } } export const IdxSigDex = new IndexedSigCodex(); export class IndexedCurrentSigCodex { constructor() { this.Ed25519_Crt_Sig = 'B'; // Ed25519 sig appears in current list only. this.ECDSA_256k1_Crt_Sig = 'D'; // ECDSA secp256k1 sig appears in current list only. this.ECDSA_256r1_Crt_Sig = 'F'; // ECDSA secp256r1 sig appears in current list. this.Ed448_Crt_Sig = '0B'; // Ed448 signature appears in current list only. this.Ed25519_Big_Crt_Sig = '2B'; // Ed25519 sig appears in current list only. this.ECDSA_256k1_Big_Crt_Sig = '2D'; // ECDSA secp256k1 sig appears in current list only. this.ECDSA_256r1_Big_Crt_Sig = '2F'; // ECDSA secp256r1 sig appears in current list only. this.Ed448_Big_Crt_Sig = '3B'; // Ed448 signature appears in current list only. } has(prop) { const m = new Map(Array.from(Object.entries(this), (v) => [v[1], v[0]])); return m.has(prop); } } export const IdxCrtSigDex = new IndexedCurrentSigCodex(); export class IndexedBothSigCodex { constructor() { this.Ed25519_Sig = 'A'; // Ed25519 sig appears same in both lists if any. this.ECDSA_256k1_Sig = 'C'; // ECDSA secp256k1 sig appears same in both lists if any. this.Ed448_Sig = '0A'; // Ed448 signature appears in both lists. this.Ed25519_Big_Sig = '2A'; // Ed25519 sig appears in both listsy. this.ECDSA_256k1_Big_Sig = '2C'; // ECDSA secp256k1 sig appears in both lists. this.Ed448_Big_Sig = '3A'; // Ed448 signature appears in both lists. } has(prop) { const m = new Map(Array.from(Object.entries(this), (v) => [v[1], v[0]])); return m.has(prop); } } export const IdxBthSigDex = new IndexedBothSigCodex(); export class Xizage { constructor(hs, ss, os, fs, ls) { this.hs = hs; this.ss = ss; this.os = os; this.fs = fs; this.ls = ls; } } export class Indexer { constructor({ raw = undefined, code = IdrDex.Ed25519_Sig, index = 0, ondex = undefined, qb64b = undefined, qb64 = undefined, qb2 = undefined, }) { this.Codex = IdrDex; this._code = ''; this._index = -1; this._raw = new Uint8Array(0); if (raw != undefined) { if (code == undefined) { throw new EmptyMaterialError(`Improper initialization need either (raw and code) or qb64b or qb64 or qb2.`); } if (!Indexer.Sizes.has(code)) { throw new Error(`Unsupported code=${code}.`); } const xizage = Indexer.Sizes.get(code); const os = xizage.os; const fs = xizage.fs; const cs = xizage.hs + xizage.ss; const ms = xizage.ss - xizage.os; if (!Number.isInteger(index) || index < 0 || index > 64 ** ms - 1) { throw new Error(`Invalid index=${index} for code=${code}.`); } if (ondex != undefined && xizage.os != 0 && !(ondex >= 0 && ondex <= 64 ** os - 1)) { throw new Error(`Invalid ondex=${ondex} for code=${code}.`); } if (IdxCrtSigDex.has(code) && ondex != undefined) { throw new Error(`Non None ondex=${ondex} for code=${code}.`); } if (IdxBthSigDex.has(code)) { if (ondex == undefined) { ondex = index; } else { if (ondex != index && os == 0) { throw new Error(`Non matching ondex=${ondex} and index=${index} for code=${code}.`); } } } if (fs == undefined) { throw new Error('variable length unsupported'); } // TODO: Don't support this code // if not fs: # compute fs from index // if cs % 4: // raise InvalidCodeSizeError(f"Whole code size not multiple of 4 for " // f"variable length material. cs={cs}.") // if os != 0: // raise InvalidCodeSizeError(f"Non-zero other index size for " // f"variable length material. os={os}.") // fs = (index * 4) + cs const rawsize = Math.floor(((fs - cs) * 3) / 4); raw = raw.slice(0, rawsize); if (raw.length != rawsize) { throw new Error(`Not enougth raw bytes for code=${code} and index=${index} ,expected ${rawsize} got ${raw.length}.`); } this._code = code; this._index = index; this._ondex = ondex; this._raw = raw; } else if (qb64b != undefined) { const qb64 = d(qb64b); this._exfil(qb64); } else if (qb64 != undefined) { this._exfil(qb64); } else if (qb2 != undefined) { this._bexfil(qb2); } else { throw new EmptyMaterialError(`Improper initialization need either (raw and code and index) or qb64b or qb64 or qb2.`); } } _bexfil(qb2) { throw new Error(`qb2 not yet supported: ${qb2}`); } static _rawSize(code) { const xizage = Indexer.Sizes.get(code); return Math.floor(xizage.fs - ((xizage.hs + xizage.ss) * 3) / 4); } get code() { return this._code; } get raw() { return this._raw; } get index() { return this._index; } get ondex() { return this._ondex; } get qb64() { return this._infil(); } get qb64b() { return b(this.qb64); } _infil() { const code = this.code; const index = this.index; const ondex = this.ondex; const raw = this.raw; const ps = (3 - (raw.length % 3)) % 3; const xizage = Indexer.Sizes.get(code); const cs = xizage.hs + xizage.ss; const ms = xizage.ss - xizage.os; // TODO: don't support this code // if not fs: # compute fs from index // if cs % 4: // raise InvalidCodeSizeError(f"Whole code size not multiple of 4 for " // f"variable length material. cs={cs}.") // if os != 0: // raise InvalidCodeSizeError(f"Non-zero other index size for " // f"variable length material. os={os}.") // fs = (index * 4) + cs if (index < 0 || index > 64 ** ms - 1) { throw new Error(`Invalid index=${index} for code=${code}.`); } if (ondex != undefined && xizage.os != 0 && !(ondex >= 0 && ondex <= 64 ** xizage.os - 1)) { throw new Error(`Invalid ondex=${ondex} for os=${xizage.os} and code=${code}.`); } const both = `${code}${intToB64(index, ms)}${intToB64(ondex == undefined ? 0 : ondex, xizage.os)}`; if (both.length != cs) { throw new Error(`Mismatch code size = ${cs} with table = ${both.length}.`); } if (cs % 4 != ps - xizage.ls) { throw new Error(`Invalid code=${both} for converted raw pad size=${ps}.`); } const bytes = new Uint8Array(ps + raw.length); for (let i = 0; i < ps; i++) { bytes[i] = 0; } for (let i = 0; i < raw.length; i++) { const odx = i + ps; bytes[odx] = raw[i]; } const full = both + encodeBase64Url(bytes).slice(ps - xizage.ls); if (full.length != xizage.fs) { throw new Error(`Invalid code=${both} for raw size=${raw.length}.`); } return full; } _exfil(qb64) { if (qb64.length == 0) { throw new Error('Empty Material'); } const first = qb64[0]; if (!Array.from(Indexer.Hards.keys()).includes(first)) { throw new Error(`Unexpected code ${first}`); } const hs = Indexer.Hards.get(first); if (qb64.length < hs) { throw new Error(`Need ${hs - qb64.length} more characters.`); } const hard = qb64.slice(0, hs); if (!Array.from(Indexer.Sizes.keys()).includes(hard)) { throw new Error(`Unsupported code ${hard}`); } const xizage = Indexer.Sizes.get(hard); const cs = xizage.hs + xizage.ss; // both hard + soft code size const ms = xizage.ss - xizage.os; if (qb64.length < cs) { throw new Error(`Need ${cs - qb64.length} more characters.`); } const sindex = qb64.slice(hs, hs + ms); const index = b64ToInt(sindex); const sondex = qb64.slice(hs + ms, hs + ms + xizage.os); let ondex; if (IdxCrtSigDex.has(hard)) { ondex = xizage.os != 0 ? b64ToInt(sondex) : undefined; if (ondex != 0 && ondex != undefined) { throw new Error(`Invalid ondex=${ondex} for code=${hard}.`); } else { ondex = undefined; } } else { ondex = xizage.os != 0 ? b64ToInt(sondex) : index; } if (xizage.fs == undefined) { throw new Error('variable length not supported'); } // TODO: support variable length // if not fs: # compute fs from index which means variable length // if cs % 4: // raise ValidationError(f"Whole code size not multiple of 4 for " // f"variable length material. cs={cs}.") // if os != 0: // raise ValidationError(f"Non-zero other index size for " // f"variable length material. os={os}.") // fs = (index * 4) + cs if (qb64.length < xizage.fs) { throw new Error(`Need ${xizage.fs - qb64.length} more chars.`); } qb64 = qb64.slice(0, xizage.fs); const ps = cs % 4; const pbs = 2 * ps != 0 ? ps : xizage.ls; let raw; if (ps != 0) { const base = new Array(ps + 1).join('A') + qb64.slice(cs); const paw = decodeBase64Url(base); // decode base to leave prepadded raw const pi = readInt(paw.slice(0, ps)); // prepad as int if (pi & (2 ** pbs - 1)) { // masked pad bits non-zero throw new Error(`Non zeroed prepad bits = {pi & (2 ** pbs - 1 ):<06b} in {qb64b[cs:cs+1]}.`); } raw = paw.slice(ps); // strip off ps prepad paw bytes } else { const base = qb64.slice(cs); const paw = decodeBase64Url(base); const li = readInt(paw.slice(0, xizage.ls)); if (li != 0) { if (li == 1) { throw new Error(`Non zeroed lead byte = 0x{li:02x}.`); } else { throw new Error(`Non zeroed lead bytes = 0x{li:04x}`); } } raw = paw.slice(xizage.ls); } if (raw.length != Math.floor(((qb64.length - cs) * 3) / 4)) { throw new Error(`Improperly qualified material = ${qb64}`); } this._code = hard; this._index = index; this._ondex = ondex; this._raw = new Uint8Array(raw); // must be bytes for crpto opts and immutable not bytearray } } Indexer.Hards = new Map([ ['A', 1], ['B', 1], ['C', 1], ['D', 1], ['E', 1], ['F', 1], ['G', 1], ['H', 1], ['I', 1], ['J', 1], ['K', 1], ['L', 1], ['M', 1], ['N', 1], ['O', 1], ['P', 1], ['Q', 1], ['R', 1], ['S', 1], ['T', 1], ['U', 1], ['V', 1], ['W', 1], ['X', 1], ['Y', 1], ['Z', 1], ['a', 1], ['b', 1], ['c', 1], ['d', 1], ['e', 1], ['f', 1], ['g', 1], ['h', 1], ['i', 1], ['j', 1], ['k', 1], ['l', 1], ['m', 1], ['n', 1], ['o', 1], ['p', 1], ['q', 1], ['r', 1], ['s', 1], ['t', 1], ['u', 1], ['v', 1], ['w', 1], ['x', 1], ['y', 1], ['z', 1], ['0', 2], ['1', 2], ['2', 2], ['3', 2], ['4', 2], ]); Indexer.Sizes = new Map(Object.entries({ A: new Xizage(1, 1, 0, 88, 0), B: new Xizage(1, 1, 0, 88, 0), C: new Xizage(1, 1, 0, 88, 0), D: new Xizage(1, 1, 0, 88, 0), E: new Xizage(1, 1, 0, 88, 0), F: new Xizage(1, 1, 0, 88, 0), '0A': new Xizage(2, 2, 1, 156, 0), '0B': new Xizage(2, 2, 1, 156, 0), '2A': new Xizage(2, 4, 2, 92, 0), '2B': new Xizage(2, 4, 2, 92, 0), '2C': new Xizage(2, 4, 2, 92, 0), '2D': new Xizage(2, 4, 2, 92, 0), '2E': new Xizage(2, 4, 2, 92, 0), '2F': new Xizage(2, 4, 2, 92, 0), '3A': new Xizage(2, 6, 3, 160, 0), '3B': new Xizage(2, 6, 3, 160, 0), '0z': new Xizage(2, 2, 0, undefined, 0), '1z': new Xizage(2, 2, 1, 76, 1), '4z': new Xizage(2, 6, 3, 80, 1), }));