]> git.codecow.com Git - libnemo.git/commitdiff
Reuse text encoder to avoid initializing on every call. Consolidate redundant assignm...
authorChris Duncan <chris@codecow.com>
Thu, 14 May 2026 20:32:57 +0000 (13:32 -0700)
committerChris Duncan <chris@codecow.com>
Thu, 14 May 2026 20:32:57 +0000 (13:32 -0700)
src/lib/crypto/bip44.ts

index 135bae75caed3df80beb49e5da76bd3c4b1dd484..922b7d106418af37cb8c87a9993a81a4f05ff58a 100644 (file)
@@ -3,6 +3,7 @@
 
 import { Point, getPublicKey as secp256k1_getPublicKey } from '@noble/secp256k1'
 
+type Bytes = Uint8Array<ArrayBuffer>
 type Curve = 'Bitcoin seed' | 'ed25519 seed'
 type ExtendedKey = {
        privateKey: ArrayBuffer
@@ -11,6 +12,7 @@ type ExtendedKey = {
 }
 const BIP44_PURPOSE: 44 = 44
 const HARDENED_OFFSET: 0x80000000 = 0x80000000
+const ENCODER: TextEncoder = new TextEncoder()
 
 /**
  * Derives a private child key for a coin by following the specified BIP-32 and
@@ -74,8 +76,8 @@ function ckd (curve: Curve, seed: ArrayBuffer, coin: number, account: number, ch
 }
 
 function slip10 (curve: string, S: ArrayBuffer): Promise<ExtendedKey> {
-       const key = new Uint8Array(new TextEncoder().encode(curve))
-       const data = new Uint8Array(S)
+       const key = ENCODER.encode(curve).buffer
+       const data = S
        return hmac(key, data)
                .then(I => {
                        const IL = I.slice(0, I.byteLength / 2)
@@ -89,18 +91,17 @@ function CKDpriv (curve: Curve, { privateKey, chainCode }: ExtendedKey, index?:
                return Promise.resolve({ privateKey, chainCode })
        }
        const pk = new Uint8Array(privateKey)
-       const key = new Uint8Array(chainCode)
-       const data = new Uint8Array(37)
+       const t = new Uint8Array(37)
        if (index >= HARDENED_OFFSET) {
-               data.set([0])
-               data.set(pk, 1)
-               data.set(ser32(index), 33)
+               t.set(pk, 1)
        } else if (curve === 'ed25519 seed') {
                throw new RangeError('Only hardened child keys are supported for ed25519')
        } else {
-               data.set(secp256k1_getPublicKey(pk))
-               data.set(ser32(index), 33)
+               t.set(secp256k1_getPublicKey(pk))
        }
+       t.set(ser32(index), 33)
+       const key = chainCode
+       const data = t.buffer
        return hmac(key, data)
                .then(I => {
                        const IL = I.slice(0, I.byteLength / 2)
@@ -122,13 +123,13 @@ function CKDpriv (curve: Curve, { privateKey, chainCode }: ExtendedKey, index?:
                })
 }
 
-function ser32 (integer: number): Uint8Array<ArrayBuffer> {
+function ser32 (integer: number): Bytes {
        const view = new DataView(new ArrayBuffer(4))
        view.setUint32(0, integer, false)
        return new Uint8Array(view.buffer)
 }
 
-function ser256 (integer: bigint): Uint8Array<ArrayBuffer> {
+function ser256 (integer: bigint): Bytes {
        let bytes = new Uint8Array(32)
        for (let i = bytes.byteLength - 1; i >= 0; i--) {
                bytes[i] = Number(integer & 0xffn)
@@ -137,7 +138,7 @@ function ser256 (integer: bigint): Uint8Array<ArrayBuffer> {
        return bytes
 }
 
-function parse256 (integer: Uint8Array<ArrayBuffer>): bigint {
+function parse256 (integer: Bytes): bigint {
        let result = 0n
        for (let i = 0; i < integer.byteLength; i++) {
                result = (result << 8n) | BigInt(integer[i])
@@ -145,7 +146,7 @@ function parse256 (integer: Uint8Array<ArrayBuffer>): bigint {
        return result
 }
 
-function hmac (key: Uint8Array<ArrayBuffer>, data: Uint8Array<ArrayBuffer>): Promise<ArrayBuffer> {
+function hmac (key: ArrayBuffer, data: ArrayBuffer): Promise<ArrayBuffer> {
        return crypto.subtle
                .importKey('raw', key, { name: 'HMAC', hash: 'SHA-512' }, false, ['sign'])
                .then(pk => crypto.subtle.sign('HMAC', pk, data))