From: Chris Duncan Date: Fri, 22 Nov 2024 11:58:26 +0000 (-0800) Subject: Eliminate remaining imported convert functions. Add type guard to initial seed ingest... X-Git-Url: https://zoso.dev/?a=commitdiff_plain;h=28ffe66d912a0873a9939fee2bd544ae21d40f21;p=libnemo.git Eliminate remaining imported convert functions. Add type guard to initial seed ingestion. Pass around DataViews instead of strings. --- diff --git a/src/lib/workers/ckdBip44.ts b/src/lib/workers/ckdBip44.ts index a05aef4..e48156e 100644 --- a/src/lib/workers/ckdBip44.ts +++ b/src/lib/workers/ckdBip44.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later type ExtendedKey = { - privateKey: string - chainCode: string + privateKey: DataView + chainCode: DataView } async function ckdBip44 () { @@ -11,12 +11,9 @@ async function ckdBip44 () { const BIP44_PURPOSE = 44 const HARDENED_OFFSET = 0x80000000 const SLIP10_ED25519 = 'ed25519 seed' - let bytes: any - let hex: any let addEventListener = globalThis.addEventListener let postMessage = globalThis.postMessage if (addEventListener == null || postMessage == null) { - ({ bytes, hex } = await import(new URL('./dist/lib/convert.js', import.meta.url).toString())) const { isMainThread, parentPort } = await import('node:worker_threads') if (!isMainThread && parentPort) { addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort) @@ -41,6 +38,9 @@ async function ckdBip44 () { * @returns Private child key for the account */ async function nanoCKD (seed: string, index: number): Promise { + if (seed.length < 32 || seed.length > 128) { + throw new RangeError(`Invalid seed length`) + } if (!Number.isSafeInteger(index) || index < 0 || index > 0x7fffffff) { throw new RangeError(`Invalid child key index 0x${index.toString(16)}`) } @@ -48,24 +48,29 @@ async function ckdBip44 () { const purposeKey = await CKDpriv(masterKey, BIP44_PURPOSE + HARDENED_OFFSET) const coinKey = await CKDpriv(purposeKey, BIP44_COIN_NANO + HARDENED_OFFSET) const accountKey = await CKDpriv(coinKey, index + HARDENED_OFFSET) - return accountKey.privateKey + const privateKey = new Uint8Array(accountKey.privateKey.buffer) + return privateKey.reduce((key, byte) => key.concat(byte.toString(16).padStart(2, '0')), '') } async function slip10 (curve: string, S: string): Promise { const key = new TextEncoder().encode(curve) - const data = hex.toBytes(S) + const data = new Uint8Array(64) + data.set(S.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))) const I = await hmac(key, data) - const IL = I.slice(0, I.length / 2) - const IR = I.slice(I.length / 2) + const IL = new DataView(I.buffer.slice(0, I.length / 2)) + const IR = new DataView(I.buffer.slice(I.length / 2)) return ({ privateKey: IL, chainCode: IR }) } async function CKDpriv ({ privateKey, chainCode }: ExtendedKey, index: number): Promise { - const key = hex.toBytes(chainCode) - const data = hex.toBytes(`00${bytes.toHex(ser256(privateKey))}${bytes.toHex(ser32(index))}`) + const key = new Uint8Array(chainCode.buffer) + const data = new Uint8Array(37) + data.set([0]) + data.set(ser256(privateKey), 1) + data.set(ser32(index), 33) const I = await hmac(key, data) - const IL = I.slice(0, I.length / 2) - const IR = I.slice(I.length / 2) + const IL = new DataView(I.buffer.slice(0, I.length / 2)) + const IR = new DataView(I.buffer.slice(I.length / 2)) return ({ privateKey: IL, chainCode: IR }) } @@ -81,26 +86,24 @@ async function ckdBip44 () { return new Uint8Array(view.buffer) } - function ser256 (integer: string): Uint8Array { - if (typeof integer !== 'string') { - throw new TypeError(`Expected string, received ${typeof integer}`) + function ser256 (integer: DataView): Uint8Array { + if (integer.constructor !== DataView) { + throw new TypeError(`Expected DataView, received ${typeof integer}`) } - const bits = hex.toBin(integer) - if (bits.length > 256) { - throw new RangeError(`Expected 256-bit integer, received ${bits.length}-bit value: ${integer}`) + if (integer.byteLength > 32) { + throw new RangeError(`Expected 32-byte integer, received ${integer.byteLength}-byte value: ${integer}`) } - return hex.toBytes(integer, 32) + return new Uint8Array(integer.buffer) } - async function hmac (key: Uint8Array, data: Uint8Array): Promise { + async function hmac (key: Uint8Array, data: Uint8Array): Promise { const { subtle } = globalThis.crypto const pk = await subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-512' }, false, ['sign']) const signature = await subtle.sign('HMAC', pk, data) - return bytes.toHex(new Uint8Array(signature)) + return new Uint8Array(signature) } } const ckdBip44Worker = `(${ckdBip44.toString()})()` -// const ckdBip44Worker = `data:text/javascript,(${ckdBip44.toString()})()` export { ckdBip44Worker }