nostr-crypto-utils 0.1.6 → 0.2.0
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/README.md +52 -85
- package/dist/__tests__/crypto.test.js +13 -13
- package/dist/__tests__/guards.test.js +1 -0
- package/dist/__tests__/integration.test.js +18 -6
- package/dist/__tests__/nip_compliance.test.js +56 -53
- package/dist/__tests__/setup.d.ts +1 -0
- package/dist/__tests__/setup.js +5 -0
- package/dist/__tests__/transport.test.js +95 -43
- package/dist/__tests__/types.test.js +16 -0
- package/dist/__tests__/validation.test.js +29 -5
- package/dist/index.d.ts +172 -12
- package/dist/index.js +354 -98
- package/dist/integration.d.ts +150 -19
- package/dist/integration.js +157 -25
- package/dist/types/guards.d.ts +2 -2
- package/dist/types/guards.js +80 -43
- package/dist/types/protocol.d.ts +44 -34
- package/dist/types/protocol.js +16 -12
- package/dist/validation.d.ts +48 -5
- package/dist/validation.js +133 -62
- package/package.json +33 -17
package/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nostr-crypto-utils
|
|
2
2
|
|
|
3
3
|
A comprehensive TypeScript library providing cryptographic utilities and protocol-compliant message handling for Nostr applications, designed to work seamlessly with [@humanjavaenterprises/nostr-nsec-seedphrase](https://github.com/HumanjavaEnterprises/nostr-nsec-seedphrase).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@humanjavaenterprises/nostr-crypto-utils)
|
|
6
6
|
[](http://www.typescriptlang.org/)
|
|
7
7
|
[](https://github.com/HumanjavaEnterprises/nostr-crypto-utils/blob/main/LICENSE)
|
|
8
|
+
[](https://humanjavaenterprises.github.io/nostr-crypto-utils/)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
## Security Notice
|
|
10
11
|
|
|
11
|
-
This library handles cryptographic keys and operations that are critical for securing your Nostr identity and data. All cryptographic operations, including key generation, signing, and encryption, must be handled with appropriate security measures.
|
|
12
|
+
⚠️ **Important**: This library handles cryptographic keys and operations that are critical for securing your Nostr identity and data. All cryptographic operations, including key generation, signing, and encryption, must be handled with appropriate security measures.
|
|
13
|
+
|
|
14
|
+
If you discover a security vulnerability, please follow our [Security Policy](SECURITY.md) and report it through [GitHub's Security Advisory feature](https://github.com/humanjavaenterprises/nostr-crypto-utils/security/advisories/new).
|
|
12
15
|
|
|
13
16
|
## Features
|
|
14
17
|
|
|
@@ -19,117 +22,81 @@ This library handles cryptographic keys and operations that are critical for sec
|
|
|
19
22
|
- **Encryption**: Secure encryption and decryption for direct messages (NIP-04)
|
|
20
23
|
- **Validation**: Comprehensive validation for events, filters, and subscriptions
|
|
21
24
|
|
|
25
|
+
## Features and Capabilities
|
|
26
|
+
|
|
27
|
+
| Feature | Status | Description |
|
|
28
|
+
|---------------------------|--------|-------------------------------------------------------|
|
|
29
|
+
| Key Management | ✅ | Generate, validate, and manage Nostr keypairs |
|
|
30
|
+
| Event Creation | ✅ | Create and validate Nostr events |
|
|
31
|
+
| Event Signing | ✅ | Sign events with schnorr signatures |
|
|
32
|
+
| Event Verification | ✅ | Verify event signatures and validate event structure |
|
|
33
|
+
| Message Encryption | ✅ | NIP-04 compliant message encryption |
|
|
34
|
+
| Message Decryption | ✅ | NIP-04 compliant message decryption |
|
|
35
|
+
| Event Serialization | ✅ | Protocol-compliant event serialization |
|
|
36
|
+
| Event Hashing | ✅ | Generate and verify event IDs |
|
|
37
|
+
|
|
22
38
|
## Installation
|
|
23
39
|
|
|
24
40
|
```bash
|
|
25
|
-
npm install
|
|
41
|
+
npm install nostr-crypto-utils
|
|
26
42
|
```
|
|
27
43
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
### Key Management
|
|
44
|
+
## Quick Start
|
|
31
45
|
|
|
32
46
|
```typescript
|
|
33
|
-
import { generateKeyPair,
|
|
47
|
+
import { generateKeyPair, signEvent, verifyEvent } from 'nostr-crypto-utils';
|
|
34
48
|
|
|
35
|
-
// Generate a new
|
|
49
|
+
// Generate a new keypair
|
|
36
50
|
const keyPair = await generateKeyPair();
|
|
37
51
|
console.log('Public Key:', keyPair.publicKey);
|
|
38
52
|
console.log('Private Key:', keyPair.privateKey);
|
|
39
53
|
|
|
40
|
-
// Get public key from private key
|
|
41
|
-
const pubKey = getPublicKey(privateKey);
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Event Operations
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { createEvent, signEvent, validateEvent } from '@humanjavaenterprises/nostr-crypto-utils';
|
|
48
|
-
|
|
49
54
|
// Create and sign an event
|
|
50
|
-
const event =
|
|
51
|
-
kind:
|
|
55
|
+
const event = await signEvent({
|
|
56
|
+
kind: 1,
|
|
52
57
|
content: 'Hello Nostr!',
|
|
53
58
|
tags: []
|
|
54
|
-
});
|
|
55
|
-
const signedEvent = await signEvent(event, privateKey);
|
|
56
|
-
|
|
57
|
-
// Validate an event
|
|
58
|
-
const validation = validateEvent(event);
|
|
59
|
-
if (validation.isValid) {
|
|
60
|
-
console.log('Event is valid');
|
|
61
|
-
} else {
|
|
62
|
-
console.log('Validation errors:', validation.errors);
|
|
63
|
-
}
|
|
64
|
-
```
|
|
59
|
+
}, keyPair.privateKey);
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import {
|
|
70
|
-
formatEventForRelay,
|
|
71
|
-
formatSubscriptionForRelay,
|
|
72
|
-
parseNostrMessage
|
|
73
|
-
} from '@humanjavaenterprises/nostr-crypto-utils';
|
|
74
|
-
|
|
75
|
-
// Format event for relay
|
|
76
|
-
const eventMessage = formatEventForRelay(signedEvent);
|
|
77
|
-
|
|
78
|
-
// Format subscription request
|
|
79
|
-
const subscription = {
|
|
80
|
-
id: 'sub1',
|
|
81
|
-
filters: [{ kinds: [1], limit: 10 }]
|
|
82
|
-
};
|
|
83
|
-
const subMessage = formatSubscriptionForRelay(subscription);
|
|
84
|
-
|
|
85
|
-
// Parse incoming messages
|
|
86
|
-
const message = ['EVENT', signedEvent];
|
|
87
|
-
const parsed = parseNostrMessage(message);
|
|
61
|
+
// Verify the event
|
|
62
|
+
const isValidEvent = await verifyEvent(event);
|
|
88
63
|
```
|
|
89
64
|
|
|
90
|
-
|
|
65
|
+
## Documentation
|
|
91
66
|
|
|
92
|
-
|
|
93
|
-
import { encrypt, decrypt } from '@humanjavaenterprises/nostr-crypto-utils';
|
|
94
|
-
|
|
95
|
-
// Encrypt a message
|
|
96
|
-
const encrypted = await encrypt(
|
|
97
|
-
'Secret message',
|
|
98
|
-
recipientPublicKey,
|
|
99
|
-
senderPrivateKey
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// Decrypt a message
|
|
103
|
-
const decrypted = await decrypt(
|
|
104
|
-
encrypted,
|
|
105
|
-
senderPublicKey,
|
|
106
|
-
recipientPrivateKey
|
|
107
|
-
);
|
|
108
|
-
```
|
|
67
|
+
For detailed API documentation, visit our [TypeDoc Documentation](https://humanjavaenterprises.github.io/nostr-crypto-utils/).
|
|
109
68
|
|
|
110
|
-
##
|
|
69
|
+
## Support & Community
|
|
111
70
|
|
|
112
|
-
|
|
71
|
+
We welcome your feedback and contributions! Here's how you can get involved:
|
|
113
72
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
- NIP-28: Public Chat Channels
|
|
73
|
+
- 🐛 [Report bugs](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues/new?labels=bug&template=bug_report.md)
|
|
74
|
+
- 💡 [Request features](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues/new?labels=enhancement&template=feature_request.md)
|
|
75
|
+
- 💬 [Start a discussion](https://github.com/humanjavaenterprises/nostr-crypto-utils/discussions)
|
|
76
|
+
- 📖 [Read documentation](https://humanjavaenterprises.github.io/nostr-crypto-utils/)
|
|
77
|
+
- 🔒 [Report security issues](https://github.com/humanjavaenterprises/nostr-crypto-utils/security/advisories/new)
|
|
120
78
|
|
|
121
79
|
## Contributing
|
|
122
80
|
|
|
123
|
-
|
|
81
|
+
We welcome contributions! Please follow these steps:
|
|
124
82
|
|
|
125
|
-
|
|
83
|
+
1. Fork the repository
|
|
84
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
85
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
86
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
87
|
+
5. Open a Pull Request
|
|
126
88
|
|
|
127
|
-
|
|
89
|
+
Before contributing:
|
|
90
|
+
- Read our [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
91
|
+
- Check our [Contributing Guidelines](.github/CONTRIBUTING.md)
|
|
92
|
+
- Review our [Security Policy](SECURITY.md)
|
|
93
|
+
- Search [existing issues](https://github.com/humanjavaenterprises/nostr-crypto-utils/issues) before creating a new one
|
|
128
94
|
|
|
129
95
|
## License
|
|
130
96
|
|
|
131
|
-
|
|
97
|
+
[MIT](LICENSE)
|
|
132
98
|
|
|
133
99
|
---
|
|
134
|
-
|
|
135
|
-
|
|
100
|
+
<div align="center">
|
|
101
|
+
Made with ❤️ by <a href="https://github.com/humanjavaenterprises">Humanjava Enterprises</a>
|
|
102
|
+
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { generateKeyPair, getPublicKey, validateKeyPair, signEvent, verifySignature, encrypt, decrypt } from '../index';
|
|
3
3
|
describe('NOSTR Crypto Utils', () => {
|
|
4
4
|
describe('Key Management', () => {
|
|
@@ -63,7 +63,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
63
63
|
tags: [],
|
|
64
64
|
content: 'Hello, World!'
|
|
65
65
|
};
|
|
66
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
66
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
67
67
|
const signedEvent = await signEvent(event, privateKey);
|
|
68
68
|
expect(await verifySignature(signedEvent)).toBe(true);
|
|
69
69
|
});
|
|
@@ -74,7 +74,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
74
74
|
tags: [],
|
|
75
75
|
content: 'Hello, World!'
|
|
76
76
|
};
|
|
77
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
77
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
78
78
|
const signedEvent = await signEvent(event, privateKey);
|
|
79
79
|
// Tamper with the signature
|
|
80
80
|
signedEvent.sig = signedEvent.sig.replace('a', 'b');
|
|
@@ -87,7 +87,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
87
87
|
tags: [],
|
|
88
88
|
content: 'Hello, World!'
|
|
89
89
|
};
|
|
90
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
90
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
91
91
|
const signedEvent = await signEvent(event, privateKey);
|
|
92
92
|
// Tamper with the content which affects the hash
|
|
93
93
|
signedEvent.content = 'Modified content';
|
|
@@ -100,7 +100,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
100
100
|
tags: [],
|
|
101
101
|
content: 'Hello, World!'
|
|
102
102
|
};
|
|
103
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
103
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
104
104
|
const signedEvent = await signEvent(event, privateKey);
|
|
105
105
|
// Add invalid hex character
|
|
106
106
|
signedEvent.sig = 'XYZ' + signedEvent.sig.slice(3);
|
|
@@ -109,22 +109,22 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
109
109
|
});
|
|
110
110
|
describe('validateKeyPair', () => {
|
|
111
111
|
it('should validate a correct key pair', async () => {
|
|
112
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
112
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
113
113
|
const publicKey = await getPublicKey(privateKey);
|
|
114
114
|
const result = await validateKeyPair(publicKey, privateKey);
|
|
115
115
|
expect(result.isValid).toBe(true);
|
|
116
116
|
expect(result.error).toBeUndefined();
|
|
117
117
|
});
|
|
118
118
|
it('should reject mismatched key pair', async () => {
|
|
119
|
-
const privateKey1 = await generateKeyPair().privateKey;
|
|
120
|
-
const privateKey2 = await generateKeyPair().privateKey;
|
|
119
|
+
const privateKey1 = await (await generateKeyPair()).privateKey;
|
|
120
|
+
const privateKey2 = await (await generateKeyPair()).privateKey;
|
|
121
121
|
const publicKey = await getPublicKey(privateKey1);
|
|
122
122
|
const result = await validateKeyPair(publicKey, privateKey2);
|
|
123
123
|
expect(result.isValid).toBe(false);
|
|
124
124
|
expect(result.error).toBe('Public key does not match private key');
|
|
125
125
|
});
|
|
126
126
|
it('should handle invalid private key', async () => {
|
|
127
|
-
const publicKey = await getPublicKey(await generateKeyPair().privateKey);
|
|
127
|
+
const publicKey = await getPublicKey(await (await generateKeyPair()).privateKey);
|
|
128
128
|
const result = await validateKeyPair(publicKey, 'invalid-private-key');
|
|
129
129
|
expect(result.isValid).toBe(false);
|
|
130
130
|
expect(result.error).toBe('Invalid key pair');
|
|
@@ -138,7 +138,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
138
138
|
tags: [['p', '1234']],
|
|
139
139
|
content: 'Hello, World!'
|
|
140
140
|
};
|
|
141
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
141
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
142
142
|
const signedEvent = await signEvent(event, privateKey);
|
|
143
143
|
expect(signedEvent.sig).toBeTruthy();
|
|
144
144
|
expect(signedEvent.id).toBeTruthy();
|
|
@@ -151,7 +151,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
151
151
|
content: 'Hello, World!',
|
|
152
152
|
tags: [] // Adding the required tags property
|
|
153
153
|
};
|
|
154
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
154
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
155
155
|
const signedEvent = await signEvent(event, privateKey);
|
|
156
156
|
expect(signedEvent.sig).toBeTruthy();
|
|
157
157
|
expect(signedEvent.id).toBeTruthy();
|
|
@@ -166,7 +166,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
166
166
|
content: 'Hello, World!',
|
|
167
167
|
tags: [] // Add empty tags array to satisfy NostrEvent interface
|
|
168
168
|
};
|
|
169
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
169
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
170
170
|
const signedEvent = await signEvent(event, privateKey);
|
|
171
171
|
expect(signedEvent.tags).toEqual([]);
|
|
172
172
|
});
|
|
@@ -177,7 +177,7 @@ describe('NOSTR Crypto Utils', () => {
|
|
|
177
177
|
tags: [],
|
|
178
178
|
created_at: Math.floor(Date.now() / 1000)
|
|
179
179
|
};
|
|
180
|
-
const privateKey = await generateKeyPair().privateKey;
|
|
180
|
+
const privateKey = await (await generateKeyPair()).privateKey;
|
|
181
181
|
const signedEvent = await signEvent(event, privateKey);
|
|
182
182
|
expect(signedEvent.created_at).toBeDefined();
|
|
183
183
|
expect(typeof signedEvent.created_at).toBe('number');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { formatEventForRelay, formatSubscriptionForRelay, formatCloseForRelay, formatAuthForRelay, parseNostrMessage, createMetadataEvent, createTextNoteEvent, createDirectMessageEvent, createChannelMessageEvent, extractReferencedEvents, extractMentionedPubkeys, createKindFilter, createAuthorFilter, createReplyFilter } from '../integration';
|
|
2
2
|
import { NOSTR_KIND } from '../constants';
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
3
4
|
describe('Integration Utilities', () => {
|
|
4
5
|
const mockSignedEvent = {
|
|
5
6
|
id: '123',
|
|
@@ -36,20 +37,31 @@ describe('Integration Utilities', () => {
|
|
|
36
37
|
it('should parse EVENT message', () => {
|
|
37
38
|
const message = ['EVENT', mockSignedEvent];
|
|
38
39
|
const parsed = parseNostrMessage(message);
|
|
39
|
-
expect(parsed
|
|
40
|
-
|
|
40
|
+
expect(parsed).not.toBeNull();
|
|
41
|
+
if (parsed) {
|
|
42
|
+
expect(parsed.type).toBe('EVENT');
|
|
43
|
+
expect(parsed.payload).toEqual(mockSignedEvent);
|
|
44
|
+
}
|
|
41
45
|
});
|
|
42
46
|
it('should parse NOTICE message', () => {
|
|
43
47
|
const message = ['NOTICE', 'test message'];
|
|
44
48
|
const parsed = parseNostrMessage(message);
|
|
45
|
-
expect(parsed
|
|
46
|
-
|
|
49
|
+
expect(parsed).not.toBeNull();
|
|
50
|
+
if (parsed) {
|
|
51
|
+
expect(parsed.type).toBe('NOTICE');
|
|
52
|
+
expect(parsed.payload).toBe('test message');
|
|
53
|
+
}
|
|
47
54
|
});
|
|
48
55
|
it('should parse OK message', () => {
|
|
49
56
|
const message = ['OK', 'event1', true, 'success'];
|
|
50
57
|
const parsed = parseNostrMessage(message);
|
|
51
|
-
expect(parsed
|
|
52
|
-
|
|
58
|
+
expect(parsed).not.toBeNull();
|
|
59
|
+
if (parsed) {
|
|
60
|
+
expect(parsed.type).toBe('OK');
|
|
61
|
+
if (parsed.payload) {
|
|
62
|
+
expect(parsed.payload).toEqual(['event1', true, 'success']);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
53
65
|
});
|
|
54
66
|
it('should throw error for invalid message', () => {
|
|
55
67
|
expect(() => parseNostrMessage('invalid')).toThrow('Invalid relay message: not an array');
|
|
@@ -1,104 +1,105 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
|
+
import { generateKeyPair, createEvent, signEvent, encrypt, decrypt, NostrEventKind, formatSubscriptionForRelay, formatEventForRelay, formatCloseForRelay } from '../index';
|
|
2
3
|
describe('NIP-01: Basic Protocol Flow', () => {
|
|
3
4
|
let keyPair;
|
|
4
5
|
beforeAll(async () => {
|
|
5
6
|
keyPair = await generateKeyPair();
|
|
6
7
|
});
|
|
7
|
-
|
|
8
|
+
it('Event Creation and Signing Flow', async () => {
|
|
8
9
|
// Create a text note (kind 1) as specified in NIP-01
|
|
9
10
|
const event = createEvent({
|
|
10
11
|
kind: NostrEventKind.TEXT_NOTE,
|
|
11
|
-
content: 'Hello Nostr!',
|
|
12
|
-
tags: []
|
|
13
|
-
created_at: Math.floor(Date.now() / 1000)
|
|
12
|
+
content: 'Hello, Nostr!',
|
|
13
|
+
tags: []
|
|
14
14
|
});
|
|
15
15
|
// Sign the event
|
|
16
16
|
const signedEvent = await signEvent(event, keyPair.privateKey);
|
|
17
|
-
// Verify event structure
|
|
18
|
-
|
|
19
|
-
expect(
|
|
17
|
+
// Verify the signed event structure
|
|
18
|
+
expect(signedEvent.id).toBeDefined();
|
|
19
|
+
expect(signedEvent.sig).toBeDefined();
|
|
20
20
|
expect(signedEvent.pubkey).toBe(keyPair.publicKey);
|
|
21
|
-
expect(
|
|
21
|
+
expect(signedEvent.kind).toBe(NostrEventKind.TEXT_NOTE);
|
|
22
|
+
expect(signedEvent.content).toBe('Hello, Nostr!');
|
|
23
|
+
expect(signedEvent.created_at).toBeDefined();
|
|
22
24
|
expect(Array.isArray(signedEvent.tags)).toBe(true);
|
|
23
25
|
});
|
|
24
|
-
|
|
26
|
+
it('Client-Relay Message Format', async () => {
|
|
25
27
|
// Test REQ message format
|
|
26
|
-
const reqMessage = formatSubscriptionForRelay({
|
|
28
|
+
const reqMessage = formatSubscriptionForRelay({
|
|
29
|
+
id: 'test_sub',
|
|
30
|
+
filters: [{ kinds: [1], limit: 10 }]
|
|
31
|
+
});
|
|
27
32
|
expect(reqMessage[0]).toBe('REQ');
|
|
28
|
-
expect(
|
|
29
|
-
expect(reqMessage
|
|
33
|
+
expect(reqMessage[1]).toBe('test_sub');
|
|
34
|
+
expect(reqMessage[2]).toEqual({ kinds: [1], limit: 10 });
|
|
30
35
|
// Test EVENT message format
|
|
31
|
-
const
|
|
36
|
+
const keyPair = await generateKeyPair();
|
|
37
|
+
const event = createEvent({
|
|
32
38
|
kind: NostrEventKind.TEXT_NOTE,
|
|
33
|
-
content: '
|
|
39
|
+
content: 'Test message',
|
|
34
40
|
tags: []
|
|
35
41
|
});
|
|
36
|
-
const signedEvent = await signEvent(
|
|
42
|
+
const signedEvent = await signEvent(event, keyPair.privateKey);
|
|
37
43
|
const eventMessage = formatEventForRelay(signedEvent);
|
|
38
44
|
expect(eventMessage[0]).toBe('EVENT');
|
|
39
|
-
expect(eventMessage[1]).
|
|
40
|
-
kind: NostrEventKind.TEXT_NOTE,
|
|
41
|
-
content: 'test message'
|
|
42
|
-
});
|
|
45
|
+
expect(eventMessage[1]).toEqual(signedEvent);
|
|
43
46
|
// Test CLOSE message format
|
|
44
|
-
const closeMessage =
|
|
45
|
-
|
|
46
|
-
expect(
|
|
47
|
-
expect(closeParsed[1]).toBe('subscription_id');
|
|
47
|
+
const closeMessage = formatCloseForRelay('test_sub');
|
|
48
|
+
expect(closeMessage[0]).toBe('CLOSE');
|
|
49
|
+
expect(closeMessage[1]).toBe('test_sub');
|
|
48
50
|
});
|
|
49
51
|
});
|
|
50
52
|
describe('NIP-02: Contact List', () => {
|
|
51
|
-
|
|
53
|
+
it('Contact List Event Structure', async () => {
|
|
52
54
|
const keyPair = await generateKeyPair();
|
|
53
55
|
const contacts = createEvent({
|
|
54
56
|
kind: NostrEventKind.CONTACTS,
|
|
55
|
-
content: JSON.stringify(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
petname: 'friend'
|
|
60
|
-
}
|
|
61
|
-
]),
|
|
62
|
-
tags: [],
|
|
57
|
+
content: JSON.stringify({
|
|
58
|
+
name: 'Alice',
|
|
59
|
+
about: 'I love Nostr!'
|
|
60
|
+
}),
|
|
63
61
|
created_at: Math.floor(Date.now() / 1000)
|
|
64
62
|
});
|
|
65
|
-
const
|
|
66
|
-
expect(
|
|
67
|
-
expect(
|
|
63
|
+
const signedContacts = await signEvent(contacts, keyPair.privateKey);
|
|
64
|
+
expect(signedContacts).toHaveProperty('id');
|
|
65
|
+
expect(signedContacts).toHaveProperty('sig');
|
|
66
|
+
// Verify the event structure
|
|
67
|
+
expect(signedContacts.kind).toBe(NostrEventKind.CONTACTS);
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
70
|
describe('NIP-04: Encrypted Direct Messages', () => {
|
|
71
|
-
|
|
71
|
+
let keyPair;
|
|
72
|
+
beforeAll(async () => {
|
|
73
|
+
keyPair = await generateKeyPair();
|
|
74
|
+
});
|
|
75
|
+
it('Message Encryption and Decryption', async () => {
|
|
72
76
|
const alice = await generateKeyPair();
|
|
73
77
|
const bob = await generateKeyPair();
|
|
74
78
|
const message = 'Secret message for testing';
|
|
75
79
|
// Encrypt message from Alice to Bob
|
|
76
80
|
const encrypted = await encrypt(message, bob.publicKey, alice.privateKey);
|
|
77
81
|
expect(encrypted).not.toBe(message);
|
|
78
|
-
//
|
|
82
|
+
// Decrypt message
|
|
79
83
|
const decrypted = await decrypt(encrypted, alice.publicKey, bob.privateKey);
|
|
80
84
|
expect(decrypted).toBe(message);
|
|
81
85
|
});
|
|
82
|
-
|
|
83
|
-
const sender = await generateKeyPair();
|
|
86
|
+
it('Encrypted DM Event Structure', async () => {
|
|
84
87
|
const recipient = await generateKeyPair();
|
|
85
|
-
const message = 'Secret message';
|
|
86
|
-
const encrypted = await encrypt(message, recipient.publicKey, sender.privateKey);
|
|
87
88
|
const dmEvent = createEvent({
|
|
88
89
|
kind: NostrEventKind.ENCRYPTED_DIRECT_MESSAGE,
|
|
89
|
-
content:
|
|
90
|
-
tags: [['p', recipient.publicKey]]
|
|
91
|
-
created_at: Math.floor(Date.now() / 1000)
|
|
90
|
+
content: 'Encrypted message',
|
|
91
|
+
tags: [['p', recipient.publicKey]]
|
|
92
92
|
});
|
|
93
|
-
const
|
|
94
|
-
expect(
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
97
|
-
expect(
|
|
93
|
+
const signedDM = await signEvent(dmEvent, keyPair.privateKey);
|
|
94
|
+
expect(signedDM).toHaveProperty('id');
|
|
95
|
+
expect(signedDM).toHaveProperty('sig');
|
|
96
|
+
expect(signedDM.kind).toBe(NostrEventKind.ENCRYPTED_DIRECT_MESSAGE);
|
|
97
|
+
expect(signedDM.tags[0][0]).toBe('p');
|
|
98
|
+
expect(signedDM.tags[0][1]).toBe(recipient.publicKey);
|
|
98
99
|
});
|
|
99
100
|
});
|
|
100
101
|
describe('NIP-09: Event Deletion', () => {
|
|
101
|
-
|
|
102
|
+
it('Event Deletion Structure', async () => {
|
|
102
103
|
const keyPair = await generateKeyPair();
|
|
103
104
|
const originalEvent = await signEvent(createEvent({
|
|
104
105
|
kind: NostrEventKind.TEXT_NOTE,
|
|
@@ -112,8 +113,10 @@ describe('NIP-09: Event Deletion', () => {
|
|
|
112
113
|
tags: [['e', originalEvent.id]],
|
|
113
114
|
created_at: Math.floor(Date.now() / 1000)
|
|
114
115
|
});
|
|
115
|
-
const
|
|
116
|
-
expect(
|
|
116
|
+
const signedDeleteEvent = await signEvent(deleteEvent, keyPair.privateKey);
|
|
117
|
+
expect(signedDeleteEvent).toHaveProperty('id');
|
|
118
|
+
expect(signedDeleteEvent).toHaveProperty('sig');
|
|
119
|
+
// Verify the event structure
|
|
117
120
|
expect(deleteEvent.kind).toBe(NostrEventKind.DELETE);
|
|
118
121
|
expect(deleteEvent.tags[0][0]).toBe('e');
|
|
119
122
|
expect(deleteEvent.tags[0][1]).toBe(originalEvent.id);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|