Skip to content

Commit 50ad6e9

Browse files
v0.0.4: Fix key generation, add comprehensive tests
- Fix randomPrivateKey -> randomSecretKey for @noble/secp256k1 v3.0.0 - Add comprehensive test suite (20 tests, all passing) - Add better error logging and debugging - All functionality verified working on test page
1 parent 51aa67e commit 50ad6e9

8 files changed

Lines changed: 365 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to Podkey will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.0.4] - 2024-12-XX
9+
10+
### Fixed
11+
12+
- Fixed key generation bug: Changed `randomPrivateKey()` to `randomSecretKey()` for @noble/secp256k1 v3.0.0 compatibility
13+
- Added comprehensive error logging for debugging
14+
- Fixed storage test suite (removed beforeEach, using clearStorage function)
15+
16+
### Added
17+
18+
- Comprehensive test suite with 20 passing tests
19+
- Crypto function tests (key generation, signing, verification)
20+
- Storage function tests (keypair management, trusted origins, auto-sign)
21+
- Better error handling and logging in background service worker
22+
823
## [0.0.3] - 2024-12-XX
924

1025
### Added
@@ -50,4 +65,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5065
- Trust management system
5166
- Storage abstraction layer
5267

68+
[0.0.4]: https://github.com/JavaScriptSolidServer/podkey/compare/v0.0.3...v0.0.4
5369
[0.0.3]: https://github.com/JavaScriptSolidServer/podkey/compare/v0.0.2...v0.0.3

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> World-class Nostr wallet extension with Solid superpowers
44
5-
[![Version](https://img.shields.io/badge/version-0.0.3-blue.svg)](https://github.com/JavaScriptSolidServer/podkey/releases)
5+
[![Version](https://img.shields.io/badge/version-0.0.4-blue.svg)](https://github.com/JavaScriptSolidServer/podkey/releases)
66
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
77
[![NIP-07](https://img.shields.io/badge/NIP--07-compatible-purple.svg)](https://github.com/nostr-protocol/nips/blob/master/07.md)
88
[![Test Page](https://img.shields.io/badge/test--page-live-brightgreen)](https://javascriptsolidserver.github.io/podkey/test-page/)
@@ -341,4 +341,4 @@ MIT License - see [LICENSE](LICENSE) for details
341341

342342
**Made with 🔑 by the JavaScriptSolidServer team**
343343

344-
_Podkey v0.0.3 - Your keys, your identity, your data_
344+
_Podkey v0.0.4 - Your keys, your identity, your data_

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "Podkey",
4-
"version": "0.0.3",
4+
"version": "0.0.4",
55
"description": "World-class Nostr wallet with Solid superpowers - your keys, your identity, your data",
66
"permissions": [
77
"storage",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "podkey",
3-
"version": "0.0.3",
3+
"version": "0.0.4",
44
"description": "World-class Nostr wallet extension with Solid superpowers - NIP-07 provider + auto-authentication",
55
"main": "src/index.js",
66
"type": "module",

src/background.js

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@ chrome.runtime.onInstalled.addListener(async () => {
2929

3030
// Handle messages from content scripts and popup
3131
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
32-
handleMessage(message, sender).then(sendResponse).catch(error => {
33-
sendResponse({ error: error.message });
34-
});
32+
handleMessage(message, sender)
33+
.then(result => {
34+
console.log('[Podkey] Message handled successfully:', message.type, result);
35+
sendResponse(result);
36+
})
37+
.catch(error => {
38+
console.error('[Podkey] Error handling message:', message.type, error);
39+
sendResponse({ error: error.message });
40+
});
3541

3642
return true; // Async response
3743
});
@@ -163,15 +169,28 @@ async function handleSignEvent (event, origin, sender) {
163169
* Generate new keypair
164170
*/
165171
async function handleGenerateKeypair () {
166-
const keypair = await generateKeypair();
167-
await storeKeypair(keypair.privateKey, keypair.publicKey);
168-
169-
console.log('[Podkey] New keypair generated');
170-
171-
return {
172-
publicKey: keypair.publicKey,
173-
did: `did:nostr:${keypair.publicKey}`
174-
};
172+
try {
173+
console.log('[Podkey] Starting keypair generation...');
174+
const keypair = await generateKeypair();
175+
console.log('[Podkey] Keypair generated:', {
176+
privateKeyLength: keypair.privateKey.length,
177+
publicKeyLength: keypair.publicKey.length
178+
});
179+
180+
await storeKeypair(keypair.privateKey, keypair.publicKey);
181+
console.log('[Podkey] Keypair stored');
182+
183+
const result = {
184+
publicKey: keypair.publicKey,
185+
did: `did:nostr:${keypair.publicKey}`
186+
};
187+
188+
console.log('[Podkey] Returning result:', result);
189+
return result;
190+
} catch (error) {
191+
console.error('[Podkey] Error generating keypair:', error);
192+
throw error;
193+
}
175194
}
176195

177196
/**

src/crypto.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ secp256k1.hashes.sha256 = sha256;
2020
*/
2121
export async function generateKeypair () {
2222
// Generate random 32-byte private key
23-
const privateKeyBytes = secp256k1.utils.randomPrivateKey();
23+
// In @noble/secp256k1 v3.0.0, use randomSecretKey instead of randomPrivateKey
24+
const privateKeyBytes = secp256k1.utils.randomSecretKey();
2425
const privateKey = bytesToHex(privateKeyBytes);
2526

2627
// Derive public key

test/crypto.test.js

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* Tests for Podkey cryptographic functions
3+
*/
4+
5+
import { describe, it } from 'node:test';
6+
import assert from 'node:assert';
7+
import {
8+
generateKeypair,
9+
getPublicKey,
10+
signEvent,
11+
verifySignature,
12+
getEventHash,
13+
isValidPublicKey
14+
} from '../src/crypto.js';
15+
16+
describe('Crypto Functions', () => {
17+
describe('generateKeypair', () => {
18+
it('should generate a valid keypair', async () => {
19+
const keypair = await generateKeypair();
20+
21+
assert(keypair, 'Keypair should be returned');
22+
assert(keypair.privateKey, 'Private key should exist');
23+
assert(keypair.publicKey, 'Public key should exist');
24+
assert.strictEqual(keypair.privateKey.length, 64, 'Private key should be 64 chars');
25+
assert.strictEqual(keypair.publicKey.length, 64, 'Public key should be 64 chars');
26+
assert(/^[0-9a-fA-F]{64}$/.test(keypair.privateKey), 'Private key should be hex');
27+
assert(/^[0-9a-fA-F]{64}$/.test(keypair.publicKey), 'Public key should be hex');
28+
});
29+
30+
it('should generate different keypairs each time', async () => {
31+
const keypair1 = await generateKeypair();
32+
const keypair2 = await generateKeypair();
33+
34+
assert.notStrictEqual(keypair1.privateKey, keypair2.privateKey, 'Private keys should differ');
35+
assert.notStrictEqual(keypair1.publicKey, keypair2.publicKey, 'Public keys should differ');
36+
});
37+
});
38+
39+
describe('getPublicKey', () => {
40+
it('should derive public key from private key', async () => {
41+
const keypair = await generateKeypair();
42+
const derivedPublicKey = getPublicKey(keypair.privateKey);
43+
44+
assert.strictEqual(derivedPublicKey, keypair.publicKey, 'Derived public key should match');
45+
assert.strictEqual(derivedPublicKey.length, 64, 'Public key should be 64 chars');
46+
});
47+
48+
it('should throw error for invalid private key', () => {
49+
assert.throws(() => getPublicKey('invalid'), /Private key must be/);
50+
assert.throws(() => getPublicKey('123'), /Private key must be/);
51+
assert.throws(() => getPublicKey('x'.repeat(64)), /Private key must be valid hexadecimal/);
52+
});
53+
});
54+
55+
describe('signEvent', () => {
56+
it('should sign an event correctly', async () => {
57+
const keypair = await generateKeypair();
58+
const event = {
59+
kind: 1,
60+
created_at: Math.floor(Date.now() / 1000),
61+
tags: [],
62+
content: 'Test event'
63+
};
64+
65+
const signed = await signEvent(event, keypair.privateKey);
66+
67+
assert(signed.id, 'Event should have id');
68+
assert(signed.pubkey, 'Event should have pubkey');
69+
assert(signed.sig, 'Event should have signature');
70+
assert.strictEqual(signed.id.length, 64, 'Event ID should be 64 chars');
71+
assert.strictEqual(signed.pubkey.length, 64, 'Pubkey should be 64 chars');
72+
assert.strictEqual(signed.sig.length, 128, 'Signature should be 128 chars');
73+
assert.strictEqual(signed.pubkey, keypair.publicKey, 'Pubkey should match');
74+
});
75+
76+
it('should include pubkey in event hash', async () => {
77+
const keypair = await generateKeypair();
78+
const event = {
79+
kind: 1,
80+
created_at: Math.floor(Date.now() / 1000),
81+
tags: [],
82+
content: 'Test'
83+
};
84+
85+
const signed = await signEvent(event, keypair.privateKey);
86+
const hashWithPubkey = getEventHash({ ...event, pubkey: keypair.publicKey });
87+
88+
assert.strictEqual(signed.id, hashWithPubkey, 'Event ID should match hash with pubkey');
89+
});
90+
});
91+
92+
describe('verifySignature', () => {
93+
it('should verify a valid signature', async () => {
94+
const keypair = await generateKeypair();
95+
const event = {
96+
kind: 1,
97+
created_at: Math.floor(Date.now() / 1000),
98+
tags: [],
99+
content: 'Test event'
100+
};
101+
102+
const signed = await signEvent(event, keypair.privateKey);
103+
const isValid = await verifySignature(signed);
104+
105+
assert.strictEqual(isValid, true, 'Signature should be valid');
106+
});
107+
108+
it('should reject invalid signature', async () => {
109+
const keypair = await generateKeypair();
110+
const event = {
111+
kind: 1,
112+
created_at: Math.floor(Date.now() / 1000),
113+
tags: [],
114+
content: 'Test event',
115+
id: 'a'.repeat(64),
116+
pubkey: keypair.publicKey,
117+
sig: 'b'.repeat(128) // Invalid signature
118+
};
119+
120+
const isValid = await verifySignature(event);
121+
assert.strictEqual(isValid, false, 'Invalid signature should be rejected');
122+
});
123+
});
124+
125+
describe('getEventHash', () => {
126+
it('should generate consistent hashes', () => {
127+
const event = {
128+
kind: 1,
129+
created_at: 1234567890,
130+
tags: [],
131+
content: 'Test',
132+
pubkey: 'a'.repeat(64)
133+
};
134+
135+
const hash1 = getEventHash(event);
136+
const hash2 = getEventHash(event);
137+
138+
assert.strictEqual(hash1, hash2, 'Hashes should be consistent');
139+
assert.strictEqual(hash1.length, 64, 'Hash should be 64 chars');
140+
});
141+
142+
it('should include pubkey in hash', () => {
143+
const event1 = {
144+
kind: 1,
145+
created_at: 1234567890,
146+
tags: [],
147+
content: 'Test',
148+
pubkey: 'a'.repeat(64)
149+
};
150+
151+
const event2 = {
152+
...event1,
153+
pubkey: 'b'.repeat(64)
154+
};
155+
156+
const hash1 = getEventHash(event1);
157+
const hash2 = getEventHash(event2);
158+
159+
assert.notStrictEqual(hash1, hash2, 'Different pubkeys should produce different hashes');
160+
});
161+
});
162+
163+
describe('isValidPublicKey', () => {
164+
it('should validate correct public keys', () => {
165+
assert.strictEqual(isValidPublicKey('a'.repeat(64)), true);
166+
assert.strictEqual(isValidPublicKey('0123456789abcdef'.repeat(4)), true);
167+
});
168+
169+
it('should reject invalid public keys', () => {
170+
assert.strictEqual(isValidPublicKey(''), false);
171+
assert.strictEqual(isValidPublicKey('a'.repeat(63)), false);
172+
assert.strictEqual(isValidPublicKey('a'.repeat(65)), false);
173+
assert.strictEqual(isValidPublicKey('g'.repeat(64)), false); // Invalid hex
174+
assert.strictEqual(isValidPublicKey(null), false);
175+
assert.strictEqual(isValidPublicKey(undefined), false);
176+
});
177+
});
178+
});

0 commit comments

Comments
 (0)