nostr-crypto-utils 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { KeyPair, NostrEvent, SignedNostrEvent, ValidationResult } from './types';
1
+ import type { KeyPair, NostrEvent, SignedNostrEvent, ValidationResult, EncryptionResult } from './types';
2
+ export type { KeyPair, NostrEvent, SignedNostrEvent, ValidationResult, EncryptionResult };
2
3
  /**
3
4
  * Generate a private key for use with NOSTR
4
5
  */
@@ -9,8 +10,9 @@ export declare function generatePrivateKey(): string;
9
10
  export declare function getPublicKey(privateKey: string): string;
10
11
  /**
11
12
  * Generate a new key pair
13
+ * @param seedPhrase Optional seed phrase to generate deterministic key pair
12
14
  */
13
- export declare function generateKeyPair(): KeyPair;
15
+ export declare function generateKeyPair(seedPhrase?: string): KeyPair;
14
16
  /**
15
17
  * Get the hash of a NOSTR event
16
18
  */
package/dist/index.js CHANGED
@@ -4,6 +4,9 @@ import { sha256 } from '@noble/hashes/sha256';
4
4
  import { randomBytes } from '@noble/hashes/utils';
5
5
  import * as secp256k1 from '@noble/secp256k1';
6
6
  import { generateSecretKey, getPublicKey as getNostrPublicKey } from 'nostr-tools';
7
+ import { webcrypto } from 'node:crypto';
8
+ // Use Node.js crypto API
9
+ const crypto = webcrypto;
7
10
  /**
8
11
  * Generate a private key for use with NOSTR
9
12
  */
@@ -18,8 +21,18 @@ export function getPublicKey(privateKey) {
18
21
  }
19
22
  /**
20
23
  * Generate a new key pair
24
+ * @param seedPhrase Optional seed phrase to generate deterministic key pair
21
25
  */
22
- export function generateKeyPair() {
26
+ export function generateKeyPair(seedPhrase) {
27
+ if (seedPhrase) {
28
+ // Use the seed phrase to generate a deterministic private key
29
+ const encoder = new TextEncoder();
30
+ const seedBytes = encoder.encode(seedPhrase);
31
+ const hash = sha256(seedBytes);
32
+ const privateKey = bytesToHex(hash);
33
+ const publicKey = getPublicKey(privateKey);
34
+ return { privateKey, publicKey };
35
+ }
23
36
  const privateKey = generatePrivateKey();
24
37
  const publicKey = getPublicKey(privateKey);
25
38
  return { privateKey, publicKey };
@@ -43,37 +56,60 @@ export function getEventHash(event) {
43
56
  * Sign a NOSTR event
44
57
  */
45
58
  export async function signEvent(event, privateKey) {
46
- const hash = getEventHash(event);
47
- const signature = bytesToHex(await schnorr.sign(hexToBytes(hash), hexToBytes(privateKey)));
48
- return {
59
+ const pubkey = getPublicKey(privateKey);
60
+ const eventToSign = {
49
61
  ...event,
62
+ pubkey,
63
+ created_at: event.created_at || Math.floor(Date.now() / 1000),
64
+ tags: event.tags || []
65
+ };
66
+ const hash = getEventHash(eventToSign);
67
+ const sig = bytesToHex(await schnorr.sign(hexToBytes(hash), hexToBytes(privateKey)));
68
+ return {
69
+ ...eventToSign,
50
70
  id: hash,
51
- sig: signature,
52
- pubkey: getPublicKey(privateKey)
71
+ sig
53
72
  };
54
73
  }
55
74
  /**
56
75
  * Verify a signature
57
76
  */
58
77
  export function verifySignature(event) {
59
- const hash = getEventHash(event);
60
- return schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey));
78
+ try {
79
+ const hash = getEventHash({
80
+ kind: event.kind,
81
+ created_at: event.created_at,
82
+ tags: event.tags,
83
+ content: event.content,
84
+ pubkey: event.pubkey
85
+ });
86
+ if (hash !== event.id) {
87
+ return false;
88
+ }
89
+ return schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey));
90
+ }
91
+ catch (error) {
92
+ return false;
93
+ }
61
94
  }
