From b40f59af74766a70e1a636b6c0acdf5d54060a12 Mon Sep 17 00:00:00 2001 From: lemonknowsall Date: Fri, 23 Jan 2026 23:37:46 -0800 Subject: [PATCH] Upgrade to @noble/curves ^2.0.1 and @noble/hashes ^2.0.1 This commit upgrades the noble cryptography dependencies to v2.0.1, which includes: Breaking changes addressed: - Updated all @noble imports to include .js extensions (required by v2 ESM-only API) - Changed @noble/hashes/sha256 to @noble/hashes/sha2.js across 8 files - Fixed secp256k1 API changes: methods now require Uint8Array instead of hex strings - Updated schnorr.utils.randomPrivateKey() to schnorr.utils.randomSecretKey() Files modified (27 total): - package.json: Bump dependency versions - Source files (12): pure.ts, nip04.ts, nip06.ts, nip13.ts, nip19.ts, nip44.ts, nip49.ts, nip77.ts, nip98.ts, nipb7.ts, utils.ts, wasm.ts - Test files (14): All corresponding test files updated Benefits: - Latest security updates from audited noble libraries - Smaller bundle sizes from v2 optimizations - Future-proof ESM-only compatibility - All tests passing Co-authored-by: OpenCode --- nip04.test.ts | 2 +- nip04.ts | 12 ++++++------ nip06.test.ts | 2 +- nip06.ts | 2 +- nip13.ts | 4 ++-- nip17.test.ts | 2 +- nip18.test.ts | 2 +- nip19.ts | 2 +- nip25.test.ts | 2 +- nip28.test.ts | 2 +- nip44.test.ts | 8 ++++---- nip44.ts | 14 +++++++------- nip47.test.ts | 2 +- nip49.test.ts | 2 +- nip49.ts | 4 ++-- nip59.test.ts | 2 +- nip77.ts | 2 +- nip98.test.ts | 4 ++-- nip98.ts | 4 ++-- nipb7.test.ts | 2 +- nipb7.ts | 2 +- package.json | 4 ++-- pool.test.ts | 2 +- pure.test.ts | 2 +- pure.ts | 12 ++++++------ utils.ts | 2 +- wasm.ts | 2 +- 27 files changed, 51 insertions(+), 51 deletions(-) diff --git a/nip04.test.ts b/nip04.test.ts index bc94746..e7db1cb 100644 --- a/nip04.test.ts +++ b/nip04.test.ts @@ -2,7 +2,7 @@ import { test, expect } from 'bun:test' import { encrypt, decrypt } from './nip04.ts' import { getPublicKey, generateSecretKey } from './pure.ts' -import { bytesToHex, hexToBytes } from '@noble/hashes/utils' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js' test('encrypt and decrypt message', async () => { let sk1 = generateSecretKey() diff --git a/nip04.ts b/nip04.ts index 83a279f..549b173 100644 --- a/nip04.ts +++ b/nip04.ts @@ -1,13 +1,13 @@ -import { bytesToHex, randomBytes } from '@noble/hashes/utils' -import { secp256k1 } from '@noble/curves/secp256k1' +import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils.js' +import { secp256k1 } from '@noble/curves/secp256k1.js' import { cbc } from '@noble/ciphers/aes' import { base64 } from '@scure/base' import { utf8Decoder, utf8Encoder } from './utils.ts' export function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string { - const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey - const key = secp256k1.getSharedSecret(privkey, '02' + pubkey) + const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey) + const key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey)) const normalizedKey = getNormalizedX(key) let iv = Uint8Array.from(randomBytes(16)) @@ -22,9 +22,9 @@ export function encrypt(secretKey: string | Uint8Array, pubkey: string, text: st } export function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string { - const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey + const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey) let [ctb64, ivb64] = data.split('?iv=') - let key = secp256k1.getSharedSecret(privkey, '02' + pubkey) + let key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey)) let normalizedKey = getNormalizedX(key) let iv = base64.decode(ivb64) diff --git a/nip06.test.ts b/nip06.test.ts index f463d9b..6a993d7 100644 --- a/nip06.test.ts +++ b/nip06.test.ts @@ -5,7 +5,7 @@ import { extendedKeysFromSeedWords, accountFromExtendedKey, } from './nip06.ts' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' test('generate private key from a mnemonic', async () => { const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' diff --git a/nip06.ts b/nip06.ts index b20c7bf..dba9f01 100644 --- a/nip06.ts +++ b/nip06.ts @@ -1,4 +1,4 @@ -import { bytesToHex } from '@noble/hashes/utils' +import { bytesToHex } from '@noble/hashes/utils.js' import { wordlist } from '@scure/bip39/wordlists/english' import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39' import { HDKey } from '@scure/bip32' diff --git a/nip13.ts b/nip13.ts index 45003a7..ce786ad 100644 --- a/nip13.ts +++ b/nip13.ts @@ -1,6 +1,6 @@ -import { bytesToHex } from '@noble/hashes/utils' +import { bytesToHex } from '@noble/hashes/utils.js' import { type UnsignedEvent, type Event } from './pure.ts' -import { sha256 } from '@noble/hashes/sha256' +import { sha256 } from '@noble/hashes/sha2.js' import { utf8Encoder } from './utils.ts' diff --git a/nip17.test.ts b/nip17.test.ts index ac7eaf0..04fde03 100644 --- a/nip17.test.ts +++ b/nip17.test.ts @@ -2,7 +2,7 @@ import { test, expect } from 'bun:test' import { getPublicKey } from './pure.ts' import { decode } from './nip19.ts' import { wrapEvent, wrapManyEvents, unwrapEvent } from './nip17.ts' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' const senderPrivateKey = decode(`nsec1p0ht6p3wepe47sjrgesyn4m50m6avk2waqudu9rl324cg2c4ufesyp6rdg`).data diff --git a/nip18.test.ts b/nip18.test.ts index a2630fd..d22f18f 100644 --- a/nip18.test.ts +++ b/nip18.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'bun:test' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' import { EventTemplate, finalizeEvent, getPublicKey } from './pure.ts' import { GenericRepost, Repost, ShortTextNote, BadgeDefinition as BadgeDefinitionKind } from './kinds.ts' import { finishRepostEvent, getRepostedEventPointer, getRepostedEvent } from './nip18.ts' diff --git a/nip19.ts b/nip19.ts index 5642471..ca83d81 100644 --- a/nip19.ts +++ b/nip19.ts @@ -1,4 +1,4 @@ -import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils' +import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js' import { bech32 } from '@scure/base' import { utf8Decoder, utf8Encoder } from './utils.ts' diff --git a/nip25.test.ts b/nip25.test.ts index f1d86bc..6d371d7 100644 --- a/nip25.test.ts +++ b/nip25.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'bun:test' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' import { finalizeEvent, getPublicKey } from './pure.ts' import { Reaction, ShortTextNote } from './kinds.ts' import { finishReactionEvent, getReactedEventPointer } from './nip25.ts' diff --git a/nip28.test.ts b/nip28.test.ts index 446d28d..79bfcd9 100644 --- a/nip28.test.ts +++ b/nip28.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'bun:test' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' import { getPublicKey } from './pure.ts' import * as Kind from './kinds.ts' import { diff --git a/nip44.test.ts b/nip44.test.ts index a21801e..4c1bb82 100644 --- a/nip44.test.ts +++ b/nip44.test.ts @@ -1,8 +1,8 @@ import { test, expect } from 'bun:test' import { v2 } from './nip44.js' -import { bytesToHex, hexToBytes } from '@noble/hashes/utils' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js' import { default as vec } from './nip44.vectors.json' with { type: 'json' } -import { schnorr } from '@noble/curves/secp256k1' +import { schnorr } from '@noble/curves/secp256k1.js' const v2vec = vec.v2 test('get_conversation_key', () => { @@ -14,7 +14,7 @@ test('get_conversation_key', () => { test('encrypt_decrypt', () => { for (const v of v2vec.valid.encrypt_decrypt) { - const pub2 = bytesToHex(schnorr.getPublicKey(v.sec2)) + const pub2 = bytesToHex(schnorr.getPublicKey(hexToBytes(v.sec2))) const key = v2.utils.getConversationKey(hexToBytes(v.sec1), pub2) expect(bytesToHex(key)).toEqual(v.conversation_key) const ciphertext = v2.encrypt(v.plaintext, key, hexToBytes(v.nonce)) @@ -40,7 +40,7 @@ test('decrypt', async () => { test('get_conversation_key', async () => { for (const v of v2vec.invalid.get_conversation_key) { expect(() => v2.utils.getConversationKey(hexToBytes(v.sec1), v.pub2)).toThrow( - /(Point is not on curve|Cannot find square root)/, + /(Point is not on curve|Cannot find square root|invalid field element)/, ) } }) diff --git a/nip44.ts b/nip44.ts index 6af484d..8b439e7 100644 --- a/nip44.ts +++ b/nip44.ts @@ -1,10 +1,10 @@ import { chacha20 } from '@noble/ciphers/chacha' import { equalBytes } from '@noble/ciphers/utils' -import { secp256k1 } from '@noble/curves/secp256k1' -import { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf' -import { hmac } from '@noble/hashes/hmac' -import { sha256 } from '@noble/hashes/sha256' -import { concatBytes, randomBytes } from '@noble/hashes/utils' +import { secp256k1 } from '@noble/curves/secp256k1.js' +import { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js' +import { hmac } from '@noble/hashes/hmac.js' +import { sha256 } from '@noble/hashes/sha2.js' +import { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js' import { base64 } from '@scure/base' import { utf8Decoder, utf8Encoder } from './utils.ts' @@ -13,8 +13,8 @@ const minPlaintextSize = 0x0001 // 1b msg => padded to 32b const maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb export function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array { - const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33) - return hkdf_extract(sha256, sharedX, 'nip44-v2') + const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33) + return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2')) } function getMessageKeys( diff --git a/nip47.test.ts b/nip47.test.ts index 2aecc67..a457f38 100644 --- a/nip47.test.ts +++ b/nip47.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'bun:test' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' import { makeNwcRequestEvent, parseConnectionString } from './nip47.ts' import { decrypt } from './nip04.ts' import { NWCWalletRequest } from './kinds.ts' diff --git a/nip49.test.ts b/nip49.test.ts index 429b539..984110b 100644 --- a/nip49.test.ts +++ b/nip49.test.ts @@ -1,6 +1,6 @@ import { test, expect } from 'bun:test' import { decrypt, encrypt } from './nip49.ts' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' test('encrypt and decrypt', () => { for (let i = 0; i < vectors.length; i++) { diff --git a/nip49.ts b/nip49.ts index 0021163..09728a6 100644 --- a/nip49.ts +++ b/nip49.ts @@ -1,6 +1,6 @@ -import { scrypt } from '@noble/hashes/scrypt' +import { scrypt } from '@noble/hashes/scrypt.js' import { xchacha20poly1305 } from '@noble/ciphers/chacha' -import { concatBytes, randomBytes } from '@noble/hashes/utils' +import { concatBytes, randomBytes } from '@noble/hashes/utils.js' import { Bech32MaxSize, Ncryptsec, encodeBytes } from './nip19.ts' import { bech32 } from '@scure/base' diff --git a/nip59.test.ts b/nip59.test.ts index 1e1b4ad..c60a52c 100644 --- a/nip59.test.ts +++ b/nip59.test.ts @@ -4,7 +4,7 @@ import { decode } from './nip19.ts' import { NostrEvent, getPublicKey } from './pure.ts' import { SimplePool } from './pool.ts' import { GiftWrap } from './kinds.ts' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' const senderPrivateKey = decode(`nsec1p0ht6p3wepe47sjrgesyn4m50m6avk2waqudu9rl324cg2c4ufesyp6rdg`).data as Uint8Array const recipientPrivateKey = decode(`nsec1uyyrnx7cgfp40fcskcr2urqnzekc20fj0er6de0q8qvhx34ahazsvs9p36`).data as Uint8Array diff --git a/nip77.ts b/nip77.ts index fb0ae6f..58ece6c 100644 --- a/nip77.ts +++ b/nip77.ts @@ -1,7 +1,7 @@ import { bytesToHex, hexToBytes } from '@noble/ciphers/utils' import { Filter } from './filter.ts' import { AbstractRelay, Subscription } from './relay.ts' -import { sha256 } from '@noble/hashes/sha256' +import { sha256 } from '@noble/hashes/sha2.js' // Negentropy implementation by Doug Hoyte const PROTOCOL_VERSION = 0x61 // Version 1 diff --git a/nip98.test.ts b/nip98.test.ts index 4b05868..7018e95 100644 --- a/nip98.test.ts +++ b/nip98.test.ts @@ -1,5 +1,5 @@ -import { sha256 } from '@noble/hashes/sha256' -import { bytesToHex } from '@noble/hashes/utils' +import { sha256 } from '@noble/hashes/sha2.js' +import { bytesToHex } from '@noble/hashes/utils.js' import { describe, expect, test } from 'bun:test' import { HTTPAuth } from './kinds.ts' diff --git a/nip98.ts b/nip98.ts index 5800d65..55baafa 100644 --- a/nip98.ts +++ b/nip98.ts @@ -1,5 +1,5 @@ -import { sha256 } from '@noble/hashes/sha256' -import { bytesToHex } from '@noble/hashes/utils' +import { sha256 } from '@noble/hashes/sha2.js' +import { bytesToHex } from '@noble/hashes/utils.js' import { base64 } from '@scure/base' import { HTTPAuth } from './kinds.ts' diff --git a/nipb7.test.ts b/nipb7.test.ts index 2088cd7..26475ab 100644 --- a/nipb7.test.ts +++ b/nipb7.test.ts @@ -1,6 +1,6 @@ import { test, expect } from 'bun:test' import { BlossomClient } from './nipb7.ts' -import { sha256 } from '@noble/hashes/sha256' +import { sha256 } from '@noble/hashes/sha2.js' import { bytesToHex } from './utils.ts' import { PlainKeySigner } from './signer.ts' import { generateSecretKey } from './pure.ts' diff --git a/nipb7.ts b/nipb7.ts index 15bc2c5..94b57de 100644 --- a/nipb7.ts +++ b/nipb7.ts @@ -1,4 +1,4 @@ -import { sha256 } from '@noble/hashes/sha256' +import { sha256 } from '@noble/hashes/sha2.js' import { EventTemplate } from './core.ts' import { Signer } from './signer.ts' import { bytesToHex } from './utils.ts' diff --git a/package.json b/package.json index 21d8428..c981101 100644 --- a/package.json +++ b/package.json @@ -237,8 +237,8 @@ "license": "Unlicense", "dependencies": { "@noble/ciphers": "^0.5.1", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.1", + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1", diff --git a/pool.test.ts b/pool.test.ts index f385c16..543d161 100644 --- a/pool.test.ts +++ b/pool.test.ts @@ -3,7 +3,7 @@ import { afterEach, beforeEach, expect, test } from 'bun:test' import { SimplePool, useWebSocketImplementation } from './pool.ts' import { finalizeEvent, generateSecretKey, getPublicKey, type Event } from './pure.ts' import { MockRelay, MockWebSocketClient } from './test-helpers.ts' -import { hexToBytes } from '@noble/hashes/utils' +import { hexToBytes } from '@noble/hashes/utils.js' useWebSocketImplementation(MockWebSocketClient) diff --git a/pure.test.ts b/pure.test.ts index 72f2153..bc8f0aa 100644 --- a/pure.test.ts +++ b/pure.test.ts @@ -11,7 +11,7 @@ import { generateSecretKey, } from './pure.ts' import { ShortTextNote } from './kinds.ts' -import { bytesToHex, hexToBytes } from '@noble/hashes/utils' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js' test('private key generation', () => { expect(bytesToHex(generateSecretKey())).toMatch(/[a-f0-9]{64}/) diff --git a/pure.ts b/pure.ts index ca192f0..71a73e0 100644 --- a/pure.ts +++ b/pure.ts @@ -1,13 +1,13 @@ -import { schnorr } from '@noble/curves/secp256k1' -import { bytesToHex } from '@noble/hashes/utils' +import { schnorr } from '@noble/curves/secp256k1.js' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js' import { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts' -import { sha256 } from '@noble/hashes/sha256' +import { sha256 } from '@noble/hashes/sha2.js' import { utf8Encoder } from './utils.ts' class JS implements Nostr { generateSecretKey(): Uint8Array { - return schnorr.utils.randomPrivateKey() + return schnorr.utils.randomSecretKey() } getPublicKey(secretKey: Uint8Array): string { return bytesToHex(schnorr.getPublicKey(secretKey)) @@ -16,7 +16,7 @@ class JS implements Nostr { const event = t as VerifiedEvent event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey)) event.id = getEventHash(event) - event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey)) + event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey)) event[verifiedSymbol] = true return event } @@ -30,7 +30,7 @@ class JS implements Nostr { } try { - const valid = schnorr.verify(event.sig, hash, event.pubkey) + const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey)) event[verifiedSymbol] = valid return valid } catch (err) { diff --git a/utils.ts b/utils.ts index c08d697..55d6a32 100644 --- a/utils.ts +++ b/utils.ts @@ -3,7 +3,7 @@ import type { Event } from './core.ts' export const utf8Decoder: TextDecoder = new TextDecoder('utf-8') export const utf8Encoder: TextEncoder = new TextEncoder() -export { bytesToHex, hexToBytes } from '@noble/hashes/utils' +export { bytesToHex, hexToBytes } from '@noble/hashes/utils.js' export function normalizeURL(url: string): string { try { diff --git a/wasm.ts b/wasm.ts index 3ae4de7..9d4a272 100644 --- a/wasm.ts +++ b/wasm.ts @@ -1,4 +1,4 @@ -import { bytesToHex } from '@noble/hashes/utils' +import { bytesToHex } from '@noble/hashes/utils.js' import { Nostr as NostrWasm } from 'nostr-wasm' import { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'