import { Address } from './address.js'; import { binToHex, hexToBin, isHex, secp256k1, sha256, ripemd160, utf8ToBin, } from '@bitauth/libauth'; /** * Public Key Entity. */ export class PublicKey { // The raw bytes representing the Public Key. protected readonly bytes: Uint8Array; /** * Construct a new Public Key. * * @param bytes The raw bytes (33) for the Public Key. */ protected constructor(bytes: Uint8Array) { this.bytes = bytes; } /** * Instantiates a Public Key from either Public Key Hex or Public Key Bytes. * * @param publicKey The public key in either hex or bytes. * * @throws {Error} If the public key cannot be decoded. * * @returns The created Public Key Primitive. */ public static from(publicKey: Uint8Array | string) { return publicKey instanceof Uint8Array ? this.fromBytes(publicKey) : this.fromHex(publicKey); } /** * Create a Public Key entity from the given hexadecimal string. * * @param publicKeyBytes Uint8Array containing the Public Key bytes. * * @throws {Error} If Public Key cannot be created. * * @returns The created Public Key entity. */ public static fromBytes(publicKeyBytes: Uint8Array): PublicKey { // Ensure that exactly 33 Bytes are given. if (!secp256k1.validatePublicKey(publicKeyBytes)) { throw new Error(`Invalid Public Key.`); } return new this(publicKeyBytes); } /** * Create a Public Key entity from the given hexadecimal string. * * @param publicKeyHex Hexadecimal encoding of the Public Key bytes. * * @throws {Error} If Public Key cannot be created. * * @returns The created Public Key entity. */ public static fromHex(publicKeyHex: string): PublicKey { // Ensure that the given string is hex. if (!isHex(publicKeyHex)) { throw new Error( `The given string (${publicKeyHex}) is not valid hexadecimal.`, ); } // Convert the hex to binary (Uint8Array). const publicKey = hexToBin(publicKeyHex); // Create a Public Key Entity from the binary. return new this(publicKey); } /** * Creates an instance from serialized data. * * @remarks This method is UNSAFE and MUST only be used where input is already verified or trusted. * * @param bytes - Serialized Primitive Data * * @returns A new instance of this primitive. */ public static fromRaw(bytes: Uint8Array) { return new this(bytes); } /** * Encode the raw bytes of this Public Key to a hexadecimal representation. * * @returns The raw bytes Public Key encoded as Hexadecimal. */ public toHex(): string { return binToHex(this.bytes); } /** * Get the raw bytes of this Public Key. * * @returns The raw bytes of the Public Key. */ public toBytes(): Uint8Array { // Return the raw bytes for this Public Key. return this.bytes; } /** * Return the hexadecimal representation of the raw bytes of the Public Key. * * @returns The raw bytes of the Public Key as hexadecimal. */ public toString(): string { // Return the raw Public Key byes encoded as a hexadecimal string. return this.toHex(); } /** * Converts this primitive to a serializable representation. * * @returns Serialzied primitive data. */ toRaw() { return this.bytes; } /** * Derive the Hash160 for this Public Key. * * @returns The Hash160 of the Public Key. */ public deriveHash160(): Uint8Array { // SHA256 Hash the public key. const sha256Hash = sha256.hash(this.bytes); // RIPEMD160 the SHA256 hash. const ripemd160Hash = ripemd160.hash(sha256Hash); // And then return it. return ripemd160Hash; } /** * Derive the Hash160 as Hex for this Public Key. * * @returns The Hash160 of the Public Key as hex. */ public deriveHash160Hex(): string { const hash160Bytes = this.deriveHash160(); return binToHex(hash160Bytes); } /** * Derive the Address for this Public Key. * * @returns The Address for this Public Key. */ public deriveAddress(): Address { // Get the RIPEMD160 Hash of the Public Key. const ripemd160Hash = this.deriveHash160(); // Return an instance of address return Address.fromHash160(ripemd160Hash, 'P2PKH'); } public verifyMessageSchnorr( message: Uint8Array | string, signature: Uint8Array | string, ) { if (typeof message === 'string') { message = utf8ToBin(message); } const messageHash = sha256.hash(message); return this.verifyMessageHashSchnorr(messageHash, signature); } public verifyMessageDER( message: Uint8Array | string, signature: Uint8Array | string, ) { if (typeof message === 'string') { message = utf8ToBin(message); } const messageHash = sha256.hash(message); return this.verifyMessageHashDER(messageHash, signature); } /** * Verify that a compact message hash was signed with the corresponding Private Key. * * @param messageHash The hash of the message. * @param signature The signature of the message. * * @returns True if signature and message hash verifies, false otherwise.. */ public verifyMessageHashCompact( messageHash: Uint8Array, signature: Uint8Array, ): boolean { const validity = secp256k1.verifySignatureCompact( signature, this.bytes, messageHash, ); return validity; } /** * Verify that a DER message hash was signed with the corresponding Private Key. * * @param messageHash The hash of the message. * @param signature The signature of the message. * * @returns True if signature and message hash verifies, false otherwise.. */ public verifyMessageHashDER( messageHash: Uint8Array | string, signature: Uint8Array | string, ): boolean { if (typeof messageHash === 'string') { messageHash = hexToBin(messageHash); } if (typeof signature === 'string') { signature = hexToBin(signature); } const validity = secp256k1.verifySignatureDER( signature, this.bytes, messageHash, ); return validity; } /** * Verify that a Schnorr message hash was signed with the corresponding Private Key. * * @param messageHash The hash of the message. * @param signature The signature of the message. * * @returns True if signature and message hash verifies, false otherwise.. */ public verifyMessageHashSchnorr( messageHash: Uint8Array | string, signature: Uint8Array | string, ): boolean { if (typeof messageHash === 'string') { messageHash = hexToBin(messageHash); } if (typeof signature === 'string') { signature = hexToBin(signature); } const validity = secp256k1.verifySignatureSchnorr( signature, this.bytes, messageHash, ); return validity; } }