ehbp 0.0.7 → 0.1.1
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 +84 -138
- package/dist/cjs/client.d.ts +13 -13
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +30 -55
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/derive.d.ts +63 -0
- package/dist/cjs/derive.d.ts.map +1 -0
- package/dist/cjs/derive.js +136 -0
- package/dist/cjs/derive.js.map +1 -0
- package/dist/cjs/identity.d.ts +37 -10
- package/dist/cjs/identity.d.ts.map +1 -1
- package/dist/cjs/identity.js +152 -146
- package/dist/cjs/identity.js.map +1 -1
- package/dist/cjs/index.d.ts +4 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +15 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/protocol.d.ts +1 -2
- package/dist/cjs/protocol.d.ts.map +1 -1
- package/dist/cjs/protocol.js +1 -2
- package/dist/cjs/protocol.js.map +1 -1
- package/dist/esm/client.d.ts +13 -13
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +30 -55
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/derive.d.ts +63 -0
- package/dist/esm/derive.d.ts.map +1 -0
- package/dist/esm/derive.js +127 -0
- package/dist/esm/derive.js.map +1 -0
- package/dist/esm/identity.d.ts +37 -10
- package/dist/esm/identity.d.ts.map +1 -1
- package/dist/esm/identity.js +152 -146
- package/dist/esm/identity.js.map +1 -1
- package/dist/esm/index.d.ts +4 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/protocol.d.ts +1 -2
- package/dist/esm/protocol.d.ts.map +1 -1
- package/dist/esm/protocol.js +1 -2
- package/dist/esm/protocol.js.map +1 -1
- package/dist/esm/test/client.test.js +15 -16
- package/dist/esm/test/client.test.js.map +1 -1
- package/dist/esm/test/derive.test.d.ts +2 -0
- package/dist/esm/test/derive.test.d.ts.map +1 -0
- package/dist/esm/test/derive.test.js +164 -0
- package/dist/esm/test/derive.test.js.map +1 -0
- package/dist/esm/test/security.test.d.ts +10 -0
- package/dist/esm/test/security.test.d.ts.map +1 -0
- package/dist/esm/test/security.test.js +153 -0
- package/dist/esm/test/security.test.js.map +1 -0
- package/dist/esm/test/streaming.integration.d.ts +9 -0
- package/dist/esm/test/streaming.integration.d.ts.map +1 -0
- package/dist/esm/test/streaming.integration.js +190 -0
- package/dist/esm/test/streaming.integration.js.map +1 -0
- package/package.json +6 -7
- package/dist/esm/example.d.ts +0 -6
- package/dist/esm/example.d.ts.map +0 -1
- package/dist/esm/example.js +0 -115
- package/dist/esm/example.js.map +0 -1
- package/dist/esm/streaming-test.d.ts +0 -3
- package/dist/esm/streaming-test.d.ts.map +0 -1
- package/dist/esm/streaming-test.js +0 -102
- package/dist/esm/streaming-test.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
JavaScript/TypeScript client for [Encrypted HTTP Body Protocol (EHBP)](https://github.com/tinfoilsh/encrypted-http-body-protocol).
|
|
4
4
|
|
|
5
|
-
EHBP encrypts HTTP
|
|
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
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -15,198 +15,144 @@ npm install ehbp
|
|
|
15
15
|
- Node.js 20+
|
|
16
16
|
- Works in both Node.js and modern browsers
|
|
17
17
|
|
|
18
|
+
|
|
18
19
|
## Quick Start
|
|
19
20
|
|
|
20
21
|
```javascript
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
async function main() {
|
|
24
|
-
// Create client identity
|
|
25
|
-
const clientIdentity = await Identity.generate();
|
|
22
|
+
import { createTransport } from 'ehbp';
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
// Create transport (fetches server public key automatically)
|
|
25
|
+
const transport = await createTransport('https://example.com');
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
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
|
+
});
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
const data = await response.json();
|
|
37
33
|
```
|
|
38
34
|
|
|
39
|
-
## API
|
|
40
|
-
|
|
41
|
-
### `Identity`
|
|
35
|
+
## API
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
### `createTransport(serverURL: string): Promise<Transport>`
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Generates a new identity with X25519 key pair.
|
|
39
|
+
Creates a transport that automatically encrypts requests and decrypts responses.
|
|
48
40
|
|
|
49
41
|
```javascript
|
|
50
|
-
const
|
|
42
|
+
const transport = await createTransport('https://example.com');
|
|
51
43
|
```
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
### `transport.request(input, init?): Promise<Response>`
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
General-purpose method supporting all fetch options.
|
|
56
48
|
|
|
57
49
|
```javascript
|
|
58
|
-
const
|
|
59
|
-
|
|
50
|
+
const response = await transport.request('/api/data', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify({ key: 'value' })
|
|
54
|
+
});
|
|
60
55
|
```
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Loads an identity from JSON.
|
|
57
|
+
### Convenience Methods
|
|
65
58
|
|
|
66
59
|
```javascript
|
|
67
|
-
|
|
68
|
-
|
|
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');
|
|
69
64
|
```
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
## Browser Usage
|
|
72
67
|
|
|
73
|
-
|
|
68
|
+
```html
|
|
69
|
+
<script type="module">
|
|
70
|
+
import { createTransport } from './dist/browser.js';
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
console.log(
|
|
72
|
+
const transport = await createTransport('https://example.com');
|
|
73
|
+
const response = await transport.post('/api', 'Hello!');
|
|
74
|
+
console.log(await response.text());
|
|
75
|
+
</script>
|
|
78
76
|
```
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
HTTP transport that encrypts requests and decrypts responses.
|
|
83
|
-
|
|
84
|
-
#### `createTransport(serverURL: string, clientIdentity: Identity): Promise<Transport>`
|
|
85
|
-
|
|
86
|
-
Creates a new transport instance.
|
|
87
|
-
|
|
88
|
-
```javascript
|
|
89
|
-
const transport = await createTransport('http://localhost:8080', clientIdentity);
|
|
90
|
-
```
|
|
78
|
+
## Requirements
|
|
91
79
|
|
|
92
|
-
|
|
80
|
+
- Node.js 20+ or modern browsers with Web Crypto API
|
|
93
81
|
|
|
94
|
-
|
|
82
|
+
## Development
|
|
95
83
|
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
84
|
+
```sh
|
|
85
|
+
npm install
|
|
86
|
+
npm run build
|
|
87
|
+
npm test
|
|
88
|
+
npm run build:browser # Browser bundle
|
|
102
89
|
```
|
|
103
90
|
|
|
104
|
-
|
|
91
|
+
### Running Examples (Node.js)
|
|
105
92
|
|
|
106
|
-
|
|
107
|
-
- `transport.post(url: string | URL, body?: BodyInit, init?: RequestInit): Promise<Response>`
|
|
108
|
-
- `transport.put(url: string | URL, body?: BodyInit, init?: RequestInit): Promise<Response>`
|
|
109
|
-
- `transport.delete(url: string | URL, init?: RequestInit): Promise<Response>`
|
|
93
|
+
The Node.js example requires a build and an EHBP server running at `http://localhost:8080`:
|
|
110
94
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
import { Identity } from 'ehbp';
|
|
117
|
-
import { writeFile, readFile } from 'fs/promises';
|
|
95
|
+
```sh
|
|
96
|
+
# Build first
|
|
97
|
+
npm run build
|
|
118
98
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const identityJSON = await identity.toJSON();
|
|
122
|
-
await writeFile('client_identity.json', identityJSON);
|
|
99
|
+
# Start the Go server (from parent directory)
|
|
100
|
+
go run pkg/server/main.go
|
|
123
101
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const loadedIdentity = await Identity.fromJSON(loadedJSON);
|
|
102
|
+
# Run the example (in another terminal)
|
|
103
|
+
npm run example
|
|
127
104
|
```
|
|
128
105
|
|
|
129
|
-
###
|
|
106
|
+
### Running Examples (Browser)
|
|
130
107
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// POST with JSON
|
|
137
|
-
const postResponse = await transport.post(
|
|
138
|
-
'/api/users',
|
|
139
|
-
JSON.stringify({ name: 'Alice' }),
|
|
140
|
-
{ headers: { 'Content-Type': 'application/json' } }
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// PUT request
|
|
144
|
-
const putResponse = await transport.put(
|
|
145
|
-
'/api/users/123',
|
|
146
|
-
JSON.stringify({ name: 'Bob' }),
|
|
147
|
-
{ headers: { 'Content-Type': 'application/json' } }
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
// DELETE request
|
|
151
|
-
const deleteResponse = await transport.delete('/api/users/123');
|
|
152
|
-
```
|
|
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
|
|
153
113
|
|
|
154
|
-
|
|
114
|
+
# Start the local dev server
|
|
115
|
+
npm run serve
|
|
155
116
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const response = await transport.post('/secure', 'data');
|
|
159
|
-
if (!response.ok) {
|
|
160
|
-
console.error('Request failed:', response.status, response.statusText);
|
|
161
|
-
}
|
|
162
|
-
const data = await response.text();
|
|
163
|
-
console.log('Success:', data);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('Transport error:', error.message);
|
|
166
|
-
}
|
|
117
|
+
# Start the Go EHBP server (from parent directory, in another terminal)
|
|
118
|
+
go run pkg/server/main.go
|
|
167
119
|
```
|
|
168
120
|
|
|
169
|
-
|
|
121
|
+
Then open in your browser:
|
|
170
122
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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`) |
|
|
174
127
|
|
|
175
|
-
|
|
176
|
-
const transport = await createTransport('http://localhost:8080', identity);
|
|
128
|
+
**Note:** `chat.html` connects to port 8443 and expects an OpenAI-compatible chat completions API.
|
|
177
129
|
|
|
178
|
-
|
|
179
|
-
const data = await response.text();
|
|
180
|
-
console.log(data);
|
|
181
|
-
</script>
|
|
182
|
-
```
|
|
130
|
+
### Running Integration Tests
|
|
183
131
|
|
|
184
|
-
|
|
132
|
+
Integration tests verify streaming functionality against a live server:
|
|
185
133
|
|
|
186
134
|
```sh
|
|
187
|
-
#
|
|
188
|
-
|
|
135
|
+
# Start the Go server (from parent directory)
|
|
136
|
+
go run pkg/server/main.go
|
|
189
137
|
|
|
190
|
-
#
|
|
138
|
+
# Run integration tests (in another terminal)
|
|
191
139
|
npm run build
|
|
192
|
-
|
|
193
|
-
# Run tests
|
|
194
|
-
npm test
|
|
195
|
-
|
|
196
|
-
# Build browser bundle
|
|
197
|
-
npm run build:browser
|
|
140
|
+
npm run test:integration
|
|
198
141
|
```
|
|
199
142
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
For the complete protocol specification, see [SPEC.md](../SPEC.md) in the root of the repository.
|
|
143
|
+
### Commands
|
|
203
144
|
|
|
204
|
-
|
|
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 |
|
|
205
151
|
|
|
206
|
-
|
|
152
|
+
## Protocol
|
|
207
153
|
|
|
208
|
-
|
|
154
|
+
See [SPEC.md](../SPEC.md) for the complete protocol specification.
|
|
209
155
|
|
|
210
|
-
|
|
156
|
+
## Security
|
|
211
157
|
|
|
212
|
-
|
|
158
|
+
Report vulnerabilities to [security@tinfoil.sh](mailto:security@tinfoil.sh) or open a GitHub issue.
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -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
|
|
7
|
+
private serverIdentity;
|
|
7
8
|
private serverHost;
|
|
8
|
-
|
|
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
|
|
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():
|
|
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
|
-
*
|
|
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
|
|
50
|
+
export declare function createTransport(serverURL: string): Promise<Transport>;
|
|
51
51
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/cjs/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
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;IAuE9E;;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"}
|
package/dist/cjs/client.js
CHANGED
|
@@ -8,18 +8,16 @@ const protocol_js_1 = require("./protocol.js");
|
|
|
8
8
|
* HTTP transport for EHBP
|
|
9
9
|
*/
|
|
10
10
|
class Transport {
|
|
11
|
-
|
|
11
|
+
serverIdentity;
|
|
12
12
|
serverHost;
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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,33 +32,28 @@ 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
|
-
|
|
38
|
-
|
|
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.
|
|
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
|
-
|
|
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
59
|
// Skip EHBP for non-network URLs (data:, blob:)
|
|
@@ -99,45 +92,27 @@ class Transport {
|
|
|
99
92
|
method,
|
|
100
93
|
headers,
|
|
101
94
|
body: requestBody,
|
|
102
|
-
duplex: 'half'
|
|
95
|
+
duplex: 'half',
|
|
103
96
|
});
|
|
104
|
-
// Encrypt request
|
|
105
|
-
|
|
106
|
-
|
|
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(
|
|
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
|
-
//
|
|
124
|
-
|
|
125
|
-
if (fallbackHeader === '1') {
|
|
105
|
+
// Bodyless requests: context is null, response is plaintext
|
|
106
|
+
if (context === null) {
|
|
126
107
|
return response;
|
|
127
108
|
}
|
|
128
|
-
// Check for
|
|
129
|
-
const
|
|
130
|
-
if (!
|
|
131
|
-
throw new Error(`Missing ${protocol_js_1.PROTOCOL.
|
|
132
|
-
}
|
|
133
|
-
// Validate hex encoding
|
|
134
|
-
if (!/^[0-9a-fA-F]+$/.test(encapKeyHeader) || encapKeyHeader.length % 2 !== 0) {
|
|
135
|
-
throw new Error(`Invalid ${protocol_js_1.PROTOCOL.ENCAPSULATED_KEY_HEADER} header: must be valid hex string with even length`);
|
|
109
|
+
// Check for response nonce header (required for response decryption)
|
|
110
|
+
const responseNonceHeader = response.headers.get(protocol_js_1.PROTOCOL.RESPONSE_NONCE_HEADER);
|
|
111
|
+
if (!responseNonceHeader) {
|
|
112
|
+
throw new Error(`Missing ${protocol_js_1.PROTOCOL.RESPONSE_NONCE_HEADER} header`);
|
|
136
113
|
}
|
|
137
|
-
// Decode encapsulated key
|
|
138
|
-
const serverEncapKey = new Uint8Array(encapKeyHeader.match(/.{2}/g).map(byte => parseInt(byte, 16)));
|
|
139
114
|
// Decrypt response
|
|
140
|
-
return await this.
|
|
115
|
+
return await this.serverIdentity.decryptResponseWithContext(response, context);
|
|
141
116
|
}
|
|
142
117
|
/**
|
|
143
118
|
* Convenience method for GET requests
|
|
@@ -166,9 +141,9 @@ class Transport {
|
|
|
166
141
|
}
|
|
167
142
|
exports.Transport = Transport;
|
|
168
143
|
/**
|
|
169
|
-
* Create a new transport instance
|
|
144
|
+
* Create a new transport instance.
|
|
170
145
|
*/
|
|
171
|
-
async function createTransport(serverURL
|
|
172
|
-
return Transport.create(serverURL
|
|
146
|
+
async function createTransport(serverURL) {
|
|
147
|
+
return Transport.create(serverURL);
|
|
173
148
|
}
|
|
174
149
|
//# sourceMappingURL=client.js.map
|
package/dist/cjs/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":";;;
|
|
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"}
|