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 <opencode@anomalyco.com>
This commit is contained in:
lemonknowsall
2026-01-23 23:37:46 -08:00
committed by fiatjaf_
parent bfa40da316
commit b40f59af74
27 changed files with 51 additions and 51 deletions

View File

@@ -2,7 +2,7 @@ import { test, expect } from 'bun:test'
import { encrypt, decrypt } from './nip04.ts' import { encrypt, decrypt } from './nip04.ts'
import { getPublicKey, generateSecretKey } from './pure.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 () => { test('encrypt and decrypt message', async () => {
let sk1 = generateSecretKey() let sk1 = generateSecretKey()

View File

@@ -1,13 +1,13 @@
import { bytesToHex, randomBytes } from '@noble/hashes/utils' import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils.js'
import { secp256k1 } from '@noble/curves/secp256k1' import { secp256k1 } from '@noble/curves/secp256k1.js'
import { cbc } from '@noble/ciphers/aes' import { cbc } from '@noble/ciphers/aes'
import { base64 } from '@scure/base' import { base64 } from '@scure/base'
import { utf8Decoder, utf8Encoder } from './utils.ts' import { utf8Decoder, utf8Encoder } from './utils.ts'
export function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string { export function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {
const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)
const key = secp256k1.getSharedSecret(privkey, '02' + pubkey) const key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))
const normalizedKey = getNormalizedX(key) const normalizedKey = getNormalizedX(key)
let iv = Uint8Array.from(randomBytes(16)) 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 { 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 [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 normalizedKey = getNormalizedX(key)
let iv = base64.decode(ivb64) let iv = base64.decode(ivb64)

View File

@@ -5,7 +5,7 @@ import {
extendedKeysFromSeedWords, extendedKeysFromSeedWords,
accountFromExtendedKey, accountFromExtendedKey,
} from './nip06.ts' } from './nip06.ts'
import { hexToBytes } from '@noble/hashes/utils' import { hexToBytes } from '@noble/hashes/utils.js'
test('generate private key from a mnemonic', async () => { test('generate private key from a mnemonic', async () => {
const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'

View File

@@ -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 { wordlist } from '@scure/bip39/wordlists/english'
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39' import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'
import { HDKey } from '@scure/bip32' import { HDKey } from '@scure/bip32'

View File

@@ -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 { 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' import { utf8Encoder } from './utils.ts'

View File

@@ -2,7 +2,7 @@ import { test, expect } from 'bun:test'
import { getPublicKey } from './pure.ts' import { getPublicKey } from './pure.ts'
import { decode } from './nip19.ts' import { decode } from './nip19.ts'
import { wrapEvent, wrapManyEvents, unwrapEvent } from './nip17.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 const senderPrivateKey = decode(`nsec1p0ht6p3wepe47sjrgesyn4m50m6avk2waqudu9rl324cg2c4ufesyp6rdg`).data

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'bun:test' 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 { EventTemplate, finalizeEvent, getPublicKey } from './pure.ts'
import { GenericRepost, Repost, ShortTextNote, BadgeDefinition as BadgeDefinitionKind } from './kinds.ts' import { GenericRepost, Repost, ShortTextNote, BadgeDefinition as BadgeDefinitionKind } from './kinds.ts'
import { finishRepostEvent, getRepostedEventPointer, getRepostedEvent } from './nip18.ts' import { finishRepostEvent, getRepostedEventPointer, getRepostedEvent } from './nip18.ts'

View File

@@ -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 { bech32 } from '@scure/base'
import { utf8Decoder, utf8Encoder } from './utils.ts' import { utf8Decoder, utf8Encoder } from './utils.ts'

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'bun:test' 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 { finalizeEvent, getPublicKey } from './pure.ts'
import { Reaction, ShortTextNote } from './kinds.ts' import { Reaction, ShortTextNote } from './kinds.ts'
import { finishReactionEvent, getReactedEventPointer } from './nip25.ts' import { finishReactionEvent, getReactedEventPointer } from './nip25.ts'

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'bun:test' 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 { getPublicKey } from './pure.ts'
import * as Kind from './kinds.ts' import * as Kind from './kinds.ts'
import { import {

View File

@@ -1,8 +1,8 @@
import { test, expect } from 'bun:test' import { test, expect } from 'bun:test'
import { v2 } from './nip44.js' 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 { 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 const v2vec = vec.v2
test('get_conversation_key', () => { test('get_conversation_key', () => {
@@ -14,7 +14,7 @@ test('get_conversation_key', () => {
test('encrypt_decrypt', () => { test('encrypt_decrypt', () => {
for (const v of v2vec.valid.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) const key = v2.utils.getConversationKey(hexToBytes(v.sec1), pub2)
expect(bytesToHex(key)).toEqual(v.conversation_key) expect(bytesToHex(key)).toEqual(v.conversation_key)
const ciphertext = v2.encrypt(v.plaintext, key, hexToBytes(v.nonce)) const ciphertext = v2.encrypt(v.plaintext, key, hexToBytes(v.nonce))
@@ -40,7 +40,7 @@ test('decrypt', async () => {
test('get_conversation_key', async () => { test('get_conversation_key', async () => {
for (const v of v2vec.invalid.get_conversation_key) { for (const v of v2vec.invalid.get_conversation_key) {
expect(() => v2.utils.getConversationKey(hexToBytes(v.sec1), v.pub2)).toThrow( 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)/,
) )
} }
}) })

View File

@@ -1,10 +1,10 @@
import { chacha20 } from '@noble/ciphers/chacha' import { chacha20 } from '@noble/ciphers/chacha'
import { equalBytes } from '@noble/ciphers/utils' import { equalBytes } from '@noble/ciphers/utils'
import { secp256k1 } from '@noble/curves/secp256k1' import { secp256k1 } from '@noble/curves/secp256k1.js'
import { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf' import { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'
import { hmac } from '@noble/hashes/hmac' import { hmac } from '@noble/hashes/hmac.js'
import { sha256 } from '@noble/hashes/sha256' import { sha256 } from '@noble/hashes/sha2.js'
import { concatBytes, randomBytes } from '@noble/hashes/utils' import { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'
import { base64 } from '@scure/base' import { base64 } from '@scure/base'
import { utf8Decoder, utf8Encoder } from './utils.ts' 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 const maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb
export function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array { export function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {
const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33) const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)
return hkdf_extract(sha256, sharedX, 'nip44-v2') return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))
} }
function getMessageKeys( function getMessageKeys(

View File

@@ -1,5 +1,5 @@
import { describe, test, expect } from 'bun:test' 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 { makeNwcRequestEvent, parseConnectionString } from './nip47.ts'
import { decrypt } from './nip04.ts' import { decrypt } from './nip04.ts'
import { NWCWalletRequest } from './kinds.ts' import { NWCWalletRequest } from './kinds.ts'

View File

@@ -1,6 +1,6 @@
import { test, expect } from 'bun:test' import { test, expect } from 'bun:test'
import { decrypt, encrypt } from './nip49.ts' import { decrypt, encrypt } from './nip49.ts'
import { hexToBytes } from '@noble/hashes/utils' import { hexToBytes } from '@noble/hashes/utils.js'
test('encrypt and decrypt', () => { test('encrypt and decrypt', () => {
for (let i = 0; i < vectors.length; i++) { for (let i = 0; i < vectors.length; i++) {

View File

@@ -1,6 +1,6 @@
import { scrypt } from '@noble/hashes/scrypt' import { scrypt } from '@noble/hashes/scrypt.js'
import { xchacha20poly1305 } from '@noble/ciphers/chacha' 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 { Bech32MaxSize, Ncryptsec, encodeBytes } from './nip19.ts'
import { bech32 } from '@scure/base' import { bech32 } from '@scure/base'

View File

@@ -4,7 +4,7 @@ import { decode } from './nip19.ts'
import { NostrEvent, getPublicKey } from './pure.ts' import { NostrEvent, getPublicKey } from './pure.ts'
import { SimplePool } from './pool.ts' import { SimplePool } from './pool.ts'
import { GiftWrap } from './kinds.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 senderPrivateKey = decode(`nsec1p0ht6p3wepe47sjrgesyn4m50m6avk2waqudu9rl324cg2c4ufesyp6rdg`).data as Uint8Array
const recipientPrivateKey = decode(`nsec1uyyrnx7cgfp40fcskcr2urqnzekc20fj0er6de0q8qvhx34ahazsvs9p36`).data as Uint8Array const recipientPrivateKey = decode(`nsec1uyyrnx7cgfp40fcskcr2urqnzekc20fj0er6de0q8qvhx34ahazsvs9p36`).data as Uint8Array

View File

@@ -1,7 +1,7 @@
import { bytesToHex, hexToBytes } from '@noble/ciphers/utils' import { bytesToHex, hexToBytes } from '@noble/ciphers/utils'
import { Filter } from './filter.ts' import { Filter } from './filter.ts'
import { AbstractRelay, Subscription } from './relay.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 // Negentropy implementation by Doug Hoyte
const PROTOCOL_VERSION = 0x61 // Version 1 const PROTOCOL_VERSION = 0x61 // Version 1

View File

@@ -1,5 +1,5 @@
import { sha256 } from '@noble/hashes/sha256' import { sha256 } from '@noble/hashes/sha2.js'
import { bytesToHex } from '@noble/hashes/utils' import { bytesToHex } from '@noble/hashes/utils.js'
import { describe, expect, test } from 'bun:test' import { describe, expect, test } from 'bun:test'
import { HTTPAuth } from './kinds.ts' import { HTTPAuth } from './kinds.ts'

View File

@@ -1,5 +1,5 @@
import { sha256 } from '@noble/hashes/sha256' import { sha256 } from '@noble/hashes/sha2.js'
import { bytesToHex } from '@noble/hashes/utils' import { bytesToHex } from '@noble/hashes/utils.js'
import { base64 } from '@scure/base' import { base64 } from '@scure/base'
import { HTTPAuth } from './kinds.ts' import { HTTPAuth } from './kinds.ts'

View File

@@ -1,6 +1,6 @@
import { test, expect } from 'bun:test' import { test, expect } from 'bun:test'
import { BlossomClient } from './nipb7.ts' import { BlossomClient } from './nipb7.ts'
import { sha256 } from '@noble/hashes/sha256' import { sha256 } from '@noble/hashes/sha2.js'
import { bytesToHex } from './utils.ts' import { bytesToHex } from './utils.ts'
import { PlainKeySigner } from './signer.ts' import { PlainKeySigner } from './signer.ts'
import { generateSecretKey } from './pure.ts' import { generateSecretKey } from './pure.ts'

View File

@@ -1,4 +1,4 @@
import { sha256 } from '@noble/hashes/sha256' import { sha256 } from '@noble/hashes/sha2.js'
import { EventTemplate } from './core.ts' import { EventTemplate } from './core.ts'
import { Signer } from './signer.ts' import { Signer } from './signer.ts'
import { bytesToHex } from './utils.ts' import { bytesToHex } from './utils.ts'

View File

@@ -237,8 +237,8 @@
"license": "Unlicense", "license": "Unlicense",
"dependencies": { "dependencies": {
"@noble/ciphers": "^0.5.1", "@noble/ciphers": "^0.5.1",
"@noble/curves": "1.2.0", "@noble/curves": "^2.0.1",
"@noble/hashes": "1.3.1", "@noble/hashes": "^2.0.1",
"@scure/base": "1.1.1", "@scure/base": "1.1.1",
"@scure/bip32": "1.3.1", "@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1", "@scure/bip39": "1.2.1",

View File

@@ -3,7 +3,7 @@ import { afterEach, beforeEach, expect, test } from 'bun:test'
import { SimplePool, useWebSocketImplementation } from './pool.ts' import { SimplePool, useWebSocketImplementation } from './pool.ts'
import { finalizeEvent, generateSecretKey, getPublicKey, type Event } from './pure.ts' import { finalizeEvent, generateSecretKey, getPublicKey, type Event } from './pure.ts'
import { MockRelay, MockWebSocketClient } from './test-helpers.ts' import { MockRelay, MockWebSocketClient } from './test-helpers.ts'
import { hexToBytes } from '@noble/hashes/utils' import { hexToBytes } from '@noble/hashes/utils.js'
useWebSocketImplementation(MockWebSocketClient) useWebSocketImplementation(MockWebSocketClient)

View File

@@ -11,7 +11,7 @@ import {
generateSecretKey, generateSecretKey,
} from './pure.ts' } from './pure.ts'
import { ShortTextNote } from './kinds.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', () => { test('private key generation', () => {
expect(bytesToHex(generateSecretKey())).toMatch(/[a-f0-9]{64}/) expect(bytesToHex(generateSecretKey())).toMatch(/[a-f0-9]{64}/)

12
pure.ts
View File

@@ -1,13 +1,13 @@
import { schnorr } from '@noble/curves/secp256k1' import { schnorr } from '@noble/curves/secp256k1.js'
import { bytesToHex } from '@noble/hashes/utils' import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'
import { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts' 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' import { utf8Encoder } from './utils.ts'
class JS implements Nostr { class JS implements Nostr {
generateSecretKey(): Uint8Array { generateSecretKey(): Uint8Array {
return schnorr.utils.randomPrivateKey() return schnorr.utils.randomSecretKey()
} }
getPublicKey(secretKey: Uint8Array): string { getPublicKey(secretKey: Uint8Array): string {
return bytesToHex(schnorr.getPublicKey(secretKey)) return bytesToHex(schnorr.getPublicKey(secretKey))
@@ -16,7 +16,7 @@ class JS implements Nostr {
const event = t as VerifiedEvent const event = t as VerifiedEvent
event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey)) event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))
event.id = getEventHash(event) event.id = getEventHash(event)
event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey)) event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))
event[verifiedSymbol] = true event[verifiedSymbol] = true
return event return event
} }
@@ -30,7 +30,7 @@ class JS implements Nostr {
} }
try { 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 event[verifiedSymbol] = valid
return valid return valid
} catch (err) { } catch (err) {

View File

@@ -3,7 +3,7 @@ import type { Event } from './core.ts'
export const utf8Decoder: TextDecoder = new TextDecoder('utf-8') export const utf8Decoder: TextDecoder = new TextDecoder('utf-8')
export const utf8Encoder: TextEncoder = new TextEncoder() 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 { export function normalizeURL(url: string): string {
try { try {

View File

@@ -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 { Nostr as NostrWasm } from 'nostr-wasm'
import { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts' import { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'