80 lines
1.9 KiB
TypeScript
80 lines
1.9 KiB
TypeScript
import { bech32 } from "bech32"
|
|
import { ProtocolError } from "./error"
|
|
import * as secp from "@noble/secp256k1"
|
|
|
|
/**
|
|
* A 32-byte secp256k1 public key.
|
|
*/
|
|
export class PublicKey {
|
|
#hex: string
|
|
|
|
/**
|
|
* Expects the key encoded as an npub-prefixed bech32 string, hex string, or byte buffer.
|
|
*/
|
|
constructor(key: string | Uint8Array) {
|
|
this.#hex = parseKey(key, "npub1")
|
|
if (this.#hex.length !== 64) {
|
|
throw new ProtocolError(`invalid pubkey: ${key}`)
|
|
}
|
|
}
|
|
|
|
toString(): string {
|
|
return this.#hex
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A 32-byte secp256k1 private key.
|
|
*/
|
|
export class PrivateKey {
|
|
#hex: string
|
|
|
|
/**
|
|
* Expects the key encoded as an nsec-prefixed bech32 string, hex string, or byte buffer.
|
|
*/
|
|
constructor(key: string | Uint8Array) {
|
|
this.#hex = parseKey(key, "nsec1")
|
|
if (this.#hex.length !== 64) {
|
|
throw new ProtocolError(`invalid private key: ${this.#hex}`)
|
|
}
|
|
}
|
|
|
|
get pubkey(): PublicKey {
|
|
return new PublicKey(secp.schnorr.getPublicKey(this.#hex))
|
|
}
|
|
|
|
/**
|
|
* The hex representation of the private key. Use with caution!
|
|
*/
|
|
hexDangerous(): string {
|
|
return this.#hex
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a key into its hex representation.
|
|
*/
|
|
function parseKey(key: string | Uint8Array, bechPrefix: string): string {
|
|
if (typeof key === "string") {
|
|
// Is the key encoded in bech32?
|
|
if (key.startsWith(bechPrefix)) {
|
|
const { words } = bech32.decode(key)
|
|
const bytes = Uint8Array.from(bech32.fromWords(words))
|
|
return secp.utils.bytesToHex(bytes).toLowerCase()
|
|
}
|
|
// If not, it must be lowercase hex.
|
|
const valid = "0123456789abcdef"
|
|
if (key.length % 2 != 0) {
|
|
throw new ProtocolError(`invalid lowercase hex string: ${key}`)
|
|
}
|
|
for (const c of key) {
|
|
if (!valid.includes(c)) {
|
|
throw new ProtocolError(`invalid lowercase hex string: ${key}`)
|
|
}
|
|
}
|
|
return key
|
|
} else {
|
|
return secp.utils.bytesToHex(key).toLowerCase()
|
|
}
|
|
}
|