ehbp 0.0.7 → 0.1.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.
Files changed (62) hide show
  1. package/README.md +84 -138
  2. package/dist/cjs/client.d.ts +13 -13
  3. package/dist/cjs/client.d.ts.map +1 -1
  4. package/dist/cjs/client.js +32 -52
  5. package/dist/cjs/client.js.map +1 -1
  6. package/dist/cjs/derive.d.ts +63 -0
  7. package/dist/cjs/derive.d.ts.map +1 -0
  8. package/dist/cjs/derive.js +136 -0
  9. package/dist/cjs/derive.js.map +1 -0
  10. package/dist/cjs/identity.d.ts +37 -10
  11. package/dist/cjs/identity.d.ts.map +1 -1
  12. package/dist/cjs/identity.js +152 -146
  13. package/dist/cjs/identity.js.map +1 -1
  14. package/dist/cjs/index.d.ts +4 -1
  15. package/dist/cjs/index.d.ts.map +1 -1
  16. package/dist/cjs/index.js +15 -1
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/protocol.d.ts +1 -1
  19. package/dist/cjs/protocol.js +2 -2
  20. package/dist/cjs/protocol.js.map +1 -1
  21. package/dist/esm/client.d.ts +13 -13
  22. package/dist/esm/client.d.ts.map +1 -1
  23. package/dist/esm/client.js +32 -52
  24. package/dist/esm/client.js.map +1 -1
  25. package/dist/esm/derive.d.ts +63 -0
  26. package/dist/esm/derive.d.ts.map +1 -0
  27. package/dist/esm/derive.js +127 -0
  28. package/dist/esm/derive.js.map +1 -0
  29. package/dist/esm/identity.d.ts +37 -10
  30. package/dist/esm/identity.d.ts.map +1 -1
  31. package/dist/esm/identity.js +152 -146
  32. package/dist/esm/identity.js.map +1 -1
  33. package/dist/esm/index.d.ts +4 -1
  34. package/dist/esm/index.d.ts.map +1 -1
  35. package/dist/esm/index.js +2 -0
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/protocol.d.ts +1 -1
  38. package/dist/esm/protocol.js +2 -2
  39. package/dist/esm/protocol.js.map +1 -1
  40. package/dist/esm/test/client.test.js +15 -16
  41. package/dist/esm/test/client.test.js.map +1 -1
  42. package/dist/esm/test/derive.test.d.ts +2 -0
  43. package/dist/esm/test/derive.test.d.ts.map +1 -0
  44. package/dist/esm/test/derive.test.js +164 -0
  45. package/dist/esm/test/derive.test.js.map +1 -0
  46. package/dist/esm/test/security.test.d.ts +10 -0
  47. package/dist/esm/test/security.test.d.ts.map +1 -0
  48. package/dist/esm/test/security.test.js +153 -0
  49. package/dist/esm/test/security.test.js.map +1 -0
  50. package/dist/esm/test/streaming.integration.d.ts +9 -0
  51. package/dist/esm/test/streaming.integration.d.ts.map +1 -0
  52. package/dist/esm/test/streaming.integration.js +190 -0
  53. package/dist/esm/test/streaming.integration.js.map +1 -0
  54. package/package.json +6 -7
  55. package/dist/esm/example.d.ts +0 -6
  56. package/dist/esm/example.d.ts.map +0 -1
  57. package/dist/esm/example.js +0 -115
  58. package/dist/esm/example.js.map +0 -1
  59. package/dist/esm/streaming-test.d.ts +0 -3
  60. package/dist/esm/streaming-test.d.ts.map +0 -1
  61. package/dist/esm/streaming-test.js +0 -102
  62. package/dist/esm/streaming-test.js.map +0 -1
@@ -3,41 +3,41 @@ import assert from 'node:assert';
3
3
  import { Identity, Transport, createTransport } from '../index.js';
4
4
  import { PROTOCOL } from '../protocol.js';
