UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

182 lines 7.71 kB
import CachedKeyDeriver from './CachedKeyDeriver.js'; import { Hash, ECDSA, BigNumber, Signature, Schnorr, PublicKey, Point } from '../primitives/index.js'; /** * A ProtoWallet is precursor to a full wallet, capable of performing all foundational cryptographic operations. * It can derive keys, create signatures, facilitate encryption and HMAC operations, and reveal key linkages. * * However, ProtoWallet does not create transactions, manage outputs, interact with the blockchain, * enable the management of identity certificates, or store any data. It is also not concerned with privileged keys. */ export class ProtoWallet { keyDeriver; constructor(rootKeyOrKeyDeriver) { if (typeof rootKeyOrKeyDeriver.identityKey !== 'string') { rootKeyOrKeyDeriver = new CachedKeyDeriver(rootKeyOrKeyDeriver); } this.keyDeriver = rootKeyOrKeyDeriver; } async getPublicKey(args) { if (args.identityKey) { if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } return { publicKey: this.keyDeriver.rootKey.toPublicKey().toString() }; } else { if (args.protocolID == null || args.keyID == null || args.keyID === '') { throw new Error('protocolID and keyID are required if identityKey is false or undefined.'); } const keyDeriver = this.keyDeriver ?? (() => { throw new Error('keyDeriver is undefined'); })(); return { publicKey: keyDeriver .derivePublicKey(args.protocolID, args.keyID, args.counterparty ?? 'self', args.forSelf) .toString() }; } } async revealCounterpartyKeyLinkage(args) { const { publicKey: identityKey } = await this.getPublicKey({ identityKey: true }); if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } const linkage = this.keyDeriver.revealCounterpartySecret(args.counterparty); const linkageProof = new Schnorr().generateProof(this.keyDeriver.rootKey, this.keyDeriver.rootKey.toPublicKey(), PublicKey.fromString(args.counterparty), Point.fromDER(linkage)); const linkageProofBin = [ ...linkageProof.R.encode(true), ...linkageProof.SPrime.encode(true), ...linkageProof.z.toArray() ]; const revelationTime = new Date().toISOString(); const { ciphertext: encryptedLinkage } = await this.encrypt({ plaintext: linkage, protocolID: [2, 'counterparty linkage revelation'], keyID: revelationTime, counterparty: args.verifier }); const { ciphertext: encryptedLinkageProof } = await this.encrypt({ plaintext: linkageProofBin, protocolID: [2, 'counterparty linkage revelation'], keyID: revelationTime, counterparty: args.verifier }); return { prover: identityKey, verifier: args.verifier, counterparty: args.counterparty, revelationTime, encryptedLinkage, encryptedLinkageProof }; } async revealSpecificKeyLinkage(args) { const { publicKey: identityKey } = await this.getPublicKey({ identityKey: true }); if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } const linkage = this.keyDeriver.revealSpecificSecret(args.counterparty, args.protocolID, args.keyID); const { ciphertext: encryptedLinkage } = await this.encrypt({ plaintext: linkage, protocolID: [ 2, `specific linkage revelation ${args.protocolID[0]} ${args.protocolID[1]}` ], keyID: args.keyID, counterparty: args.verifier }); const { ciphertext: encryptedLinkageProof } = await this.encrypt({ plaintext: [0], protocolID: [ 2, `specific linkage revelation ${args.protocolID[0]} ${args.protocolID[1]}` ], keyID: args.keyID, counterparty: args.verifier }); return { prover: identityKey, verifier: args.verifier, counterparty: args.counterparty, protocolID: args.protocolID, keyID: args.keyID, encryptedLinkage, encryptedLinkageProof, proofType: 0 }; } async encrypt(args) { if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } const key = this.keyDeriver.deriveSymmetricKey(args.protocolID, args.keyID, args.counterparty ?? 'self'); return { ciphertext: key.encrypt(args.plaintext) }; } async decrypt(args, originator) { if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } const key = this.keyDeriver.deriveSymmetricKey(args.protocolID, args.keyID, args.counterparty ?? 'self'); return { plaintext: key.decrypt(args.ciphertext) }; } async createHmac(args) { if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } const key = this.keyDeriver.deriveSymmetricKey(args.protocolID, args.keyID, args.counterparty ?? 'self'); return { hmac: Hash.sha256hmac(key.toArray(), args.data) }; } async verifyHmac(args) { if (this.keyDeriver == null) { throw new Error('keyDeriver is undefined'); } const key = this.keyDeriver.deriveSymmetricKey(args.protocolID, args.keyID, args.counterparty ?? 'self'); const valid = Hash.sha256hmac(key.toArray(), args.data).toString() === args.hmac.toString(); if (!valid) { const e = new Error('HMAC is not valid'); e.code = 'ERR_INVALID_HMAC'; throw e; } return { valid }; } async createSignature(args) { if ((args.hashToDirectlySign == null) && (args.data == null)) { throw new Error('args.data or args.hashToDirectlySign must be valid'); } const hash = args.hashToDirectlySign ?? Hash.sha256(args.data ?? []); const keyDeriver = this.keyDeriver ?? (() => { throw new Error('keyDeriver is undefined'); })(); const key = keyDeriver.derivePrivateKey(args.protocolID, args.keyID, args.counterparty ?? 'anyone'); return { signature: ECDSA.sign(new BigNumber(hash), key, true).toDER() }; } async verifySignature(args) { if ((args.hashToDirectlyVerify == null) && (args.data == null)) { throw new Error('args.data or args.hashToDirectlyVerify must be valid'); } const hash = args.hashToDirectlyVerify ?? Hash.sha256(args.data ?? []); const keyDeriver = this.keyDeriver ?? (() => { throw new Error('keyDeriver is undefined'); })(); const key = keyDeriver.derivePublicKey(args.protocolID, args.keyID, args.counterparty ?? 'self', args.forSelf); const valid = ECDSA.verify(new BigNumber(hash), Signature.fromDER(args.signature), key); if (!valid) { const e = new Error('Signature is not valid'); e.code = 'ERR_INVALID_SIGNATURE'; throw e; } return { valid }; } } export default ProtoWallet; //# sourceMappingURL=ProtoWallet.js.map