UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

261 lines 10.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Point_js_1 = __importDefault(require("./Point.js")); const Curve_js_1 = __importDefault(require("./Curve.js")); const ECDSA_js_1 = require("./ECDSA.js"); const BigNumber_js_1 = __importDefault(require("./BigNumber.js")); const Hash_js_1 = require("./Hash.js"); const Signature_js_1 = __importDefault(require("./Signature.js")); const utils_js_1 = require("./utils.js"); /** * The PublicKey class extends the Point class. It is used in public-key cryptography to derive shared secret, verify message signatures, and encode the public key in the DER format. * The class comes with static methods to generate PublicKey instances from private keys or from strings. * * @extends {Point} * @see {@link Point} for more information on Point. */ class PublicKey extends Point_js_1.default { /** * Static factory method to derive a public key from a private key. * It multiplies the generator point 'g' on the elliptic curve by the private key. * * @static * @method fromPrivateKey * * @param key - The private key from which to derive the public key. * * @returns Returns the PublicKey derived from the given PrivateKey. * * @example * const myPrivKey = new PrivateKey(...) * const myPubKey = PublicKey.fromPrivateKey(myPrivKey) */ static fromPrivateKey(key) { const c = new Curve_js_1.default(); const p = c.g.mul(key); return new PublicKey(p.x, p.y); } /** * Static factory method to create a PublicKey instance from a string. * * @param str - A string representing a public key. * * @returns Returns the PublicKey created from the string. * * @example * const myPubKey = PublicKey.fromString("03....") */ static fromString(str) { const p = Point_js_1.default.fromString(str); return new PublicKey(p.x, p.y); } /** * Static factory method to create a PublicKey instance from a number array. * * @param bytes - A number array representing a public key. * * @returns Returns the PublicKey created from the number array. * * @example * const myPubKey = PublicKey.fromString("03....") */ static fromDER(bytes) { const p = Point_js_1.default.fromDER(bytes); return new PublicKey(p.x, p.y); } /** * @constructor * @param x - A point or the x-coordinate of the point. May be a number, a BigNumber, a string (which will be interpreted as hex), a number array, or null. If null, an "Infinity" point is constructed. * @param y - If x is not a point, the y-coordinate of the point, similar to x. * @param isRed - A boolean indicating if the point is a member of the field of integers modulo the k256 prime. Default is true. * * @example * new PublicKey(point1); * new PublicKey('abc123', 'def456'); */ constructor(x, y = null, isRed = true) { if (x instanceof Point_js_1.default) { super(x.getX(), x.getY()); } else { // Common gotcha: constructing PublicKey with a DER value when you should use .fromString() if (y === null && isRed && typeof x === 'string') { if (x.length === 66 || x.length === 130) { throw new Error('You are using the "new PublicKey()" constructor with a DER hex string. You need to use "PublicKey.fromString()" instead.'); } } super(x, y, isRed); } } /** * Derive a shared secret from a public key and a private key for use in symmetric encryption. * This method multiplies the public key (an instance of Point) with a private key. * * @param priv - The private key to use in deriving the shared secret. * * @returns Returns the Point representing the shared secret. * * @throws Will throw an error if the public key is not valid for ECDH secret derivation. * * @example * const myPrivKey = new PrivateKey(...) * const sharedSecret = myPubKey.deriveSharedSecret(myPrivKey) */ deriveSharedSecret(priv) { if (!this.validate()) { throw new Error('Public key not valid for ECDH secret derivation'); } return this.mul(priv); } /** * Verify a signature of a message using this public key. * * @param msg - The message to verify. It can be a string or an array of numbers. * @param sig - The Signature of the message that needs verification. * @param enc - The encoding of the message. It defaults to 'utf8'. * * @returns Returns true if the signature is verified successfully, otherwise false. * * @example * const myMessage = "Hello, world!" * const mySignature = new Signature(...) * const isVerified = myPubKey.verify(myMessage, mySignature) */ verify(msg, sig, enc) { const msgHash = new BigNumber_js_1.default((0, Hash_js_1.sha256)(msg, enc), 16); return (0, ECDSA_js_1.verify)(msgHash, sig, this); } /** * Encode the public key to DER (Distinguished Encoding Rules) format. * * @returns Returns the DER-encoded public key in number array or string. * * @param enc - The encoding of the DER string. undefined = number array, 'hex' = hex string. * * @example * const derPublicKey = myPubKey.toDER() */ toDER(enc) { if (enc === 'hex') return this.encode(true, enc); return this.encode(true); } /** * Hash sha256 and ripemd160 of the public key. * * @returns Returns the hash of the public key. * * @example * const publicKeyHash = pubkey.toHash() */ toHash(enc) { const pkh = (0, Hash_js_1.hash160)(this.encode(true)); if (enc === 'hex') { return (0, utils_js_1.toHex)(pkh); } return pkh; } /** * Base58Check encodes the hash of the public key with a prefix to indicate locking script type. * Defaults to P2PKH for mainnet, otherwise known as a "Bitcoin Address". * * @param prefix defaults to [0x00] for mainnet, set to [0x6f] for testnet or use the strings 'mainnet' or 'testnet' * * @returns Returns the address encoding associated with the hash of the public key. * * @example * const address = pubkey.toAddress() * const address = pubkey.toAddress('mainnet') * const testnetAddress = pubkey.toAddress([0x6f]) * const testnetAddress = pubkey.toAddress('testnet') */ toAddress(prefix = [0x00]) { if (typeof prefix === 'string') { if (prefix === 'testnet' || prefix === 'test') { prefix = [0x6f]; } else if (prefix === 'mainnet' || prefix === 'main') { prefix = [0x00]; } else { throw new Error(`Invalid prefix ${prefix}`); } } return (0, utils_js_1.toBase58Check)(this.toHash(), prefix); } /** * Derives a child key with BRC-42. * @param privateKey The private key of the other party * @param invoiceNumber The invoice number used to derive the child key * @param cacheSharedSecret Optional function to cache shared secrets * @param retrieveCachedSharedSecret Optional function to retrieve shared secrets from the cache * @returns The derived child key. */ deriveChild(privateKey, invoiceNumber, cacheSharedSecret, retrieveCachedSharedSecret) { let sharedSecret; if (typeof retrieveCachedSharedSecret === 'function') { const retrieved = retrieveCachedSharedSecret(privateKey, this); if (typeof retrieved !== 'undefined') { sharedSecret = retrieved; } else { sharedSecret = this.deriveSharedSecret(privateKey); if (typeof cacheSharedSecret === 'function') { cacheSharedSecret(privateKey, this, sharedSecret); } } } else { sharedSecret = this.deriveSharedSecret(privateKey); } const invoiceNumberBin = (0, utils_js_1.toArray)(invoiceNumber, 'utf8'); const hmac = (0, Hash_js_1.sha256hmac)(sharedSecret.encode(true), invoiceNumberBin); const curve = new Curve_js_1.default(); const point = curve.g.mul(new BigNumber_js_1.default(hmac)); const finalPoint = this.add(point); return new PublicKey(finalPoint.x, finalPoint.y); } /** * Takes an array of numbers or a string and returns a new PublicKey instance. * This method will throw an error if the Compact encoding is invalid. * If a string is provided, it is assumed to represent a hexadecimal sequence. * compactByte value 27-30 means uncompressed public key. * 31-34 means compressed public key. * The range represents the recovery param which can be 0,1,2,3. * * @static * @method fromMsgHashAndCompactSignature * @param msgHash - The message hash which was signed. * @param signature - The signature in compact format. * @param enc - The encoding of the signature string. * @returns A PublicKey instance derived from the message hash and compact signature. * @example * const publicKey = Signature.fromMsgHashAndCompactSignature(msgHash, 'IMOl2mVKfDgsSsHT4uIYBNN4e...', 'base64'); */ static fromMsgHashAndCompactSignature(msgHash, signature, enc) { const data = (0, utils_js_1.toArray)(signature, enc); if (data.length !== 65) { throw new Error('Invalid Compact Signature'); } const compactByte = data[0]; if (compactByte < 27 || compactByte >= 35) { throw new Error('Invalid Compact Byte'); } let r = data[0] - 27; // NOTE: We don't use uncompressed pubkeys in this library, // but whether the key is compressed is captured in the recovery param. // Code below is commented out for reference of how you could capture this. // let compressed = false if (r > 3) { // compressed = true r -= 4; } const s = new Signature_js_1.default(new BigNumber_js_1.default(data.slice(1, 33)), new BigNumber_js_1.default(data.slice(33, 65))); return s.RecoverPublicKey(r, msgHash); } } exports.default = PublicKey; //# sourceMappingURL=PublicKey.js.map