ehbp 0.0.6 → 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 (63) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +158 -0
  3. package/dist/cjs/client.d.ts +13 -13
  4. package/dist/cjs/client.d.ts.map +1 -1
  5. package/dist/cjs/client.js +39 -50
  6. package/dist/cjs/client.js.map +1 -1
  7. package/dist/cjs/derive.d.ts +63 -0
  8. package/dist/cjs/derive.d.ts.map +1 -0
  9. package/dist/cjs/derive.js +136 -0
  10. package/dist/cjs/derive.js.map +1 -0
  11. package/dist/cjs/identity.d.ts +37 -10
  12. package/dist/cjs/identity.d.ts.map +1 -1
  13. package/dist/cjs/identity.js +169 -150
  14. package/dist/cjs/identity.js.map +1 -1
  15. package/dist/cjs/index.d.ts +4 -1
  16. package/dist/cjs/index.d.ts.map +1 -1
  17. package/dist/cjs/index.js +15 -1
  18. package/dist/cjs/index.js.map +1 -1
  19. package/dist/cjs/protocol.d.ts +1 -1
  20. package/dist/cjs/protocol.js +2 -2
  21. package/dist/cjs/protocol.js.map +1 -1
  22. package/dist/esm/client.d.ts +13 -13
  23. package/dist/esm/client.d.ts.map +1 -1
  24. package/dist/esm/client.js +39 -50
  25. package/dist/esm/client.js.map +1 -1
  26. package/dist/esm/derive.d.ts +63 -0
  27. package/dist/esm/derive.d.ts.map +1 -0
  28. package/dist/esm/derive.js +127 -0
  29. package/dist/esm/derive.js.map +1 -0
  30. package/dist/esm/identity.d.ts +37 -10
  31. package/dist/esm/identity.d.ts.map +1 -1
  32. package/dist/esm/identity.js +169 -150
  33. package/dist/esm/identity.js.map +1 -1
  34. package/dist/esm/index.d.ts +4 -1
  35. package/dist/esm/index.d.ts.map +1 -1
  36. package/dist/esm/index.js +2 -0
  37. package/dist/esm/index.js.map +1 -1
  38. package/dist/esm/protocol.d.ts +1 -1
  39. package/dist/esm/protocol.js +2 -2
  40. package/dist/esm/protocol.js.map +1 -1
  41. package/dist/esm/test/client.test.js +15 -16
  42. package/dist/esm/test/client.test.js.map +1 -1
  43. package/dist/esm/test/derive.test.d.ts +2 -0
  44. package/dist/esm/test/derive.test.d.ts.map +1 -0
  45. package/dist/esm/test/derive.test.js +164 -0
  46. package/dist/esm/test/derive.test.js.map +1 -0
  47. package/dist/esm/test/security.test.d.ts +10 -0
  48. package/dist/esm/test/security.test.d.ts.map +1 -0
  49. package/dist/esm/test/security.test.js +153 -0
  50. package/dist/esm/test/security.test.js.map +1 -0
  51. package/dist/esm/test/streaming.integration.d.ts +9 -0
  52. package/dist/esm/test/streaming.integration.d.ts.map +1 -0
  53. package/dist/esm/test/streaming.integration.js +190 -0
  54. package/dist/esm/test/streaming.integration.js.map +1 -0
  55. package/package.json +6 -7
  56. package/dist/esm/example.d.ts +0 -6
  57. package/dist/esm/example.d.ts.map +0 -1
  58. package/dist/esm/example.js +0 -115
  59. package/dist/esm/example.js.map +0 -1
  60. package/dist/esm/streaming-test.d.ts +0 -3
  61. package/dist/esm/streaming-test.d.ts.map +0 -1
  62. package/dist/esm/streaming-test.js +0 -102
  63. package/dist/esm/streaming-test.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tinfoil, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # EHBP JavaScript Client
