ehbp 0.0.3 → 0.0.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.
Files changed (69) hide show
  1. package/dist/cjs/client.d.ts +51 -0
  2. package/dist/cjs/client.d.ts.map +1 -0
  3. package/dist/cjs/client.js +160 -0
  4. package/dist/cjs/client.js.map +1 -0
  5. package/dist/cjs/identity.d.ts +52 -0
  6. package/dist/cjs/identity.d.ts.map +1 -0
  7. package/dist/cjs/identity.js +275 -0
  8. package/dist/cjs/identity.js.map +1 -0
  9. package/{src/index.ts → dist/cjs/index.d.ts} +2 -4
  10. package/dist/cjs/index.d.ts.map +1 -0
  11. package/dist/cjs/index.js +19 -0
  12. package/dist/cjs/index.js.map +1 -0
  13. package/dist/cjs/package.json +1 -0
  14. package/dist/cjs/protocol.d.ts +19 -0
  15. package/dist/cjs/protocol.d.ts.map +1 -0
  16. package/dist/cjs/protocol.js +22 -0
  17. package/dist/cjs/protocol.js.map +1 -0
  18. package/dist/esm/client.d.ts +51 -0
  19. package/dist/esm/client.d.ts.map +1 -0
  20. package/dist/esm/client.js +155 -0
  21. package/dist/esm/client.js.map +1 -0
  22. package/dist/esm/example.d.ts +6 -0
  23. package/dist/esm/example.d.ts.map +1 -0
  24. package/dist/esm/example.js +115 -0
  25. package/dist/esm/example.js.map +1 -0
  26. package/dist/esm/identity.d.ts +52 -0
  27. package/dist/esm/identity.d.ts.map +1 -0
  28. package/dist/esm/identity.js +271 -0
  29. package/dist/esm/identity.js.map +1 -0
  30. package/dist/esm/index.d.ts +12 -0
  31. package/dist/esm/index.d.ts.map +1 -0
  32. package/dist/esm/index.js +11 -0
  33. package/dist/esm/index.js.map +1 -0
  34. package/dist/esm/package.json +1 -0
  35. package/dist/esm/protocol.d.ts +19 -0
  36. package/dist/esm/protocol.d.ts.map +1 -0
  37. package/dist/esm/protocol.js +19 -0
  38. package/dist/esm/protocol.js.map +1 -0
  39. package/dist/esm/streaming-test.d.ts +3 -0
  40. package/dist/esm/streaming-test.d.ts.map +1 -0
  41. package/dist/esm/streaming-test.js +102 -0
  42. package/dist/esm/streaming-test.js.map +1 -0
  43. package/dist/esm/test/client.test.d.ts +2 -0
  44. package/dist/esm/test/client.test.d.ts.map +1 -0
  45. package/dist/esm/test/client.test.js +71 -0
  46. package/dist/esm/test/client.test.js.map +1 -0
  47. package/dist/esm/test/identity.test.d.ts +2 -0
  48. package/dist/esm/test/identity.test.d.ts.map +1 -0
  49. package/dist/esm/test/identity.test.js +39 -0
  50. package/dist/esm/test/identity.test.js.map +1 -0
  51. package/dist/esm/test/streaming.test.d.ts +2 -0
  52. package/dist/esm/test/streaming.test.d.ts.map +1 -0
  53. package/dist/esm/test/streaming.test.js +71 -0
  54. package/dist/esm/test/streaming.test.js.map +1 -0
  55. package/package.json +7 -2
  56. package/build-browser.js +0 -54
  57. package/chat.html +0 -285
  58. package/src/client.ts +0 -181
  59. package/src/example.ts +0 -126
  60. package/src/identity.ts +0 -339
  61. package/src/protocol.ts +0 -19
  62. package/src/streaming-test.ts +0 -118
  63. package/src/test/client.test.ts +0 -93
  64. package/src/test/identity.test.ts +0 -46
  65. package/src/test/streaming.test.ts +0 -85
  66. package/test.html +0 -271
  67. package/tsconfig.cjs.json +0 -8
  68. package/tsconfig.esm.json +0 -7
  69. package/tsconfig.json +0 -19