62
95
  /**
63
96
  * Validate a key pair
64
97
  */
65
98
  export function validateKeyPair(publicKey, privateKey) {
66
99
  try {
67
- const derivedPubKey = getPublicKey(privateKey);
68
- return {
69
- isValid: derivedPubKey === publicKey,
70
- error: derivedPubKey !== publicKey ? 'Public key does not match derived key' : undefined
71
- };
100
+ const derivedPublicKey = getPublicKey(privateKey);
101
+ if (derivedPublicKey !== publicKey) {
102
+ return {
103
+ isValid: false,
104
+ error: 'Public key does not match private key'
105
+ };
106
+ }
107
+ return { isValid: true };
72
108
  }
73
109
  catch (error) {
74
110
  return {
75
111
  isValid: false,
76
- error: error instanceof Error ? error.message : 'Unknown error occurred'
112
+ error: 'Invalid key pair'
77
113
  };
78
114
  }
79
115
  }
@@ -81,24 +117,28 @@ export function validateKeyPair(publicKey, privateKey) {
81
117
  * Encrypt a message using NIP-04
82
118
  */
83
119
  export async function encrypt(message, recipientPubKey, senderPrivKey) {
84
- const sharedSecret = secp256k1.getSharedSecret(senderPrivKey, '02' + recipientPubKey);
120
+ const sharedPoint = secp256k1.getSharedSecret(senderPrivKey, '02' + recipientPubKey);
121
+ const sharedX = sharedPoint.slice(1, 33);
85
122
  const iv = randomBytes(16);
86
- const key = sha256(sharedSecret);
87
123
  const textEncoder = new TextEncoder();
88
- const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['encrypt']);
89
- const encrypted = await crypto.subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, textEncoder.encode(message));
90
- return bytesToHex(iv) + bytesToHex(new Uint8Array(encrypted));
124
+ const plaintext = textEncoder.encode(message);
125
+ const key = await crypto.subtle.importKey('raw', sharedX, { name: 'AES-CBC', length: 256 }, false, ['encrypt']);
126
+ const ciphertext = await crypto.subtle.encrypt({ name: 'AES-CBC', iv }, key, plaintext);
127
+ const ctb64 = Buffer.from(new Uint8Array(ciphertext)).toString('base64');
128
+ const ivb64 = Buffer.from(new Uint8Array(iv)).toString('base64');
129
+ return `${ctb64}?iv=${ivb64}`;
91
130
  }
92
131
  /**
93
132
  * Decrypt a message using NIP-04
94
133
  */
95
134
  export async function decrypt(encryptedMessage, senderPubKey, recipientPrivKey) {
96
- const sharedSecret = secp256k1.getSharedSecret(recipientPrivKey, '02' + senderPubKey);
97
- const key = sha256(sharedSecret);
98
- const iv = hexToBytes(encryptedMessage.slice(0, 32));
99
- const ciphertext = hexToBytes(encryptedMessage.slice(32));
135
+ const [ctb64, ivb64] = encryptedMessage.split('?iv=');
136
+ const sharedPoint = secp256k1.getSharedSecret(recipientPrivKey, '02' + senderPubKey);
137
+ const sharedX = sharedPoint.slice(1, 33);
138
+ const key = await crypto.subtle.importKey('raw', sharedX, { name: 'AES-CBC', length: 256 }, false, ['decrypt']);
139
+ const iv = Buffer.from(ivb64, 'base64');
140
+ const ciphertext = Buffer.from(ctb64, 'base64');
141
+ const decrypted = await crypto.subtle.decrypt({ name: 'AES-CBC', iv }, key, ciphertext);
100
142
  const textDecoder = new TextDecoder();
101
- const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['decrypt']);
102
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, ciphertext);
103
143
  return textDecoder.decode(new Uint8Array(decrypted));
104
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nostr-crypto-utils",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Cryptographic utilities for NOSTR",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",