Files
xo-stack/packages/primitives/src/public-key.ts
T
2025-01-13 09:43:11 +00:00

282 lines
6.8 KiB
TypeScript

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.
public 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
? PublicKey.fromBytes(publicKey)
: PublicKey.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 PublicKey(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 PublicKey(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;
}
}