Skip to content

Commit 2f3a94e

Browse files
vveerrggclaude
andcommitted
chore(deps): upgrade to Noble 2.0, nostr-tools 2.x, vitest 4 (v0.2.0)
Replace @noble/secp256k1 with @noble/curves, migrate @noble/hashes/sha256 to sha2, enforce Uint8Array-only inputs, rename randomPrivateKey to randomSecretKey. All 11 tests pass, 0 vulnerabilities. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a55dab9 commit 2f3a94e

4 files changed

Lines changed: 34 additions & 56 deletions

File tree

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-red-contrib-nostr",
3-
"version": "0.1.5",
3+
"version": "0.2.0",
44
"description": "Node-RED nodes for seamless Nostr protocol integration. Features robust WebSocket handling, event filtering, and NPUB-based routing. Built with TypeScript for type safety and extensive testing. Perfect for Nostr automation flows.",
55
"author": "vveerrgg",
66
"type": "commonjs",
@@ -49,11 +49,11 @@
4949
}
5050
},
5151
"dependencies": {
52-
"@noble/hashes": "^1.8.0",
53-
"@noble/secp256k1": "^2.3.0",
52+
"@noble/curves": "^2.0.1",
53+
"@noble/hashes": "^2.0.1",
5454
"bech32": "^2.0.0",
55-
"nostr-tools": "^1.17.0",
56-
"nostr-websocket-utils": "^0.3.13",
55+
"nostr-tools": "^2.23.3",
56+
"nostr-websocket-utils": "file:../nostr-websocket-utils",
5757
"ws": "^8.19.0"
5858
},
5959
"devDependencies": {
@@ -63,16 +63,16 @@
6363
"@types/ws": "^8.18.1",
6464
"@typescript-eslint/eslint-plugin": "^8.56.0",
6565
"@typescript-eslint/parser": "^8.56.0",
66-
"@vitest/coverage-v8": "^3.2.4",
67-
"@vitest/ui": "^3.2.4",
66+
"@vitest/coverage-v8": "^4.0.18",
67+
"@vitest/ui": "^4.0.18",
6868
"eslint": "^10.0.1",
6969
"node-red": "^4.1.5",
7070
"node-red-node-test-helper": "^0.3.6",
7171
"typescript": "^5.9.3",
72-
"vitest": "^3.2.4"
72+
"vitest": "^4.0.18"
7373
},
7474
"engines": {
75-
"node": ">=14.0.0"
75+
"node": ">=18.0.0"
7676
},
7777
"repository": {
7878
"type": "git",

src/core/event.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { sha256 } from '@noble/hashes/sha256';
2-
import { bytesToHex } from '@noble/hashes/utils';
1+
import { sha256 } from '@noble/hashes/sha2.js';
2+
import { bytesToHex } from '@noble/hashes/utils.js';
33
import { NostrEvent } from '../nodes/shared/types';
44
import { KeyManager } from '../crypto/keys';
55

src/crypto/keys.ts

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
// Types only
2-
import type * as secp256k1Type from '@noble/secp256k1';
1+
import { secp256k1 } from '@noble/curves/secp256k1.js';
32
import { bech32 } from 'bech32';
4-
import { bytesToHex } from '@noble/hashes/utils';
5-
import { sha256 } from '@noble/hashes/sha256';
3+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
4+
import { sha256 } from '@noble/hashes/sha2.js';
65

76
// Lazily-initialized ephemeral key pair for read-only operations.
87
// Generated at first use so no secret material is stored in source code.
@@ -15,27 +14,17 @@ export async function getDefaultReaderKeys(): Promise<{ privateKey: string; publ
1514
return _defaultReaderKeys;
1615
}
1716

18-
let secp256k1: typeof secp256k1Type;
19-
20-
async function initSecp256k1() {
21-
if (!secp256k1) {
22-
secp256k1 = await import('@noble/secp256k1');
23-
}
24-
return secp256k1;
25-
}
26-
2717
/**
2818
* Generate a new Nostr key pair
2919
* @returns {Promise<Object>} Object containing private and public keys
3020
*/
3121
export async function generateKeyPair() {
32-
const secp = await initSecp256k1();
33-
const privateKey = secp.utils.randomPrivateKey();
34-
const publicKey = secp.getPublicKey(privateKey, true);
35-
22+
const privateKey = secp256k1.utils.randomSecretKey();
23+
const publicKey = secp256k1.getPublicKey(privateKey, true);
24+
3625
return {
37-
privateKey: Buffer.from(privateKey).toString('hex'),
38-
publicKey: Buffer.from(publicKey).toString('hex')
26+
privateKey: bytesToHex(privateKey),
27+
publicKey: bytesToHex(publicKey)
3928
};
4029
}
4130

@@ -65,47 +54,36 @@ export function npubToHex(npub: string): string {
6554
* @returns {Promise<string>} Public key in hex format
6655
*/
6756
export async function getPublicKey(privateKeyHex: string): Promise<string> {
68-
const secp = await initSecp256k1();
69-
const publicKey = secp.getPublicKey(privateKeyHex, true);
70-
return Buffer.from(publicKey).toString('hex');
57+
const publicKey = secp256k1.getPublicKey(hexToBytes(privateKeyHex), true);
58+
return bytesToHex(publicKey);
7159
}
7260

7361
export class KeyManager {
74-
private secp256k1Promise: Promise<typeof secp256k1Type>;
75-
76-
constructor() {
77-
this.secp256k1Promise = initSecp256k1();
78-
}
79-
8062
async generatePrivateKey(): Promise<string> {
81-
const secp = await this.secp256k1Promise;
82-
const privateKey = secp.utils.randomPrivateKey();
83-
return Buffer.from(privateKey).toString('hex');
63+
const privateKey = secp256k1.utils.randomSecretKey();
64+
return bytesToHex(privateKey);
8465
}
8566

8667
async getPublicKey(privateKey: string): Promise<string> {
87-
const secp = await this.secp256k1Promise;
88-
const publicKey = secp.getPublicKey(privateKey, true);
89-
return Buffer.from(publicKey).toString('hex');
68+
const publicKey = secp256k1.getPublicKey(hexToBytes(privateKey), true);
69+
return bytesToHex(publicKey);
9070
}
9171

9272
async sign(
9373
privateKey: string,
9474
message: string
9575
): Promise<string> {
96-
const secp = await this.secp256k1Promise;
97-
const messageHash = sha256(Buffer.from(message));
98-
const signature = await secp.sign(messageHash, privateKey);
99-
return bytesToHex(signature.toCompactRawBytes());
76+
const messageHash = sha256(new TextEncoder().encode(message));
77+
const signature = secp256k1.sign(messageHash, hexToBytes(privateKey));
78+
return bytesToHex(signature);
10079
}
10180

10281
async verify(
10382
publicKey: string,
10483
message: string,
10584
signature: string
10685
): Promise<boolean> {
107-
const secp = await this.secp256k1Promise;
108-
const messageHash = sha256(Buffer.from(message));
109-
return secp.verify(signature, messageHash, publicKey);
86+
const messageHash = sha256(new TextEncoder().encode(message));
87+
return secp256k1.verify(hexToBytes(signature), messageHash, hexToBytes(publicKey));
11088
}
11189
}

src/nips/nip04.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import * as secp256k1 from '@noble/secp256k1';
2-
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
3-
import { sha256 } from '@noble/hashes/sha256';
1+
import { secp256k1 } from '@noble/curves/secp256k1.js';
2+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
3+
import { sha256 } from '@noble/hashes/sha2.js';
44
import { NostrEvent } from '../nodes/shared/types';
55
import { EventBuilder } from '../core/event';
66

@@ -34,7 +34,7 @@ export class NIP04 {
3434
hexToBytes(publicKey)
3535
);
3636
const _sharedX = sharedPoint.slice(1, 33);
37-
37+
3838
// TODO: Implement actual decryption using AES-256-CBC
3939
return "decrypted text";
4040
}

0 commit comments

Comments
 (0)