280 lines
6.9 KiB
TypeScript
280 lines
6.9 KiB
TypeScript
import { Bytes } from './bytes.js';
|
|
import { Address } from './address.js';
|
|
|
|
import { secp256k1, sha256, ripemd160, utf8ToBin } from '@bitauth/libauth';
|
|
|
|
/**
|
|
* Public Key Entity.
|
|
*
|
|
* @example
|
|
*
|
|
* ```ts
|
|
* // Import our primitives
|
|
* import { PublicKey } from '@xocash/primitives';
|
|
*
|
|
* // Create a Public Key from Hex.
|
|
* const publicKey = PublicKey.from('publicKeyHex');
|
|
*
|
|
* // Derive the address.
|
|
* const address = publicKey.deriveAddress();
|
|
*
|
|
* // Output as a CashAddr.
|
|
* console.log(address.toCashAddr());
|
|
* ```
|
|
*/
|
|
export class PublicKey {
|
|
// The raw bytes representing the Public Key.
|
|
protected readonly bytes: Bytes;
|
|
|
|
/**
|
|
* Construct a new Public Key.
|
|
*
|
|
* @param bytes The raw bytes (33) for the Public Key.
|
|
*/
|
|
protected constructor(bytes: Bytes | Uint8Array) {
|
|
this.bytes = Bytes.from(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: Bytes | 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: Bytes | 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 {
|
|
return new this(Bytes.fromHex(publicKeyHex));
|
|
}
|
|
|
|
/**
|
|
* 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: 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 Bytes.fromBytes(this.bytes).toHex();
|
|
}
|
|
|
|
/**
|
|
* Get the raw bytes of this Public Key.
|
|
*
|
|
* @returns The raw bytes of the Public Key.
|
|
*/
|
|
public toBytes(): Bytes {
|
|
// 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 Bytes.fromBytes(hash160Bytes).toHex();
|
|
}
|
|
|
|
/**
|
|
* 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.fromHash160Bytes(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 = Bytes.fromHex(messageHash);
|
|
}
|
|
|
|
if (typeof signature === 'string') {
|
|
signature = Bytes.fromHex(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 = Bytes.fromHex(messageHash);
|
|
}
|
|
|
|
if (typeof signature === 'string') {
|
|
signature = Bytes.fromHex(signature);
|
|
}
|
|
|
|
const validity = secp256k1.verifySignatureSchnorr(
|
|
signature,
|
|
this.bytes,
|
|
messageHash,
|
|
);
|
|
|
|
return validity;
|
|
}
|
|
}
|