5
5
  describe('Transport', () => {
6
- let clientIdentity;
7
6
  let serverIdentity;
8
7
  before(async () => {
9
- clientIdentity = await Identity.generate();
10
8
  serverIdentity = await Identity.generate();
11
9
  });
12
10
  it('should create transport with server public key', () => {
13
- const transport = new Transport(clientIdentity, 'localhost:8080', serverIdentity.getPublicKey());
11
+ const transport = new Transport(serverIdentity, 'localhost:8080');
14
12
  assert(transport instanceof Transport, 'Should create transport instance');
15
13
  });
16
14
  it('should encrypt and decrypt request', async () => {
17
- const serverPublicKey = serverIdentity.getPublicKey();
18
15
  const originalBody = new TextEncoder().encode('Hello, World!');
19
16
  const request = new Request('http://localhost:8080/test', {
20
17
  method: 'POST',
21
18
  body: originalBody
22
19
  });
23
- const encryptedRequest = await clientIdentity.encryptRequest(request, serverPublicKey);
24
- // Check that headers are set
25
- assert(encryptedRequest.headers.get(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER), 'Client public key header should be set');
20
+ const { request: encryptedRequest, context } = await serverIdentity.encryptRequestWithContext(request);
26
21
  assert(encryptedRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), 'Encapsulated key header should be set');
22
+ // Check that context was returned for response decryption
23
+ assert(context, 'Context should be returned');
24
+ assert(context.senderContext, 'Context should have sender context');
25
+ assert(context.requestEnc, 'Context should have request enc');
27
26
  // Check that body is encrypted (different from original)
28
27
  const encryptedBody = await encryptedRequest.arrayBuffer();
29
28
  assert(encryptedBody.byteLength > 0, 'Encrypted body should not be empty');
30
29
  assert(encryptedBody.byteLength !== originalBody.length, 'Encrypted body should have different length');
31
30
  });
32
31
  it('should handle request without body', async () => {
33
- const serverPublicKey = serverIdentity.getPublicKey();
34
32
  const request = new Request('http://localhost:8080/test', {
35
33
  method: 'GET'
36
34
  });
37
- const encryptedRequest = await clientIdentity.encryptRequest(request, serverPublicKey);
38
- // Check that only client public key header is set
39
- assert(encryptedRequest.headers.get(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER), 'Client public key header should be set');
40
- assert(!encryptedRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), 'Encapsulated key header should not be set for empty body');
35
+ const { request: resultRequest, context } = await serverIdentity.encryptRequestWithContext(request);
36
+ // Bodyless requests pass through unmodified - no EHBP headers set
37
+ // See SPEC.md Section 5.1
38
+ assert.strictEqual(resultRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), null, 'Encapsulated key header should NOT be set for bodyless requests');
39
+ // Context is null for bodyless requests (no HPKE context to derive response keys from)
40
+ assert.strictEqual(context, null, 'Context should be null for bodyless requests');
41
41
  });