package/src/example.ts DELETED
@@ -1,126 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Example usage of the EHBP JavaScript client
5
- */
6
-
7
- import { Identity, createTransport } from './index.js';
8
-
9
- async function main() {
10
- console.log('EHBP JavaScript Client Example');
11
- console.log('==============================');
12
-
13
- try {
14
- // Create client identity
15
- console.log('Creating client identity...');
16
- const clientIdentity = await Identity.generate();
17
- console.log('Client public key:', await clientIdentity.getPublicKeyHex());
18
-
19
- // Create transport (this will fetch server public key)
20
- console.log('Creating transport...');
21
- const serverURL = 'http://localhost:8080'; // Adjust as needed
22
- const transport = await createTransport(serverURL, clientIdentity);
23
- console.log('Transport created successfully');
24
-
25
- // Example 1: GET request to secure endpoint
26
- console.log('\n--- GET Request ---');
27
- try {
28
- const getResponse = await transport.get(`${serverURL}/secure`);
29
- console.log('GET Response status:', getResponse.status);
30
- if (getResponse.ok) {
31
- const getData = await getResponse.text();
32
- console.log('GET Response:', getData);
33
- } else {
34
- console.log('GET Request failed with status:', getResponse.status);
35
- }
36
- } catch (error) {
37
- console.log('GET Request failed:', error instanceof Error ? error.message : String(error));
38
- }
39
-
40
- // Example 2: POST request with JSON data
41
- console.log('\n--- POST Request ---');
42
- try {
43
- const postData = { message: 'Hello from JavaScript client!', timestamp: new Date().toISOString() };
44
- const postResponse = await transport.post(
45
- `${serverURL}/secure`,
46
- JSON.stringify(postData),
47
- { headers: { 'Content-Type': 'application/json' } }
48
- );
49
- console.log('POST Response status:', postResponse.status);
50
- if (postResponse.ok) {
51
- const responseData = await postResponse.text();
52
- console.log('POST Response:', responseData);
53
- } else {
54
- console.log('POST Request failed with status:', postResponse.status);
55
- }
56
- } catch (error) {
57
- console.log('POST Request failed:', error instanceof Error ? error.message : String(error));
58
- }
59
-
60
- // Example 3: PUT request
61
- console.log('\n--- PUT Request ---');
62
- try {
63
- const putData = { id: 1, name: 'Updated Item' };
64
- const putResponse = await transport.put(
65
- `${serverURL}/secure`,
66
- JSON.stringify(putData),
67
- { headers: { 'Content-Type': 'application/json' } }
68
- );
69
- console.log('PUT Response status:', putResponse.status);
70
- if (putResponse.ok) {
71
- const putResponseData = await putResponse.text();
72
- console.log('PUT Response:', putResponseData);
73
- } else {
74
- console.log('PUT Request failed with status:', putResponse.status);
75
- }
76
- } catch (error) {
77
- console.log('PUT Request failed:', error instanceof Error ? error.message : String(error));
78
- }
79
-
80
- // Example 4: Streaming request
81
- console.log('\n--- Streaming Request ---');
82
- try {
83
- const streamResponse = await transport.get(`${serverURL}/stream`);
84
- console.log('Stream Response status:', streamResponse.status);
85
- if (streamResponse.ok) {
86
- console.log('Streaming response (should show numbers 1-20):');
87
- const reader = streamResponse.body?.getReader();
88
- if (reader) {
89
- const decoder = new TextDecoder();
90
- let chunkCount = 0;
91
-
92
- while (true) {
93
- const { done, value } = await reader.read();
94
- if (done) break;
95
-
96
- const text = decoder.decode(value, { stream: true });
97
- process.stdout.write(text);
98
- chunkCount++;
99
- }
100
-
101
- console.log(`\nStream completed with ${chunkCount} chunks`);
102
- } else {
103
- console.log('No readable stream available');
104
- }
105
- } else {
106
- console.log('Stream Request failed with status:', streamResponse.status);
107
- }
108
- } catch (error) {
109
- console.log('Stream Request failed:', error instanceof Error ? error.message : String(error));
110
- }
111
-
112
- console.log('\nExample completed successfully!');
113
- console.log('\nTo test with a real server:');
114
- console.log('1. Start the Go server: go run pkg/server/main.go');
115
- console.log('2. Run this example: npm run example');
116
-
117
- } catch (error) {
118
- console.error('Error:', error);
119
- process.exit(1);
120
- }
121
- }
122
-
123
- // Run the example
124
- if (import.meta.url === `file://${process.argv[1]}`) {
125
- main().catch(console.error);
126
- }
package/src/identity.ts DELETED
@@ -1,339 +0,0 @@
1
- import { CipherSuite, DhkemX25519HkdfSha256, HkdfSha256, Aes256Gcm } from '@hpke/core';
2
- import { PROTOCOL, HPKE_CONFIG } from './protocol.js';
3
-
4
- /**
5
- * Identity class for managing HPKE key pairs and encryption/decryption
6
- */
7
- export class Identity {
8
- private suite: CipherSuite;
9
- private publicKey: CryptoKey;
10
- private privateKey: CryptoKey;
11
-
12
- constructor(suite: CipherSuite, publicKey: CryptoKey, privateKey: CryptoKey) {
13
- this.suite = suite;
14
- this.publicKey = publicKey;
15
- this.privateKey = privateKey;
16
- }
17
-
18
- /**
19
- * Generate a new identity with X25519 key pair
20
- */
21
- static async generate(): Promise<Identity> {
22
- const suite = new CipherSuite({
23
- kem: new DhkemX25519HkdfSha256(),
24
- kdf: new HkdfSha256(),
25
- aead: new Aes256Gcm()
26
- });
27
-
28
- const { publicKey, privateKey } = await suite.kem.generateKeyPair();
29
-
30
- // Make sure the public key is extractable for serialization
31
- const extractablePublicKey = await crypto.subtle.importKey(
32
- 'raw',
33
- await crypto.subtle.exportKey('raw', publicKey),
34
- { name: 'X25519' },
35
- true, // extractable
36
- []
37
- );
38
-
39
- return new Identity(suite, extractablePublicKey, privateKey);
40
- }
41
-
42
-
43
- /**
44
- * Create identity from JSON string
45
- */
46
- static async fromJSON(json: string): Promise<Identity> {
47
- const data = JSON.parse(json);
48
- const suite = new CipherSuite({
49
- kem: new DhkemX25519HkdfSha256(),
50
- kdf: new HkdfSha256(),
51
- aead: new Aes256Gcm()
52
- });
53
-
54
- // Import public key
55
- const publicKey = await crypto.subtle.importKey(
56
- 'raw',
57
- new Uint8Array(data.publicKey),
58
- { name: 'X25519' },
59
- true, // extractable
60
- []
61
- );
62
-
63
- // Deserialize private key using HPKE library
64
- const privateKey = await suite.kem.deserializePrivateKey(new Uint8Array(data.privateKey).buffer);
65
-
66
- return new Identity(suite, publicKey, privateKey);
67
- }
68
-
69
-
70
- /**
71
- * Convert identity to JSON string
72
- */
73
- async toJSON(): Promise<string> {
74
- const publicKeyBytes = new Uint8Array(await crypto.subtle.exportKey('raw', this.publicKey));
75
-
76
- // For X25519, we need to use the HPKE library's serialization for private keys
77
- const privateKeyBytes = await this.suite.kem.serializePrivateKey(this.privateKey);
78
-
79
- return JSON.stringify({
80
- publicKey: Array.from(publicKeyBytes),
81
- privateKey: Array.from(new Uint8Array(privateKeyBytes))
82
- });
83
- }
84
-
85
- /**
86
- * Get public key as CryptoKey
87
- */
88
- getPublicKey(): CryptoKey {
89
- return this.publicKey;
90
- }
91
-
92
- /**
93
- * Get public key as hex string
94
- */
95
- async getPublicKeyHex(): Promise<string> {
96
- const exported = await crypto.subtle.exportKey('raw', this.publicKey);
97
- return Array.from(new Uint8Array(exported))
98
- .map(b => b.toString(16).padStart(2, '0'))
99
- .join('');
100
- }
101
-
102
- /**
103
- * Get private key as CryptoKey
104
- */
105
- getPrivateKey(): CryptoKey {
106
- return this.privateKey;
107
- }
108
-
109
- /**
110
- * Marshal public key configuration for server key distribution
111
- * Implements RFC 9458 format
112
- */
113
- async marshalConfig(): Promise<Uint8Array> {
114
- const kemId = HPKE_CONFIG.KEM;
115
- const kdfId = HPKE_CONFIG.KDF;
116
- const aeadId = HPKE_CONFIG.AEAD;
117
-
118
- // Export public key as raw bytes
119
- const publicKeyBytes = new Uint8Array(await crypto.subtle.exportKey('raw', this.publicKey));
120
-
121
- // Key ID (1 byte) + KEM ID (2 bytes) + Public Key + Cipher Suites
122
- const keyId = 0;
123
- const publicKeySize = publicKeyBytes.length;
124
- const cipherSuitesSize = 2 + 2; // KDF ID + AEAD ID
125
-
126
- const buffer = new Uint8Array(1 + 2 + publicKeySize + 2 + cipherSuitesSize);
127
- let offset = 0;
128
-
129
- // Key ID
130
- buffer[offset++] = keyId;
131
-
132
- // KEM ID
133
- buffer[offset++] = (kemId >> 8) & 0xFF;
134
- buffer[offset++] = kemId & 0xFF;
135
-
136
- // Public Key
137
- buffer.set(publicKeyBytes, offset);
138
- offset += publicKeySize;
139
-
140
- // Cipher Suites Length (2 bytes)
141
- buffer[offset++] = (cipherSuitesSize >> 8) & 0xFF;
142
- buffer[offset++] = cipherSuitesSize & 0xFF;
143
-
144
- // KDF ID
145
- buffer[offset++] = (kdfId >> 8) & 0xFF;
146
- buffer[offset++] = kdfId & 0xFF;
147
-
148
- // AEAD ID
149
- buffer[offset++] = (aeadId >> 8) & 0xFF;
150
- buffer[offset++] = aeadId & 0xFF;
151
-
152
- return buffer;
153
- }
154
-
155
- /**
156
- * Unmarshal public configuration from server
157
- */
158
- static async unmarshalPublicConfig(data: Uint8Array): Promise<Identity> {
159
- let offset = 0;
160
-
161
- // Read Key ID
162
- const keyId = data[offset++];
163
-
164
- // Read KEM ID
165
- const kemId = (data[offset++] << 8) | data[offset++];
166
-
167
- // Read Public Key (32 bytes for X25519)
168
- const publicKeySize = 32;
169
- const publicKeyBytes = data.slice(offset, offset + publicKeySize);
170
- offset += publicKeySize;
171
-
172
- // Read Cipher Suites Length
173
- const cipherSuitesLength = (data[offset++] << 8) | data[offset++];
174
-
175
- // Read KDF ID
176
- const kdfId = (data[offset++] << 8) | data[offset++];
177
-
178
- // Read AEAD ID
179
- const aeadId = (data[offset++] << 8) | data[offset++];
180
-
181
- // Create suite (assuming X25519 for now)
182
- const suite = new CipherSuite({
183
- kem: new DhkemX25519HkdfSha256(),
184
- kdf: new HkdfSha256(),
185
- aead: new Aes256Gcm()
186
- });
187
-
188
- // Import public key using HPKE library
189
- const publicKey = await suite.kem.deserializePublicKey(publicKeyBytes.buffer);
190
-
191
- // For server config, we only have the public key, no private key
192
- // We'll create a dummy private key that won't be used
193
- const dummyPrivateKey = await suite.kem.deserializePrivateKey(new Uint8Array(32).buffer);
194
-
195
- return new Identity(suite, publicKey, dummyPrivateKey);
196
- }
197
-
198
- /**
199
- * Encrypt request body and set appropriate headers
200
- */
201
- async encryptRequest(request: Request, serverPublicKey: CryptoKey): Promise<Request> {
202
- const body = await request.arrayBuffer();
203
- if (body.byteLength === 0) {
204
- // No body to encrypt, just set client public key header
205
- const headers = new Headers(request.headers);
206
- headers.set(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER, await this.getPublicKeyHex());
207
- return new Request(request.url, {
208
- method: request.method,
209
- headers,
210
- body: null
211
- });
212
- }
213
-
214
- // Create sender for encryption
215
- const sender = await this.suite.createSenderContext({
216
- recipientPublicKey: serverPublicKey
217
- });
218
-
219
- // Encrypt the body
220
- const encrypted = await sender.seal(body);
221
-
222
- // Get encapsulated key
223
- const encapKey = sender.enc;
224
-
225
- // Create chunked format: 4-byte length header + encrypted data
226
- const chunkLength = new Uint8Array(4);
227
- const view = new DataView(chunkLength.buffer);
228
- view.setUint32(0, encrypted.byteLength, false); // Big-endian
229
-
230
- const chunkedData = new Uint8Array(4 + encrypted.byteLength);
231
- chunkedData.set(chunkLength, 0);
232
- chunkedData.set(new Uint8Array(encrypted), 4);
233
-
234
- // Create new request with encrypted body and headers
235
- const headers = new Headers(request.headers);
236
- headers.set(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER, await this.getPublicKeyHex());
237
- headers.set(PROTOCOL.ENCAPSULATED_KEY_HEADER, Array.from(new Uint8Array(encapKey))
238
- .map(b => b.toString(16).padStart(2, '0'))
239
- .join(''));
240
-
241
- return new Request(request.url, {
242
- method: request.method,
243
- headers,
244
- body: chunkedData,
245
- duplex: 'half'
246
- } as RequestInit);
247
- }
248
-
249
- /**
250
- * Decrypt response body
251
- */
252
- async decryptResponse(response: Response, serverEncapKey: Uint8Array): Promise<Response> {
253
- if (!response.body) {
254
- return response;
255
- }
256
-
257
- // Create receiver for decryption
258
- const receiver = await this.suite.createRecipientContext({
259
- recipientKey: this.privateKey,
260
- enc: serverEncapKey.buffer as ArrayBuffer
261
- });
262
-
263
- // Create a readable stream that decrypts chunks as they arrive
264
- const decryptedStream = new ReadableStream({
265
- start(controller) {
266
- const reader = response.body!.getReader();
267
- let buffer = new Uint8Array(0);
268
- let offset = 0;
269
-
270
- async function pump() {
271
- try {
272
- while (true) {
273
- const { done, value } = await reader.read();
274
- if (done) break;
275
-
276
- // Append new data to buffer
277
- const newBuffer = new Uint8Array(buffer.length + value.length);
278
- newBuffer.set(buffer);
279
- newBuffer.set(value, buffer.length);
280
- buffer = newBuffer;
281
-
282
- // Process complete chunks
283
- while (offset + 4 <= buffer.length) {
284
- // Read chunk length (4 bytes big-endian)
285
- const chunkLength = (buffer[offset] << 24) |
286
- (buffer[offset + 1] << 16) |
287
- (buffer[offset + 2] << 8) |
288
- buffer[offset + 3];
289
- offset += 4;
290
-
291
- if (chunkLength === 0) {
292
- continue; // Empty chunk
293
- }
294
-
295
- // Check if we have the complete chunk
296
- if (offset + chunkLength > buffer.length) {
297
- // Not enough data yet, wait for more
298
- break;
299
- }
300
-
301
- // Extract and decrypt the chunk
302
- const encryptedChunk = buffer.slice(offset, offset + chunkLength);
303
- offset += chunkLength;
304
-
305
- try {
306
- const decryptedChunk = await receiver.open(encryptedChunk.buffer);
307
- controller.enqueue(new Uint8Array(decryptedChunk));
308
- } catch (error) {
309
- controller.error(new Error(`Failed to decrypt chunk: ${error}`));
310
- return;
311
- }
312
- }
313
-
314
- // Remove processed data from buffer
315
- if (offset > 0) {
316
- buffer = buffer.slice(offset);
317
- offset = 0;
318
- }
319
- }
320
-
321
- controller.close();
322
- } catch (error) {
323
- controller.error(error);
324
- }
325
- }
326
-
327
- pump();
328
- }
329
- });
330
-
331
- // Create new response with decrypted stream
332
- return new Response(decryptedStream, {
333
- status: response.status,
334
- statusText: response.statusText,
335
- headers: response.headers
336
- });
337
- }
338
-
339
- }
package/src/protocol.ts DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * Protocol constants for EHBP (Encrypted HTTP Body Protocol)
3
- */
4
- export const PROTOCOL = {
5
- ENCAPSULATED_KEY_HEADER: 'Ehbp-Encapsulated-Key',
6
- CLIENT_PUBLIC_KEY_HEADER: 'Ehbp-Client-Public-Key',
7
- KEYS_MEDIA_TYPE: 'application/ohttp-keys',
8
- KEYS_PATH: '/.well-known/hpke-keys',
9
- FALLBACK_HEADER: 'Ehbp-Fallback'
10
- } as const;
11
-
12
- /**
13
- * HPKE suite configuration matching the Go implementation
14
- */
15
- export const HPKE_CONFIG = {
16
- KEM: 0x0020, // X25519 HKDF SHA256
17
- KDF: 0x0001, // HKDF SHA256
18
- AEAD: 0x0002 // AES-256-GCM
19
- } as const;
@@ -1,118 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Identity, createTransport } from './index.js';
4
-
5
- async function streamingTest() {
6
- console.log('EHBP Streaming Test');
7
- console.log('===================');
8
-
9
- try {
10
- // Create client identity
11
- console.log('Creating client identity...');
12
- const clientIdentity = await Identity.generate();
13
- console.log('Client identity created');
14
-
15
- // Create transport
16
- console.log('Creating transport...');
17
- const serverURL = 'http://localhost:8080';
18
- const transport = await createTransport(serverURL, clientIdentity);
19
- console.log('Transport created');
20
-
21
- // Test 1: Basic streaming request
22
- console.log('\n--- Test 1: Basic Streaming ---');
23
- const streamResponse = await transport.get(`${serverURL}/stream`);
24
- console.log('Stream request sent, status:', streamResponse.status);
25
-
26
- if (streamResponse.ok) {
27
- console.log('Reading stream data...');
28
- const reader = streamResponse.body?.getReader();
29
- if (reader) {
30
- const decoder = new TextDecoder();
31
-
32
- while (true) {
33
- const { done, value } = await reader.read();
34
- if (done) break;
35
-
36
- const text = decoder.decode(value, { stream: true });
37
- process.stdout.write(text);
38
- }
39
-
40
- console.log('\nStream completed');
41
- } else {
42
- console.log('No readable stream available');
43
- }
44
- } else {
45
- console.log('Stream request failed with status:', streamResponse.status);
46
- }
47
-
48
- // Test 2: Multiple concurrent streams
49
- console.log('\n--- Test 2: Concurrent Streams ---');
50
- const concurrentStreams = 3;
51
- const streamPromises = [];
52
-
53
- for (let i = 0; i < concurrentStreams; i++) {
54
- streamPromises.push(
55
- (async (streamId: number) => {
56
- const response = await transport.get(`${serverURL}/stream`);
57
- if (response.ok && response.body) {
58
- const reader = response.body.getReader();
59
- const decoder = new TextDecoder();
60
-
61
- while (true) {
62
- const { done, value } = await reader.read();
63
- if (done) break;
64
-
65
- const text = decoder.decode(value, { stream: true });
66
-
67
- // Prefix with stream ID to show concurrency
68
- process.stdout.write(`[Stream ${streamId}] ${text}`);
69
- }
70
-
71
- return { streamId };
72
- }
73
- return { streamId };
74
- })(i + 1)
75
- );
76
- }
77
-
78
- const results = await Promise.all(streamPromises);
79
- console.log('\nConcurrent streams completed:');
80
- results.forEach(result => {
81
- console.log(` - Stream ${result.streamId}: completed`);
82
- });
83
-
84
- // Test 3: Large data streaming (if server supports it)
85
- console.log('\n--- Test 3: Large Data Stream ---');
86
- try {
87
- const largeStreamResponse = await transport.get(`${serverURL}/stream`);
88
- if (largeStreamResponse.ok) {
89
- console.log('Reading large data stream...');
90
- const reader = largeStreamResponse.body?.getReader();
91
- if (reader) {
92
- const decoder = new TextDecoder();
93
-
94
- while (true) {
95
- const { done, value } = await reader.read();
96
- if (done) break;
97
-
98
- const text = decoder.decode(value, { stream: true });
99
- process.stdout.write(text);
100
- }
101
- }
102
- }
103
- } catch (error) {
104
- console.log('Large data stream test failed:', error instanceof Error ? error.message : String(error));
105
- }
106
-
107
- console.log('\nAll streaming tests completed successfully!');
108
-
109
- } catch (error) {
110
- console.error('Streaming test failed:', error);
111
- process.exit(1);
112
- }
113
- }
114
-
115
- // Run the streaming test
116
- if (import.meta.url === `file://${process.argv[1]}`) {
117
- streamingTest().catch(console.error);
118
- }
@@ -1,93 +0,0 @@
1
- import { describe, it, before, after } from 'node:test';
2
- import assert from 'node:assert';
3
- import { Identity, Transport, createTransport } from '../index.js';
4
- import { PROTOCOL } from '../protocol.js';
5
-
6
- describe('Transport', () => {
7
- let clientIdentity: Identity;
8
- let serverIdentity: Identity;
9
-
10
- before(async () => {
11
- clientIdentity = await Identity.generate();
12
- serverIdentity = await Identity.generate();
13
- });
14
-
15
- it('should create transport with server public key', () => {
16
- const transport = new Transport(
17
- clientIdentity,
18
- 'localhost:8080',
19
- serverIdentity.getPublicKey()
20
- );
21
-
22
- assert(transport instanceof Transport, 'Should create transport instance');
23
- });
24
-
25
- it('should encrypt and decrypt request', async () => {
26
- const serverPublicKey = serverIdentity.getPublicKey();
27
- const originalBody = new TextEncoder().encode('Hello, World!');
28
- const request = new Request('http://localhost:8080/test', {
29
- method: 'POST',
30
- body: originalBody
31
- });
32
-
33
- const encryptedRequest = await clientIdentity.encryptRequest(request, serverPublicKey);
34
-
35
- // Check that headers are set
36
- assert(encryptedRequest.headers.get(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER), 'Client public key header should be set');
37
- assert(encryptedRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), 'Encapsulated key header should be set');
38
-
39
- // Check that body is encrypted (different from original)
40
- const encryptedBody = await encryptedRequest.arrayBuffer();
41
- assert(encryptedBody.byteLength > 0, 'Encrypted body should not be empty');
42
- assert(encryptedBody.byteLength !== originalBody.length, 'Encrypted body should have different length');
43
- });
44
-
45
- it('should handle request without body', async () => {
46
- const serverPublicKey = serverIdentity.getPublicKey();
47
- const request = new Request('http://localhost:8080/test', {
48
- method: 'GET'
49
- });
50
-
51
- const encryptedRequest = await clientIdentity.encryptRequest(request, serverPublicKey);
52
-
53
- // Check that only client public key header is set
54
- assert(encryptedRequest.headers.get(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER), 'Client public key header should be set');
55
- assert(!encryptedRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), 'Encapsulated key header should not be set for empty body');
56
- });
57
-
58
- it('should connect to actual server and POST to /secure endpoint', async (t) => {
59
- const serverURL = 'http://localhost:8080';
60
-
61
- try {
62
- const keysResponse = await fetch(`${serverURL}${PROTOCOL.KEYS_PATH}`);
63
- if (!keysResponse.ok) {
64
- t.skip('Server not running at localhost:8080');
65
- return;
66
- }
67
- } catch (error) {
68
- t.skip('Server not running at localhost:8080');
69
- return;
70
- }
71
-
72
- // Create transport that will connect to the real server
73
- const transport = await createTransport(serverURL, clientIdentity);
74
-
75
- const testName = 'Integration Test User';
76
-
77
- const serverPubKeyHex = await transport.getServerPublicKeyHex();
78
- assert.strictEqual(serverPubKeyHex.length, 64, 'Server public key should be 64 bytes');
79
-
80
- // Make actual POST request to /secure endpoint
81
- const response = await transport.post(`${serverURL}/secure`, testName, {
82
- headers: { 'Content-Type': 'text/plain' }
83
- });
84
-
85
- // Verify response
86
- assert(response.ok, `Response should be ok, got status: ${response.status}`);
87
-
88
- const responseText = await response.text();
89
- assert.strictEqual(responseText, `Hello, ${testName}`, 'Server should respond with Hello, {name}');
90
-
91
- console.log(`✓ Integration test passed: ${responseText}`);
92
- });
93
- });