2
+
3
+ JavaScript/TypeScript client for [Encrypted HTTP Body Protocol (EHBP)](https://github.com/tinfoilsh/encrypted-http-body-protocol).
4
+
5
+ EHBP encrypts HTTP request and response bodies end-to-end using HPKE ([RFC 9180](https://datatracker.ietf.org/doc/rfc9180/)) while leaving headers in cleartext for routing.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install ehbp
11
+ ```
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 20+
16
+ - Works in both Node.js and modern browsers
17
+
18
+
19
+ ## Quick Start
20
+
21
+ ```javascript
22
+ import { createTransport } from 'ehbp';
23
+
24
+ // Create transport (fetches server public key automatically)
25
+ const transport = await createTransport('https://example.com');
26
+
27
+ // Make encrypted requests - works like fetch()
28
+ const response = await transport.post('/api/data', JSON.stringify({ message: 'hello' }), {
29
+ headers: { 'Content-Type': 'application/json' }
30
+ });
31
+
32
+ const data = await response.json();
33
+ ```
34
+
35
+ ## API
36
+
37
+ ### `createTransport(serverURL: string): Promise<Transport>`
38
+
39
+ Creates a transport that automatically encrypts requests and decrypts responses.
40
+
41
+ ```javascript
42
+ const transport = await createTransport('https://example.com');
43
+ ```
44
+
45
+ ### `transport.request(input, init?): Promise<Response>`
46
+
47
+ General-purpose method supporting all fetch options.
48
+
49
+ ```javascript
50
+ const response = await transport.request('/api/data', {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify({ key: 'value' })
54
+ });
55
+ ```
56
+
57
+ ### Convenience Methods
58
+
59
+ ```javascript
60
+ await transport.get('/users');
61
+ await transport.post('/users', body, options);
62
+ await transport.put('/users/123', body, options);
63
+ await transport.delete('/users/123');
64
+ ```
65
+
66
+ ## Browser Usage
67
+
68
+ ```html
69
+ <script type="module">
70
+ import { createTransport } from './dist/browser.js';
71
+
72
+ const transport = await createTransport('https://example.com');
73
+ const response = await transport.post('/api', 'Hello!');
74
+ console.log(await response.text());
75
+ </script>
76
+ ```
77
+
78
+ ## Requirements
79
+
80
+ - Node.js 20+ or modern browsers with Web Crypto API
81
+
82
+ ## Development
83
+
84
+ ```sh
85
+ npm install
86
+ npm run build
87
+ npm test
88
+ npm run build:browser # Browser bundle
89
+ ```
90
+
91
+ ### Running Examples (Node.js)
92
+
93
+ The Node.js example requires a build and an EHBP server running at `http://localhost:8080`:
94
+
95
+ ```sh
96
+ # Build first
97
+ npm run build
98
+
99
+ # Start the Go server (from parent directory)
100
+ go run pkg/server/main.go
101
+
102
+ # Run the example (in another terminal)
103
+ npm run example
104
+ ```
105
+
106
+ ### Running Examples (Browser)
107
+
108
+ Browser examples are located in `examples/` and require the browser bundle:
109
+
110
+ ```sh
111
+ # Build the browser bundle
112
+ npm run build:browser
113
+
114
+ # Start the local dev server
115
+ npm run serve
116
+
117
+ # Start the Go EHBP server (from parent directory, in another terminal)
118
+ go run pkg/server/main.go
119
+ ```
120
+
121
+ Then open in your browser:
122
+
123
+ | Example | URL | Description |
124
+ |---------|-----|-------------|
125
+ | `test.html` | http://localhost:3000/examples/test.html | POST and streaming tests (server: `localhost:8080`) |
126
+ | `chat.html` | http://localhost:3000/examples/chat.html | Chat interface demo (server: `localhost:8443`) |
127
+
128
+ **Note:** `chat.html` connects to port 8443 and expects an OpenAI-compatible chat completions API.
129
+
130
+ ### Running Integration Tests
131
+
132
+ Integration tests verify streaming functionality against a live server:
133
+
134
+ ```sh
135
+ # Start the Go server (from parent directory)
136
+ go run pkg/server/main.go
137
+
138
+ # Run integration tests (in another terminal)
139
+ npm run build
140
+ npm run test:integration
141
+ ```
142
+
143
+ ### Commands
144
+
145
+ | Command | Description |
146
+ |---------|-------------|
147
+ | `npm test` | Run unit tests (no server required) |
148
+ | `npm run test:integration` | Run streaming integration tests (requires server) |
149
+ | `npm run example` | Run Node.js API demo (requires server) |
150
+ | `npm run serve` | Start local server for browser examples |
151
+
152
+ ## Protocol
153
+
154
+ See [SPEC.md](../SPEC.md) for the complete protocol specification.
155
+
156
+ ## Security
157
+
158
+ Report vulnerabilities to [security@tinfoil.sh](mailto:security@tinfoil.sh) or open a GitHub issue.
@@ -1,30 +1,30 @@
1
1
  import { Identity } from './identity.js';
2
+ import type { Key } from 'hpke';
2
3
  /**
3
4
  * HTTP transport for EHBP
4
5
  */
5
6
  export declare class Transport {
6
- private clientIdentity;
7
+ private serverIdentity;
7
8
  private serverHost;
8
- private serverPublicKey;
9
- constructor(clientIdentity: Identity, serverHost: string, serverPublicKey: CryptoKey);
9
+ constructor(serverIdentity: Identity, serverHost: string);
10
10
  /**
11
- * Create a new transport by fetching server public key
11
+ * Create a new transport by fetching server public key.
12
12
  */
13
- static create(serverURL: string, clientIdentity: Identity): Promise<Transport>;
13
+ static create(serverURL: string): Promise<Transport>;
14
+ /**
15
+ * Get the server identity
16
+ */
17
+ getServerIdentity(): Identity;
14
18
  /**
15
19
  * Get the server public key
16
20
  */
17
- getServerPublicKey(): CryptoKey;
21
+ getServerPublicKey(): Key;
18
22
  /**
19
23
  * Get the server public key as hex string
20
24
  */
21
25
  getServerPublicKeyHex(): Promise<string>;
22
26
  /**
23
- * Get the client public key
24
- */
25
- getClientPublicKey(): CryptoKey;
26
- /**
27
- * Make an encrypted HTTP request
27
+ * Make an encrypted HTTP request.
28
28
  */
29
29
  request(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
30
30
  /**
@@ -45,7 +45,7 @@ export declare class Transport {
45
45
  delete(url: string | URL, init?: RequestInit): Promise<Response>;
46
46
  }
47
47
  /**
48
- * Create a new transport instance
48
+ * Create a new transport instance.
49
49
  */
50
- export declare function createTransport(serverURL: string, clientIdentity: Identity): Promise<Transport>;
50
+ export declare function createTransport(serverURL: string): Promise<Transport>;
51
51
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAY;gBAEvB,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS;IAMpF;;OAEG;WACU,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAwBpF;;OAEG;IACH,kBAAkB,IAAI,SAAS;IAI/B;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ9C;;OAEG;IACH,kBAAkB,IAAI,SAAS;IAI/B;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAgF9E;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAInE;;OAEG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIrF;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIpF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAGvE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAErG"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEhC;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,UAAU,CAAS;gBAEf,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM;IAKxD;;OAEG;WACU,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAuB1D;;OAEG;IACH,iBAAiB,IAAI,QAAQ;IAI7B;;OAEG;IACH,kBAAkB,IAAI,GAAG;IAIzB;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI9C;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IA6E9E;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAInE;;OAEG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIrF;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIpF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAGvE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAE3E"}
@@ -8,18 +8,16 @@ const protocol_js_1 = require("./protocol.js");
8
8
  * HTTP transport for EHBP
9
9
  */
10
10
  class Transport {
11
- clientIdentity;
11
+ serverIdentity;
12
12
  serverHost;
13
- serverPublicKey;
14
- constructor(clientIdentity, serverHost, serverPublicKey) {
15
- this.clientIdentity = clientIdentity;
13
+ constructor(serverIdentity, serverHost) {
14
+ this.serverIdentity = serverIdentity;
16
15
  this.serverHost = serverHost;
17
- this.serverPublicKey = serverPublicKey;
18
16
  }
19
17
  /**
20
- * Create a new transport by fetching server public key
18
+ * Create a new transport by fetching server public key.
21
19
  */
22
- static async create(serverURL, clientIdentity) {
20
+ static async create(serverURL) {
23
21
  const url = new URL(serverURL);
24
22
  const serverHost = url.host;
25
23
  // Fetch server public key
@@ -34,38 +32,33 @@ class Transport {
34
32
  }
35
33
  const keysData = new Uint8Array(await response.arrayBuffer());
36
34
  const serverIdentity = await identity_js_1.Identity.unmarshalPublicConfig(keysData);
37
- const serverPublicKey = serverIdentity.getPublicKey();
38
- return new Transport(clientIdentity, serverHost, serverPublicKey);
35
+ return new Transport(serverIdentity, serverHost);
36
+ }
37
+ /**
38
+ * Get the server identity
39
+ */
40
+ getServerIdentity() {
41
+ return this.serverIdentity;
39
42
  }
40
43
  /**
41
44
  * Get the server public key
42
45
  */
43
46
  getServerPublicKey() {
44
- return this.serverPublicKey;
47
+ return this.serverIdentity.getPublicKey();
45
48
  }
46
49
  /**
47
50
  * Get the server public key as hex string
48
51
  */
49
52
  async getServerPublicKeyHex() {
50
- const exported = await crypto.subtle.exportKey('raw', this.serverPublicKey);
51
- const keyBytes = new Uint8Array(exported);
52
- return Array.from(keyBytes)
53
- .map(b => b.toString(16).padStart(2, '0'))
54
- .join('');
55
- }
56
- /**
57
- * Get the client public key
58
- */
59
- getClientPublicKey() {
60
- return this.clientIdentity.getPublicKey();
53
+ return this.serverIdentity.getPublicKeyHex();
61
54
  }
62
55
  /**
63
- * Make an encrypted HTTP request
56
+ * Make an encrypted HTTP request.
64
57
  */
65
58
  async request(input, init) {
66
- // Skip EHBP for data: URLs (e.g., used for FormData detection)
59
+ // Skip EHBP for non-network URLs (data:, blob:)
67
60
  const inputUrl = input instanceof Request ? input.url : String(input);
68
- if (inputUrl.startsWith('data:')) {
61
+ if (inputUrl.startsWith('data:') || inputUrl.startsWith('blob:')) {
69
62
  return fetch(input, init);
70
63
  }
71
64
  // Extract body from init or original request before creating Request object
@@ -99,36 +92,32 @@ class Transport {
99
92
  method,
100
93
  headers,
101
94
  body: requestBody,
102
- duplex: 'half'
95
+ duplex: 'half',
103
96
  });
104
- // Encrypt request body if present (check the original requestBody, not request.body)
105
- if (requestBody !== null && requestBody !== undefined) {
106
- request = await this.clientIdentity.encryptRequest(request, this.serverPublicKey);
107
- }
108
- else {
109
- // No body, just set client public key header
110
- const headers = new Headers(request.headers);
111
- headers.set(protocol_js_1.PROTOCOL.CLIENT_PUBLIC_KEY_HEADER, await this.clientIdentity.getPublicKeyHex());
112
- request = new Request(request.url, {
113
- method: request.method,
114
- headers,
115
- body: null
116
- });
117
- }
97
+ // Encrypt request (returns context for response decryption)
98
+ // For bodyless requests, context will be null and request passes through unmodified
99
+ const { request: encryptedRequest, context } = await this.serverIdentity.encryptRequestWithContext(request);
118
100
  // Make the request
119
- const response = await fetch(request);
101
+ const response = await fetch(encryptedRequest);
120
102
  if (!response.ok) {
121
103
  console.warn(`Server returned non-OK status: ${response.status}`);
122
104
  }
123
- // Check for encapsulated key header
124
- const encapKeyHeader = response.headers.get(protocol_js_1.PROTOCOL.ENCAPSULATED_KEY_HEADER);
125
- if (!encapKeyHeader) {
126
- throw new Error(`Missing ${protocol_js_1.PROTOCOL.ENCAPSULATED_KEY_HEADER} encapsulated key header`);
105
+ // Bodyless requests: context is null, response is plaintext
106
+ if (context === null) {
107
+ return response;
108
+ }
109
+ // Check for fallback header - if set, server returned unencrypted response
110
+ const fallbackHeader = response.headers.get(protocol_js_1.PROTOCOL.FALLBACK_HEADER);
111
+ if (fallbackHeader === '1') {
112
+ return response;
113
+ }
114
+ // Check for response nonce header (required for response decryption)
115
+ const responseNonceHeader = response.headers.get(protocol_js_1.PROTOCOL.RESPONSE_NONCE_HEADER);
116
+ if (!responseNonceHeader) {
117
+ throw new Error(`Missing ${protocol_js_1.PROTOCOL.RESPONSE_NONCE_HEADER} header`);
127
118
  }
128
- // Decode encapsulated key
129
- const serverEncapKey = new Uint8Array(encapKeyHeader.match(/.{2}/g).map(byte => parseInt(byte, 16)));
130
119
  // Decrypt response
131
- return await this.clientIdentity.decryptResponse(response, serverEncapKey);
120
+ return await this.serverIdentity.decryptResponseWithContext(response, context);
132
121
  }
133
122
  /**
134
123
  * Convenience method for GET requests
@@ -157,9 +146,9 @@ class Transport {
157
146
  }
158
147
  exports.Transport = Transport;
159
148
  /**
160
- * Create a new transport instance
149
+ * Create a new transport instance.
161
150
  */
162
- async function createTransport(serverURL, clientIdentity) {
163
- return Transport.create(serverURL, clientIdentity);
151
+ async function createTransport(serverURL) {
152
+ return Transport.create(serverURL);
164
153
  }
165
154
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Response key derivation for EHBP
3
+ *
4
+ * This module implements the key derivation matching the Go implementation.
5
+ *
6
+ * The derivation follows OHTTP (RFC 9458):
7
+ * salt = concat(enc, response_nonce)
8
+ * prk = Extract(salt, secret)
9
+ * aead_key = Expand(prk, "key", Nk)
10
+ * aead_nonce = Expand(prk, "nonce", Nn)
11
+ */
12
+ export declare const HPKE_REQUEST_INFO = "ehbp request";
13
+ export declare const EXPORT_LABEL = "ehbp response";
14
+ export declare const EXPORT_LENGTH = 32;
15
+ export declare const RESPONSE_NONCE_LENGTH = 32;
16
+ export declare const AES256_KEY_LENGTH = 32;
17
+ export declare const AES_GCM_NONCE_LENGTH = 12;
18
+ export declare const REQUEST_ENC_LENGTH = 32;
19
+ /**
20
+ * Response key material for encryption/decryption
21
+ */
22
+ export interface ResponseKeyMaterial {
23
+ /** Raw key bytes for AEAD operations */
24
+ keyBytes: Uint8Array;
25
+ /** 12 bytes, XORed with sequence number for each chunk */
26
+ nonceBase: Uint8Array;
27
+ }
28
+ /**
29
+ * Derives response encryption keys from the HPKE exported secret.
30
+ *
31
+ * salt = concat(enc, response_nonce)
32
+ * prk = Extract(salt, secret)
33
+ * key = Expand(prk, "key", 32)
34
+ * nonceBase = Expand(prk, "nonce", 12)
35
+ *
36
+ * @param exportedSecret - 32 bytes exported from HPKE context
37
+ * @param requestEnc - 32 bytes encapsulated key from request
38
+ * @param responseNonce - 32 bytes random nonce from response
39
+ * @returns Key material for response encryption/decryption
40
+ */
41
+ export declare function deriveResponseKeys(exportedSecret: Uint8Array, requestEnc: Uint8Array, responseNonce: Uint8Array): Promise<ResponseKeyMaterial>;
42
+ /**
43
+ * Computes the nonce for a specific sequence number.
44
+ * nonce = nonceBase XOR sequence_number (big-endian in last 8 bytes)
45
+ */
46
+ export declare function computeNonce(nonceBase: Uint8Array, seq: number): Uint8Array;
47
+ /**
48
+ * Encrypts a chunk using the response key material
49
+ */
50
+ export declare function encryptChunk(km: ResponseKeyMaterial, seq: number, plaintext: Uint8Array): Promise<Uint8Array>;
51
+ /**
52
+ * Decrypts a chunk using the response key material
53
+ */
54
+ export declare function decryptChunk(km: ResponseKeyMaterial, seq: number, ciphertext: Uint8Array): Promise<Uint8Array>;
55
+ /**
56
+ * Utility: Convert hex string to Uint8Array
57
+ */
58
+ export declare function hexToBytes(hex: string): Uint8Array;
59
+ /**
60
+ * Utility: Convert Uint8Array to hex string
61
+ */
62
+ export declare function bytesToHex(bytes: Uint8Array): string;
63
+ //# sourceMappingURL=derive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/derive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAC5C,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAMrC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,QAAQ,EAAE,UAAU,CAAC;IACrB,0DAA0D;IAC1D,SAAS,EAAE,UAAU,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,UAAU,EAC1B,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,UAAU,GACxB,OAAO,CAAC,mBAAmB,CAAC,CA2B9B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAyB3E;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,mBAAmB,EACvB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,UAAU,CAAC,CAMrB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,mBAAmB,EACvB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,UAAU,CAAC,CAMrB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAYlD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIpD"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Response key derivation for EHBP
4
+ *
5
+ * This module implements the key derivation matching the Go implementation.
6
+ *
7
+ * The derivation follows OHTTP (RFC 9458):
8
+ * salt = concat(enc, response_nonce)
9
+ * prk = Extract(salt, secret)
10
+ * aead_key = Expand(prk, "key", Nk)
11
+ * aead_nonce = Expand(prk, "nonce", Nn)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.REQUEST_ENC_LENGTH = exports.AES_GCM_NONCE_LENGTH = exports.AES256_KEY_LENGTH = exports.RESPONSE_NONCE_LENGTH = exports.EXPORT_LENGTH = exports.EXPORT_LABEL = exports.HPKE_REQUEST_INFO = void 0;
15
+ exports.deriveResponseKeys = deriveResponseKeys;
16
+ exports.computeNonce = computeNonce;
17
+ exports.encryptChunk = encryptChunk;
18
+ exports.decryptChunk = decryptChunk;
19
+ exports.hexToBytes = hexToBytes;
20
+ exports.bytesToHex = bytesToHex;
21
+ const hpke_1 = require("hpke");
22
+ const kdf = (0, hpke_1.KDF_HKDF_SHA256)();
23
+ const aead = (0, hpke_1.AEAD_AES_256_GCM)();
24
+ exports.HPKE_REQUEST_INFO = 'ehbp request';
25
+ exports.EXPORT_LABEL = 'ehbp response';
26
+ exports.EXPORT_LENGTH = 32;
27
+ exports.RESPONSE_NONCE_LENGTH = 32; // max(Nn, Nk) = max(12, 32) = 32
28
+ exports.AES256_KEY_LENGTH = 32;
29
+ exports.AES_GCM_NONCE_LENGTH = 12;
30
+ exports.REQUEST_ENC_LENGTH = 32; // X25519 enc size
31
+ // Labels for HKDF-Expand
32
+ const RESPONSE_KEY_LABEL = new TextEncoder().encode('key');
33
+ const RESPONSE_NONCE_LABEL = new TextEncoder().encode('nonce');
34
+ /**
35
+ * Derives response encryption keys from the HPKE exported secret.
36
+ *
37
+ * salt = concat(enc, response_nonce)
38
+ * prk = Extract(salt, secret)
39
+ * key = Expand(prk, "key", 32)
40
+ * nonceBase = Expand(prk, "nonce", 12)
41
+ *
42
+ * @param exportedSecret - 32 bytes exported from HPKE context
43
+ * @param requestEnc - 32 bytes encapsulated key from request
44
+ * @param responseNonce - 32 bytes random nonce from response
45
+ * @returns Key material for response encryption/decryption
46
+ */
47
+ async function deriveResponseKeys(exportedSecret, requestEnc, responseNonce) {
48
+ // Validate inputs
49
+ if (exportedSecret.length !== exports.EXPORT_LENGTH) {
50
+ throw new Error(`exported secret must be ${exports.EXPORT_LENGTH} bytes, got ${exportedSecret.length}`);
51
+ }
52
+ if (requestEnc.length !== exports.REQUEST_ENC_LENGTH) {
53
+ throw new Error(`request enc must be ${exports.REQUEST_ENC_LENGTH} bytes, got ${requestEnc.length}`);
54
+ }
55
+ if (responseNonce.length !== exports.RESPONSE_NONCE_LENGTH) {
56
+ throw new Error(`response nonce must be ${exports.RESPONSE_NONCE_LENGTH} bytes, got ${responseNonce.length}`);
57
+ }
58
+ // salt = concat(enc, response_nonce)
59
+ const salt = new Uint8Array(requestEnc.length + responseNonce.length);
60
+ salt.set(requestEnc, 0);
61
+ salt.set(responseNonce, requestEnc.length);
62
+ // prk = Extract(salt, secret)
63
+ const prk = await kdf.Extract(salt, exportedSecret);
64
+ // key = Expand(prk, "key", 32)
65
+ const keyBytes = await kdf.Expand(prk, RESPONSE_KEY_LABEL, exports.AES256_KEY_LENGTH);
66
+ // nonceBase = Expand(prk, "nonce", 12)
67
+ const nonceBase = await kdf.Expand(prk, RESPONSE_NONCE_LABEL, exports.AES_GCM_NONCE_LENGTH);
68
+ return { keyBytes, nonceBase };
69
+ }
70
+ /**
71
+ * Computes the nonce for a specific sequence number.
72
+ * nonce = nonceBase XOR sequence_number (big-endian in last 8 bytes)
73
+ */
74
+ function computeNonce(nonceBase, seq) {
75
+ if (nonceBase.length !== exports.AES_GCM_NONCE_LENGTH) {
76
+ throw new Error(`nonce base must be ${exports.AES_GCM_NONCE_LENGTH} bytes`);
77
+ }
78
+ // Validate seq to prevent nonce reuse from integer overflow.
79
+ // JavaScript's >>> operator only works correctly for 32-bit unsigned integers.
80
+ // Values >= 2^32 wrap around (e.g., 2^32 >>> 0 === 0), causing nonce reuse.
81
+ // In practice, 2^32 chunks per response is impossible (~4PB minimum), but we validate defensively.
82
+ if (!Number.isInteger(seq) || seq < 0 || seq >= 0x100000000) {
83
+ throw new Error(`sequence number must be an integer in range [0, 2^32): got ${seq}`);
84
+ }
85
+ const nonce = new Uint8Array(exports.AES_GCM_NONCE_LENGTH);
86
+ nonce.set(nonceBase);
87
+ // XOR with sequence number in the last 8 bytes (big-endian)
88
+ for (let i = 0; i < 8; i++) {
89
+ const shift = i * 8;
90
+ if (shift < 32) {
91
+ nonce[exports.AES_GCM_NONCE_LENGTH - 1 - i] ^= (seq >>> shift) & 0xff;
92
+ }
93
+ }
94
+ return nonce;
95
+ }
96
+ /**
97
+ * Encrypts a chunk using the response key material
98
+ */
99
+ async function encryptChunk(km, seq, plaintext) {
100
+ const nonce = computeNonce(km.nonceBase, seq);
101
+ const ciphertext = await aead.Seal(km.keyBytes, nonce, new Uint8Array(0), plaintext);
102
+ return ciphertext;
103
+ }
104
+ /**
105
+ * Decrypts a chunk using the response key material
106
+ */
107
+ async function decryptChunk(km, seq, ciphertext) {
108
+ const nonce = computeNonce(km.nonceBase, seq);
109
+ const plaintext = await aead.Open(km.keyBytes, nonce, new Uint8Array(0), ciphertext);
110
+ return plaintext;
111
+ }
112
+ /**
113
+ * Utility: Convert hex string to Uint8Array
114
+ */
115
+ function hexToBytes(hex) {
116
+ if (hex.length % 2 !== 0) {
117
+ throw new Error('Hex string must have even length');
118
+ }
119
+ if (!/^[0-9a-fA-F]*$/.test(hex)) {
120
+ throw new Error('Invalid hex character');
121
+ }
122
+ const bytes = new Uint8Array(hex.length / 2);
123
+ for (let i = 0; i < bytes.length; i++) {
124
+ bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
125
+ }
126
+ return bytes;
127
+ }
128
+ /**
129
+ * Utility: Convert Uint8Array to hex string
130
+ */
131
+ function bytesToHex(bytes) {
132
+ return Array.from(bytes)
133
+ .map(b => b.toString(16).padStart(2, '0'))
134
+ .join('');
135
+ }
136
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../../src/derive.ts"],"names":[],"mappings":"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"}