42
42
  it('should connect to actual server and POST to /secure endpoint', async (t) => {
43
43
  const serverURL = 'http://localhost:8080';
@@ -48,15 +48,14 @@ describe('Transport', () => {
48
48
  return;
49
49
  }
50
50
  }
51
- catch (error) {
51
+ catch {
52
52
  t.skip('Server not running at localhost:8080');
53
53
  return;
54
54
  }
55
- // Create transport that will connect to the real server
56
- const transport = await createTransport(serverURL, clientIdentity);
55
+ const transport = await createTransport(serverURL);
57
56
  const testName = 'Integration Test User';
58
57
  const serverPubKeyHex = await transport.getServerPublicKeyHex();
59
- assert.strictEqual(serverPubKeyHex.length, 64, 'Server public key should be 64 bytes');
58
+ assert.strictEqual(serverPubKeyHex.length, 64, 'Server public key should be 64 hex chars (32 bytes)');
60
59
  // Make actual POST request to /secure endpoint
61
60
  const response = await transport.post(`${serverURL}/secure`, testName, {
62
61
  headers: { 'Content-Type': 'text/plain' }
@@ -1 +1 @@
1
- {"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../../src/test/client.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../../src/test/client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=derive.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.test.d.ts","sourceRoot":"","sources":["../../../src/test/derive.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,164 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { deriveResponseKeys, computeNonce, encryptChunk, decryptChunk, hexToBytes, bytesToHex, EXPORT_LENGTH, RESPONSE_NONCE_LENGTH, AES_GCM_NONCE_LENGTH, REQUEST_ENC_LENGTH, } from '../derive.js';
4
+ describe('deriveResponseKeys', () => {
5
+ it('should derive deterministic keys', async () => {
6
+ const exportedSecret = new Uint8Array(32).fill(1);
7
+ const requestEnc = new Uint8Array(32).fill(2);
8
+ const responseNonce = new Uint8Array(32).fill(3);
9
+ const km1 = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce);
10
+ const km2 = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce);
11
+ assert.deepStrictEqual(km1.keyBytes, km2.keyBytes, 'Keys should be identical');
12
+ assert.deepStrictEqual(km1.nonceBase, km2.nonceBase, 'Nonce bases should be identical');
13
+ });
14
+ it('should produce different keys for different inputs', async () => {
15
+ const exportedSecret = new Uint8Array(32).fill(1);
16
+ const requestEnc = new Uint8Array(32).fill(2);
17
+ const responseNonce1 = new Uint8Array(32).fill(3);
18
+ const responseNonce2 = new Uint8Array(32).fill(4);
19
+ const km1 = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce1);
20
+ const km2 = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce2);
21
+ assert.notDeepStrictEqual(km1.keyBytes, km2.keyBytes, 'Keys should differ for different nonces');
22
+ });
23
+ it('should reject invalid exported secret length', async () => {
24
+ const shortSecret = new Uint8Array(16);
25
+ const requestEnc = new Uint8Array(32);
26
+ const responseNonce = new Uint8Array(32);
27
+ await assert.rejects(async () => deriveResponseKeys(shortSecret, requestEnc, responseNonce), /exported secret must be 32 bytes/);
28
+ });
29
+ it('should reject invalid request enc length', async () => {
30
+ const exportedSecret = new Uint8Array(32);
31
+ const shortEnc = new Uint8Array(16);
32
+ const responseNonce = new Uint8Array(32);
33
+ await assert.rejects(async () => deriveResponseKeys(exportedSecret, shortEnc, responseNonce), /request enc must be 32 bytes/);
34
+ });
35
+ it('should reject invalid response nonce length', async () => {
36
+ const exportedSecret = new Uint8Array(32);
37
+ const requestEnc = new Uint8Array(32);
38
+ const shortNonce = new Uint8Array(12); // Wrong - should be 32
39
+ await assert.rejects(async () => deriveResponseKeys(exportedSecret, requestEnc, shortNonce), /response nonce must be 32 bytes/);
40
+ });
41
+ });
42
+ describe('computeNonce', () => {
43
+ it('should return base nonce for sequence 0', () => {
44
+ const nonceBase = new Uint8Array(12).fill(0xFF);
45
+ const nonce = computeNonce(nonceBase, 0);
46
+ assert.deepStrictEqual(nonce, nonceBase, 'Nonce should equal base for seq 0');
47
+ });
48
+ it('should XOR correctly for sequence 1', () => {
49
+ const nonceBase = new Uint8Array(12).fill(0xFF);
50
+ const nonce = computeNonce(nonceBase, 1);
51
+ assert.strictEqual(nonce[11], 0xFE, 'Last byte should be 0xFF XOR 0x01 = 0xFE');
52
+ });
53
+ it('should produce unique nonces for different sequences', () => {
54
+ const nonceBase = new Uint8Array(12).fill(0);
55
+ const seen = new Set();
56
+ for (let i = 0; i < 1000; i++) {
57
+ const nonce = computeNonce(nonceBase, i);
58
+ const key = bytesToHex(nonce);
59
+ assert(!seen.has(key), `Nonce for seq ${i} should be unique`);
60
+ seen.add(key);
61
+ }
62
+ });
63
+ it('should handle large sequence numbers', () => {
64
+ const nonceBase = new Uint8Array(12).fill(0);
65
+ // Test doesn't throw for large numbers
66
+ const nonce = computeNonce(nonceBase, 0xFFFFFFFF);
67
+ assert.strictEqual(nonce.length, 12);
68
+ });
69
+ });
70
+ describe('encrypt/decrypt round trip', () => {
71
+ it('should round-trip successfully', async () => {
72
+ const exportedSecret = new Uint8Array(32);
73
+ crypto.getRandomValues(exportedSecret);
74
+ const requestEnc = new Uint8Array(32);
75
+ crypto.getRandomValues(requestEnc);
76
+ const responseNonce = new Uint8Array(32);
77
+ crypto.getRandomValues(responseNonce);
78
+ const km = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce);
79
+ const plaintext = new TextEncoder().encode('Hello, World!');
80
+ const ciphertext = await encryptChunk(km, 0, plaintext);
81
+ const decrypted = await decryptChunk(km, 0, ciphertext);
82
+ assert.deepStrictEqual(decrypted, plaintext, 'Decrypted should match original');
83
+ });
84
+ it('should fail with wrong sequence number', async () => {
85
+ const exportedSecret = new Uint8Array(32);
86
+ crypto.getRandomValues(exportedSecret);
87
+ const requestEnc = new Uint8Array(32);
88
+ const responseNonce = new Uint8Array(32);
89
+ const km = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce);
90
+ const plaintext = new TextEncoder().encode('Hello, World!');
91
+ const ciphertext = await encryptChunk(km, 0, plaintext);
92
+ // Try to decrypt with wrong sequence
93
+ await assert.rejects(async () => decryptChunk(km, 1, ciphertext), /error/i // AES-GCM decryption failure
94
+ );
95
+ });
96
+ it('should encrypt multiple chunks with different nonces', async () => {
97
+ const exportedSecret = new Uint8Array(32);
98
+ crypto.getRandomValues(exportedSecret);
99
+ const requestEnc = new Uint8Array(32);
100
+ crypto.getRandomValues(requestEnc);
101
+ const responseNonce = new Uint8Array(32);
102
+ crypto.getRandomValues(responseNonce);
103
+ const km = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce);
104
+ const chunks = ['chunk1', 'chunk2', 'chunk3'].map((s) => new TextEncoder().encode(s));
105
+ // Encrypt all chunks
106
+ const encrypted = await Promise.all(chunks.map((chunk, i) => encryptChunk(km, i, chunk)));
107
+ // Decrypt all chunks
108
+ const decrypted = await Promise.all(encrypted.map((ct, i) => decryptChunk(km, i, ct)));
109
+ for (let i = 0; i < chunks.length; i++) {
110
+ assert.deepStrictEqual(decrypted[i], chunks[i], `Chunk ${i} should match`);
111
+ }
112
+ });
113
+ });
114
+ describe('hex utilities', () => {
115
+ it('should convert hex to bytes correctly', () => {
116
+ const hex = 'deadbeef';
117
+ const bytes = hexToBytes(hex);
118
+ assert.deepStrictEqual(bytes, new Uint8Array([0xde, 0xad, 0xbe, 0xef]));
119
+ });
120
+ it('should convert bytes to hex correctly', () => {
121
+ const bytes = new Uint8Array([0xde, 0xad, 0xbe, 0xef]);
122
+ const hex = bytesToHex(bytes);
123
+ assert.strictEqual(hex, 'deadbeef');
124
+ });
125
+ it('should round-trip hex conversion', () => {
126
+ const original = new Uint8Array(32);
127
+ crypto.getRandomValues(original);
128
+ const hex = bytesToHex(original);
129
+ const restored = hexToBytes(hex);
130
+ assert.deepStrictEqual(restored, original);
131
+ });
132
+ it('should reject odd-length hex strings', () => {
133
+ assert.throws(() => hexToBytes('abc'), /even length/);
134
+ });
135
+ });
136
+ describe('constants', () => {
137
+ it('should have correct constant values', () => {
138
+ assert.strictEqual(EXPORT_LENGTH, 32);
139
+ assert.strictEqual(RESPONSE_NONCE_LENGTH, 32);
140
+ assert.strictEqual(AES_GCM_NONCE_LENGTH, 12);
141
+ assert.strictEqual(REQUEST_ENC_LENGTH, 32);
142
+ });
143
+ });
144
+ describe('Go interoperability', () => {
145
+ it('should derive same keys as Go implementation', async () => {
146
+ // Test vectors from Go tests: exportedSecret[i] = i, requestEnc[i] = i+32, responseNonce[i] = i+64
147
+ const exportedSecret = new Uint8Array(32);
148
+ for (let i = 0; i < 32; i++)
149
+ exportedSecret[i] = i;
150
+ const requestEnc = new Uint8Array(32);
151
+ for (let i = 0; i < 32; i++)
152
+ requestEnc[i] = i + 32;
153
+ const responseNonce = new Uint8Array(32);
154
+ for (let i = 0; i < 32; i++)
155
+ responseNonce[i] = i + 64;
156
+ const km = await deriveResponseKeys(exportedSecret, requestEnc, responseNonce);
157
+ // Expected values from Go implementation
158
+ const expectedKey = hexToBytes('40ec528847cd4e928449f2ed1a70a7d1e8ee317d5e900424fc1dd5b0475b97f7');
159
+ const expectedNonceBase = hexToBytes('f8b0ce9466f27aa6243c65f9');
160
+ assert.deepStrictEqual(km.keyBytes, expectedKey, `Key mismatch.\nExpected: ${bytesToHex(expectedKey)}\nGot: ${bytesToHex(km.keyBytes)}`);
161
+ assert.deepStrictEqual(km.nonceBase, expectedNonceBase, `NonceBase mismatch.\nExpected: ${bytesToHex(expectedNonceBase)}\nGot: ${bytesToHex(km.nonceBase)}`);
162
+ });
163
+ });
164
+ //# sourceMappingURL=derive.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.test.js","sourceRoot":"","sources":["../../../src/test/derive.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Security Tests for EHBP
3
+ *
4
+ * These tests verify that the following MitM key substitution vulnerability cannot occur:
5
+ * 1. MitM cannot derive the correct response decryption keys
6
+ * 2. MitM cannot forge valid encrypted responses
7
+ * 3. Modified headers cause decryption failures
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=security.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.test.d.ts","sourceRoot":"","sources":["../../../src/test/security.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Security Tests for EHBP
3
+ *
4
+ * These tests verify that the following MitM key substitution vulnerability cannot occur:
5
+ * 1. MitM cannot derive the correct response decryption keys
6
+ * 2. MitM cannot forge valid encrypted responses
7
+ * 3. Modified headers cause decryption failures
8
+ */
9
+ import { describe, it } from 'node:test';
10
+ import assert from 'node:assert';
11
+ import { CipherSuite, KEM_DHKEM_X25519_HKDF_SHA256, KDF_HKDF_SHA256, AEAD_AES_256_GCM, } from 'hpke';
12
+ import { deriveResponseKeys, encryptChunk, decryptChunk, HPKE_REQUEST_INFO, EXPORT_LABEL, EXPORT_LENGTH, } from '../derive.js';
13
+ describe('Security Tests', () => {
14
+ const suite = new CipherSuite(KEM_DHKEM_X25519_HKDF_SHA256, KDF_HKDF_SHA256, AEAD_AES_256_GCM);
15
+ const infoBytes = new TextEncoder().encode(HPKE_REQUEST_INFO);
16
+ const exportLabelBytes = new TextEncoder().encode(EXPORT_LABEL);
17
+ describe('MitM cannot read responses', () => {
18
+ it('should derive different keys for attacker vs legitimate client', async () => {
19
+ // Server keypair
20
+ const serverKeyPair = await suite.GenerateKeyPair(true);
21
+ // Client (Alice) creates request context with info parameter
22
+ const { encapsulatedSecret: aliceEnc, ctx: aliceCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
23
+ const requestEnc = aliceEnc;
24
+ // Response nonce (public, sent in header)
25
+ const responseNonce = new Uint8Array(32);
26
+ crypto.getRandomValues(responseNonce);
27
+ // Alice exports secret from her HPKE context
28
+ const aliceExported = await aliceCtx.Export(exportLabelBytes, EXPORT_LENGTH);
29
+ // Eve (attacker) creates her own HPKE context to the server
30
+ // Even though Eve intercepts requestEnc, she cannot derive the shared secret
31
+ const { ctx: eveCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
32
+ const eveExported = await eveCtx.Export(exportLabelBytes, EXPORT_LENGTH);
33
+ // Alice derives correct keys
34
+ const aliceKM = await deriveResponseKeys(aliceExported, requestEnc, responseNonce);
35
+ // Eve derives WRONG keys (she has different HPKE shared secret)
36
+ const eveKM = await deriveResponseKeys(eveExported, requestEnc, responseNonce);
37
+ // Keys MUST be different - this is the core security property
38
+ assert.notDeepStrictEqual(aliceKM.keyBytes, eveKM.keyBytes, 'Alice and Eve must derive different keys');
39
+ assert.notDeepStrictEqual(aliceKM.nonceBase, eveKM.nonceBase, 'Alice and Eve must derive different nonce bases');
40
+ });
41
+ it('should prevent Eve from decrypting responses meant for Alice', async () => {
42
+ const serverKeyPair = await suite.GenerateKeyPair(true);
43
+ // Alice creates request
44
+ const { encapsulatedSecret: aliceEnc, ctx: aliceCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
45
+ const requestEnc = aliceEnc;
46
+ // Server receives and creates receiver context
47
+ const serverCtx = await suite.SetupRecipient(serverKeyPair.privateKey, aliceEnc, { info: infoBytes });
48
+ // Server generates response nonce and encrypts response
49
+ const responseNonce = new Uint8Array(32);
50
+ crypto.getRandomValues(responseNonce);
51
+ const serverExported = await serverCtx.Export(exportLabelBytes, EXPORT_LENGTH);
52
+ const serverKM = await deriveResponseKeys(serverExported, requestEnc, responseNonce);
53
+ const secretMessage = new TextEncoder().encode('Secret API key: sk-12345');
54
+ const encryptedResponse = await encryptChunk(serverKM, 0, secretMessage);
55
+ // Alice can decrypt (she has matching exported secret)
56
+ const aliceExported = await aliceCtx.Export(exportLabelBytes, EXPORT_LENGTH);
57
+ const aliceKM = await deriveResponseKeys(aliceExported, requestEnc, responseNonce);
58
+ const aliceDecrypted = await decryptChunk(aliceKM, 0, encryptedResponse);
59
+ assert.deepStrictEqual(aliceDecrypted, secretMessage, 'Alice should decrypt successfully');
60
+ // Eve creates her own context - she CANNOT decrypt
61
+ const { ctx: eveCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
62
+ const eveExported = await eveCtx.Export(exportLabelBytes, EXPORT_LENGTH);
63
+ const eveKM = await deriveResponseKeys(eveExported, requestEnc, responseNonce);
64
+ // Eve's decryption MUST fail
65
+ await assert.rejects(async () => decryptChunk(eveKM, 0, encryptedResponse), /error/i, 'Eve must not be able to decrypt the response');
66
+ });
67
+ });
68
+ describe('MitM cannot forge responses', () => {
69
+ it('should reject responses encrypted with wrong keys', async () => {
70
+ const serverKeyPair = await suite.GenerateKeyPair(true);
71
+ // Alice creates request
72
+ const { encapsulatedSecret: aliceEnc, ctx: aliceCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
73
+ const requestEnc = aliceEnc;
74
+ const aliceExported = await aliceCtx.Export(exportLabelBytes, EXPORT_LENGTH);
75
+ // Attacker creates forged response with random keys
76
+ const attackerSecret = new Uint8Array(32);
77
+ crypto.getRandomValues(attackerSecret);
78
+ const forgedNonce = new Uint8Array(32);
79
+ crypto.getRandomValues(forgedNonce);
80
+ const attackerKM = await deriveResponseKeys(attackerSecret, requestEnc, forgedNonce);
81
+ const forgedMessage = new TextEncoder().encode('Malicious message');
82
+ const forgedCiphertext = await encryptChunk(attackerKM, 0, forgedMessage);
83
+ // Alice tries to decrypt with her real keys
84
+ const aliceKM = await deriveResponseKeys(aliceExported, requestEnc, forgedNonce);
85
+ // Decryption MUST fail - attacker used wrong shared secret
86
+ await assert.rejects(async () => decryptChunk(aliceKM, 0, forgedCiphertext), /error/i, 'Forged response must be rejected');
87
+ });
88
+ });
89
+ describe('Modified headers cause failure', () => {
90
+ it('should fail decryption if request enc is modified', async () => {
91
+ const serverKeyPair = await suite.GenerateKeyPair(true);
92
+ const { encapsulatedSecret: aliceEnc, ctx: aliceCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
93
+ const originalEnc = aliceEnc;
94
+ const serverCtx = await suite.SetupRecipient(serverKeyPair.privateKey, aliceEnc, { info: infoBytes });
95
+ // Server encrypts response using original enc
96
+ const serverExported = await serverCtx.Export(exportLabelBytes, EXPORT_LENGTH);
97
+ const responseNonce = new Uint8Array(32);
98
+ crypto.getRandomValues(responseNonce);
99
+ const serverKM = await deriveResponseKeys(serverExported, originalEnc, responseNonce);
100
+ const plaintext = new TextEncoder().encode('Secret response');
101
+ const ciphertext = await encryptChunk(serverKM, 0, plaintext);
102
+ // Alice receives with MODIFIED enc (tampered by MitM)
103
+ const modifiedEnc = new Uint8Array(originalEnc);
104
+ modifiedEnc[0] ^= 0xff; // Flip bits
105
+ const aliceExported = await aliceCtx.Export(exportLabelBytes, EXPORT_LENGTH);
106
+ const aliceKM = await deriveResponseKeys(aliceExported, modifiedEnc, responseNonce);
107
+ // Decryption MUST fail because enc was modified
108
+ await assert.rejects(async () => decryptChunk(aliceKM, 0, ciphertext), /error/i, 'Modified enc must cause decryption failure');
109
+ });
110
+ it('should fail decryption if response nonce is modified', async () => {
111
+ const serverKeyPair = await suite.GenerateKeyPair(true);
112
+ const { encapsulatedSecret: aliceEnc, ctx: aliceCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
113
+ const requestEnc = aliceEnc;
114
+ const serverCtx = await suite.SetupRecipient(serverKeyPair.privateKey, aliceEnc, { info: infoBytes });
115
+ // Server encrypts response
116
+ const serverExported = await serverCtx.Export(exportLabelBytes, EXPORT_LENGTH);
117
+ const originalNonce = new Uint8Array(32);
118
+ crypto.getRandomValues(originalNonce);
119
+ const serverKM = await deriveResponseKeys(serverExported, requestEnc, originalNonce);
120
+ const plaintext = new TextEncoder().encode('Secret response');
121
+ const ciphertext = await encryptChunk(serverKM, 0, plaintext);
122
+ // Alice receives with MODIFIED nonce (tampered by MitM)
123
+ const modifiedNonce = new Uint8Array(originalNonce);
124
+ modifiedNonce[0] ^= 0xff;
125
+ const aliceExported = await aliceCtx.Export(exportLabelBytes, EXPORT_LENGTH);
126
+ const aliceKM = await deriveResponseKeys(aliceExported, requestEnc, modifiedNonce);
127
+ // Decryption MUST fail because nonce was modified
128
+ await assert.rejects(async () => decryptChunk(aliceKM, 0, ciphertext), /error/i, 'Modified nonce must cause decryption failure');
129
+ });
130
+ });
131
+ describe('Client public key header attack prevented', () => {
132
+ it('should not use client public key for response encryption', async () => {
133
+ const serverKeyPair = await suite.GenerateKeyPair(true);
134
+ // Alice creates request
135
+ const { encapsulatedSecret: aliceEnc, ctx: aliceCtx } = await suite.SetupSender(serverKeyPair.publicKey, { info: infoBytes });
136
+ const requestEnc = aliceEnc;
137
+ // Server creates receiver from Alice's actual enc
138
+ const serverCtx = await suite.SetupRecipient(serverKeyPair.privateKey, aliceEnc, { info: infoBytes });
139
+ const responseNonce = new Uint8Array(32);
140
+ crypto.getRandomValues(responseNonce);
141
+ const serverExported = await serverCtx.Export(exportLabelBytes, EXPORT_LENGTH);
142
+ const serverKM = await deriveResponseKeys(serverExported, requestEnc, responseNonce);
143
+ const secretData = new TextEncoder().encode('Sensitive API response');
144
+ const encrypted = await encryptChunk(serverKM, 0, secretData);
145
+ // Alice can decrypt using her HPKE context
146
+ const aliceExported = await aliceCtx.Export(exportLabelBytes, EXPORT_LENGTH);
147
+ const aliceKM = await deriveResponseKeys(aliceExported, requestEnc, responseNonce);
148
+ const decrypted = await decryptChunk(aliceKM, 0, encrypted);
149
+ assert.deepStrictEqual(decrypted, secretData);
150
+ });
151
+ });
152
+ });
153
+ //# sourceMappingURL=security.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.test.js","sourceRoot":"","sources":["../../../src/test/security.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Integration test for EHBP streaming functionality.
4
+ * Requires a running EHBP server at localhost:8080.
5
+ *
6
+ * Run with: npm run test:integration
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=streaming.integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.integration.d.ts","sourceRoot":"","sources":["../../../src/test/streaming.integration.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}