UNPKG

jinaga

Version:

Data management for web and mobile applications.

46 lines (40 loc) 1.87 kB
import { md, pki, util } from "node-forge"; import { canonicalizeFact } from "../fact/hash"; import { FactEnvelope, FactSignature } from "../storage"; import { Trace } from "../util/trace"; type PublicKeyCache = { [key: string]: pki.rsa.PublicKey }; export function verifyEnvelopes(envelopes: FactEnvelope[]): boolean { // Cache public keys to avoid parsing them multiple times const publicKeyCache: PublicKeyCache = {}; for (const envelope of envelopes) { for (const signature of envelope.signatures) { if (!publicKeyCache[signature.publicKey]) { publicKeyCache[signature.publicKey] = pki.publicKeyFromPem(signature.publicKey); } } } return envelopes.every(e => verifySignatures(e, publicKeyCache)); } function verifySignatures(envelope: FactEnvelope, publicKeyCache: PublicKeyCache): boolean { const canonicalString = canonicalizeFact(envelope.fact.fields, envelope.fact.predecessors); const encodedString = util.encodeUtf8(canonicalString); const digest = md.sha512.create().update(encodedString); const digestBytes = digest.digest().getBytes(); const hash = util.encode64(digestBytes); if (envelope.fact.hash !== hash) { Trace.error(`Hash does not match. "${envelope.fact.hash}" !== "${hash}"\nFact: ${canonicalString}`); return false; } return envelope.signatures.every(s => verifySignature(s, digestBytes, publicKeyCache)); } function verifySignature(signature: FactSignature, digestBytes: string, publicKeyCache: PublicKeyCache) { const publicKey = publicKeyCache[signature.publicKey]; const signatureBytes = util.decode64(signature.signature); try { return publicKey.verify(digestBytes, signatureBytes); } catch (e) { Trace.error(`Failed to verify signature. ${e}`); return false; } }