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
@@ -6,10 +6,10 @@ exports.HPKE_CONFIG = exports.PROTOCOL = void 0;
6
6
  */
7
7
  exports.PROTOCOL = {
8
8
  ENCAPSULATED_KEY_HEADER: 'Ehbp-Encapsulated-Key',
9
- CLIENT_PUBLIC_KEY_HEADER: 'Ehbp-Client-Public-Key',
9
+ RESPONSE_NONCE_HEADER: 'Ehbp-Response-Nonce',
10
10
  KEYS_MEDIA_TYPE: 'application/ohttp-keys',
11
11
  KEYS_PATH: '/.well-known/hpke-keys',
12
- FALLBACK_HEADER: 'Ehbp-Fallback'
12
+ FALLBACK_HEADER: 'Ehbp-Fallback',
13
13
  };
14
14
  /**
15
15
  * HPKE suite configuration matching the Go implementation
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,QAAQ,GAAG;IACtB,uBAAuB,EAAE,uBAAuB;IAChD,wBAAwB,EAAE,wBAAwB;IAClD,eAAe,EAAE,wBAAwB;IACzC,SAAS,EAAE,wBAAwB;IACnC,eAAe,EAAE,eAAe;CACxB,CAAC;AAEX;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB,GAAG,EAAE,MAAM,EAAE,qBAAqB;IAClC,GAAG,EAAE,MAAM,EAAE,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC,cAAc;CACnB,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,QAAQ,GAAG;IACtB,uBAAuB,EAAE,uBAAuB;IAChD,qBAAqB,EAAE,qBAAqB;IAC5C,eAAe,EAAE,wBAAwB;IACzC,SAAS,EAAE,wBAAwB;IACnC,eAAe,EAAE,eAAe;CACxB,CAAC;AAEX;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB,GAAG,EAAE,MAAM,EAAE,qBAAqB;IAClC,GAAG,EAAE,MAAM,EAAE,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC,cAAc;CACnB,CAAC"}
@@ -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"}
@@ -4,18 +4,16 @@ import { PROTOCOL } from './protocol.js';
4
4
  * HTTP transport for EHBP
5
5
  */
6
6
  export class Transport {
7
- clientIdentity;
7
+ serverIdentity;
8
8
  serverHost;
9
- serverPublicKey;
10
- constructor(clientIdentity, serverHost, serverPublicKey) {
11
- this.clientIdentity = clientIdentity;
9
+ constructor(serverIdentity, serverHost) {
10
+ this.serverIdentity = serverIdentity;
12
11
  this.serverHost = serverHost;
13
- this.serverPublicKey = serverPublicKey;
14
12
  }
15
13
  /**
16
- * Create a new transport by fetching server public key
14
+ * Create a new transport by fetching server public key.
17
15
  */
18
- static async create(serverURL, clientIdentity) {
16
+ static async create(serverURL) {
19
17
  const url = new URL(serverURL);
20
18
  const serverHost = url.host;
21
19
  // Fetch server public key
@@ -30,38 +28,33 @@ export class Transport {
30
28
  }
31
29
  const keysData = new Uint8Array(await response.arrayBuffer());
32
30
  const serverIdentity = await Identity.unmarshalPublicConfig(keysData);
33
- const serverPublicKey = serverIdentity.getPublicKey();
34
- return new Transport(clientIdentity, serverHost, serverPublicKey);
31
+ return new Transport(serverIdentity, serverHost);
32
+ }
33
+ /**
34
+ * Get the server identity
35
+ */
36
+ getServerIdentity() {
37
+ return this.serverIdentity;
35
38
  }
36
39
  /**
37
40
  * Get the server public key
38
41
  */
39
42
  getServerPublicKey() {
40
- return this.serverPublicKey;
43
+ return this.serverIdentity.getPublicKey();
41
44
  }
42
45
  /**
43
46
  * Get the server public key as hex string
44
47
  */
45
48
  async getServerPublicKeyHex() {
46
- const exported = await crypto.subtle.exportKey('raw', this.serverPublicKey);
47
- const keyBytes = new Uint8Array(exported);
48
- return Array.from(keyBytes)
49
- .map(b => b.toString(16).padStart(2, '0'))
50
- .join('');
51
- }
52
- /**
53
- * Get the client public key
54
- */
55
- getClientPublicKey() {
56
- return this.clientIdentity.getPublicKey();
49
+ return this.serverIdentity.getPublicKeyHex();
57
50
  }
58
51
  /**
59
- * Make an encrypted HTTP request
52
+ * Make an encrypted HTTP request.
60
53
  */
61
54
  async request(input, init) {
62
- // Skip EHBP for data: URLs (e.g., used for FormData detection)
55
+ // Skip EHBP for non-network URLs (data:, blob:)
63
56
  const inputUrl = input instanceof Request ? input.url : String(input);
64
- if (inputUrl.startsWith('data:')) {
57
+ if (inputUrl.startsWith('data:') || inputUrl.startsWith('blob:')) {
65
58
  return fetch(input, init);
66
59
  }
67
60
  // Extract body from init or original request before creating Request object
@@ -95,36 +88,32 @@ export class Transport {
95
88
  method,
96
89
  headers,
97
90
  body: requestBody,
98
- duplex: 'half'
91
+ duplex: 'half',
99
92
  });
100
- // Encrypt request body if present (check the original requestBody, not request.body)
101
- if (requestBody !== null && requestBody !== undefined) {
102
- request = await this.clientIdentity.encryptRequest(request, this.serverPublicKey);
103
- }
104
- else {
105
- // No body, just set client public key header
106
- const headers = new Headers(request.headers);
107
- headers.set(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER, await this.clientIdentity.getPublicKeyHex());
108
- request = new Request(request.url, {
109
- method: request.method,
110
- headers,
111
- body: null
112
- });
113
- }
93
+ // Encrypt request (returns context for response decryption)
94
+ // For bodyless requests, context will be null and request passes through unmodified
95
+ const { request: encryptedRequest, context } = await this.serverIdentity.encryptRequestWithContext(request);
114
96
  // Make the request
115
- const response = await fetch(request);
97
+ const response = await fetch(encryptedRequest);
116
98
  if (!response.ok) {
117
99
  console.warn(`Server returned non-OK status: ${response.status}`);
118
100
  }
119
- // Check for encapsulated key header
120
- const encapKeyHeader = response.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER);
121
- if (!encapKeyHeader) {
122
- throw new Error(`Missing ${PROTOCOL.ENCAPSULATED_KEY_HEADER} encapsulated key header`);
101
+ // Bodyless requests: context is null, response is plaintext
102
+ if (context === null) {
103
+ return response;
104
+ }
105
+ // Check for fallback header - if set, server returned unencrypted response
106
+ const fallbackHeader = response.headers.get(PROTOCOL.FALLBACK_HEADER);
107
+ if (fallbackHeader === '1') {
108
+ return response;
109
+ }
110
+ // Check for response nonce header (required for response decryption)
111
+ const responseNonceHeader = response.headers.get(PROTOCOL.RESPONSE_NONCE_HEADER);
112
+ if (!responseNonceHeader) {
113
+ throw new Error(`Missing ${PROTOCOL.RESPONSE_NONCE_HEADER} header`);
123
114
  }
124
- // Decode encapsulated key
125
- const serverEncapKey = new Uint8Array(encapKeyHeader.match(/.{2}/g).map(byte => parseInt(byte, 16)));
126
115
  // Decrypt response
127
- return await this.clientIdentity.decryptResponse(response, serverEncapKey);
116
+ return await this.serverIdentity.decryptResponseWithContext(response, context);
128
117
  }
129
118
  /**
130
119
  * Convenience method for GET requests
@@ -152,9 +141,9 @@ export class Transport {
152
141
  }
153
142
  }
154
143
  /**
155
- * Create a new transport instance
144
+ * Create a new transport instance.
156
145
  */
157
- export async function createTransport(serverURL, clientIdentity) {
158
- return Transport.create(serverURL, clientIdentity);
146
+ export async function createTransport(serverURL) {
147
+ return Transport.create(serverURL);
159
148
  }
160
149
  //# 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,127 @@
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
+ import { KDF_HKDF_SHA256, AEAD_AES_256_GCM } from 'hpke';
13
+ const kdf = KDF_HKDF_SHA256();
14
+ const aead = AEAD_AES_256_GCM();
15
+ export const HPKE_REQUEST_INFO = 'ehbp request';
16
+ export const EXPORT_LABEL = 'ehbp response';
17
+ export const EXPORT_LENGTH = 32;
18
+ export const RESPONSE_NONCE_LENGTH = 32; // max(Nn, Nk) = max(12, 32) = 32
19
+ export const AES256_KEY_LENGTH = 32;
20
+ export const AES_GCM_NONCE_LENGTH = 12;
21
+ export const REQUEST_ENC_LENGTH = 32; // X25519 enc size
22
+ // Labels for HKDF-Expand
23
+ const RESPONSE_KEY_LABEL = new TextEncoder().encode('key');
24
+ const RESPONSE_NONCE_LABEL = new TextEncoder().encode('nonce');
25
+ /**
26
+ * Derives response encryption keys from the HPKE exported secret.
27
+ *
28
+ * salt = concat(enc, response_nonce)
29
+ * prk = Extract(salt, secret)
30
+ * key = Expand(prk, "key", 32)
31
+ * nonceBase = Expand(prk, "nonce", 12)
32
+ *
33
+ * @param exportedSecret - 32 bytes exported from HPKE context
34
+ * @param requestEnc - 32 bytes encapsulated key from request
35
+ * @param responseNonce - 32 bytes random nonce from response
36
+ * @returns Key material for response encryption/decryption
37
+ */
38
+ export async function deriveResponseKeys(exportedSecret, requestEnc, responseNonce) {
39
+ // Validate inputs
40
+ if (exportedSecret.length !== EXPORT_LENGTH) {
41
+ throw new Error(`exported secret must be ${EXPORT_LENGTH} bytes, got ${exportedSecret.length}`);
42
+ }
43
+ if (requestEnc.length !== REQUEST_ENC_LENGTH) {
44
+ throw new Error(`request enc must be ${REQUEST_ENC_LENGTH} bytes, got ${requestEnc.length}`);
45
+ }
46
+ if (responseNonce.length !== RESPONSE_NONCE_LENGTH) {
47
+ throw new Error(`response nonce must be ${RESPONSE_NONCE_LENGTH} bytes, got ${responseNonce.length}`);
48
+ }
49
+ // salt = concat(enc, response_nonce)
50
+ const salt = new Uint8Array(requestEnc.length + responseNonce.length);
51
+ salt.set(requestEnc, 0);
52
+ salt.set(responseNonce, requestEnc.length);
53
+ // prk = Extract(salt, secret)
54
+ const prk = await kdf.Extract(salt, exportedSecret);
55
+ // key = Expand(prk, "key", 32)
56
+ const keyBytes = await kdf.Expand(prk, RESPONSE_KEY_LABEL, AES256_KEY_LENGTH);
57
+ // nonceBase = Expand(prk, "nonce", 12)
58
+ const nonceBase = await kdf.Expand(prk, RESPONSE_NONCE_LABEL, AES_GCM_NONCE_LENGTH);
59
+ return { keyBytes, nonceBase };
60
+ }
61
+ /**
62
+ * Computes the nonce for a specific sequence number.
63
+ * nonce = nonceBase XOR sequence_number (big-endian in last 8 bytes)
64
+ */
65
+ export function computeNonce(nonceBase, seq) {
66
+ if (nonceBase.length !== AES_GCM_NONCE_LENGTH) {
67
+ throw new Error(`nonce base must be ${AES_GCM_NONCE_LENGTH} bytes`);
68
+ }
69
+ // Validate seq to prevent nonce reuse from integer overflow.
70
+ // JavaScript's >>> operator only works correctly for 32-bit unsigned integers.
71
+ // Values >= 2^32 wrap around (e.g., 2^32 >>> 0 === 0), causing nonce reuse.
72
+ // In practice, 2^32 chunks per response is impossible (~4PB minimum), but we validate defensively.
73
+ if (!Number.isInteger(seq) || seq < 0 || seq >= 0x100000000) {
74
+ throw new Error(`sequence number must be an integer in range [0, 2^32): got ${seq}`);
75
+ }
76
+ const nonce = new Uint8Array(AES_GCM_NONCE_LENGTH);
77
+ nonce.set(nonceBase);
78
+ // XOR with sequence number in the last 8 bytes (big-endian)
79
+ for (let i = 0; i < 8; i++) {
80
+ const shift = i * 8;
81
+ if (shift < 32) {
82
+ nonce[AES_GCM_NONCE_LENGTH - 1 - i] ^= (seq >>> shift) & 0xff;
83
+ }
84
+ }
85
+ return nonce;
86
+ }
87
+ /**
88
+ * Encrypts a chunk using the response key material
89
+ */
90
+ export async function encryptChunk(km, seq, plaintext) {
91
+ const nonce = computeNonce(km.nonceBase, seq);
92
+ const ciphertext = await aead.Seal(km.keyBytes, nonce, new Uint8Array(0), plaintext);
93
+ return ciphertext;
94
+ }
95
+ /**
96
+ * Decrypts a chunk using the response key material
97
+ */
98
+ export async function decryptChunk(km, seq, ciphertext) {
99
+ const nonce = computeNonce(km.nonceBase, seq);
100
+ const plaintext = await aead.Open(km.keyBytes, nonce, new Uint8Array(0), ciphertext);
101
+ return plaintext;
102
+ }
103
+ /**
104
+ * Utility: Convert hex string to Uint8Array
105
+ */
106
+ export function hexToBytes(hex) {
107
+ if (hex.length % 2 !== 0) {
108
+ throw new Error('Hex string must have even length');
109
+ }
110
+ if (!/^[0-9a-fA-F]*$/.test(hex)) {
111
+ throw new Error('Invalid hex character');
112
+ }
113
+ const bytes = new Uint8Array(hex.length / 2);
114
+ for (let i = 0; i < bytes.length; i++) {
115
+ bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
116
+ }
117
+ return bytes;
118
+ }
119
+ /**
120
+ * Utility: Convert Uint8Array to hex string
121
+ */
122
+ export function bytesToHex(bytes) {
123
+ return Array.from(bytes)
124
+ .map(b => b.toString(16).padStart(2, '0'))
125
+ .join('');
126
+ }
127
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../../src/derive.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,12 @@
1
- import { CipherSuite } from '@hpke/core';
1
+ import { CipherSuite, type SenderContext, type Key } from 'hpke';
2
+ /**
3
+ * Request context for response decryption.
4
+ * Holds the HPKE sender context needed to derive response keys.
5
+ */
6
+ export interface RequestContext {
7
+ senderContext: SenderContext;
8
+ requestEnc: Uint8Array;
9
+ }
2
10
  /**
3
11
  * Identity class for managing HPKE key pairs and encryption/decryption
4
12
  */
@@ -6,7 +14,7 @@ export declare class Identity {
6
14
  private suite;
7
15
  private publicKey;
8
16
  private privateKey;
9
- constructor(suite: CipherSuite, publicKey: CryptoKey, privateKey: CryptoKey);
17
+ constructor(suite: CipherSuite, publicKey: Key, privateKey: Key);
10
18
  /**
11
19
  * Generate a new identity with X25519 key pair
12
20
  */
@@ -20,17 +28,17 @@ export declare class Identity {
20
28
  */
21
29
  toJSON(): Promise<string>;
22
30
  /**
23
- * Get public key as CryptoKey
31
+ * Get public key
24
32
  */
25
- getPublicKey(): CryptoKey;
33
+ getPublicKey(): Key;
26
34
  /**
27
35
  * Get public key as hex string
28
36
  */
29
37
  getPublicKeyHex(): Promise<string>;
30
38
  /**
31
- * Get private key as CryptoKey
39
+ * Get private key
32
40
  */
33
- getPrivateKey(): CryptoKey;
41
+ getPrivateKey(): Key;
34
42
  /**
35
43
  * Marshal public key configuration for server key distribution
36
44
  * Implements RFC 9458 format
@@ -41,12 +49,31 @@ export declare class Identity {
41
49
  */
42
50
  static unmarshalPublicConfig(data: Uint8Array): Promise<Identity>;
43
51
  /**
44
- * Encrypt request body and set appropriate headers
52
+ * Encrypt request body and return context for response decryption.
53
+ *
54
+ * This method is called on the SERVER's identity (public key only).
55
+ * It:
56
+ * 1. Creates an HPKE sender context to this identity's public key
57
+ * 2. Encrypts the request body
58
+ * 3. Returns a RequestContext that must be used to decrypt the response
59
+ */
60
+ encryptRequestWithContext(request: Request): Promise<{
61
+ request: Request;
62
+ context: RequestContext | null;
63
+ }>;
64
+ /**
65
+ * Decrypt response using keys derived from request context.
66
+ *
67
+ * This method:
68
+ * 1. Reads the response nonce from Ehbp-Response-Nonce header
69
+ * 2. Exports a secret from the HPKE sender context
70
+ * 3. Derives response keys using HKDF
71
+ * 4. Decrypts the response body
45
72
  */
46
- encryptRequest(request: Request, serverPublicKey: CryptoKey): Promise<Request>;
73
+ decryptResponseWithContext(response: Response, context: RequestContext): Promise<Response>;
47
74
  /**
48
- * Decrypt response body
75
+ * Creates a ReadableStream that decrypts response chunks.
49
76
  */
50
- decryptResponse(response: Response, serverEncapKey: Uint8Array): Promise<Response>;
77
+ private createDecryptStream;
51
78
  }
52
79
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAgD,MAAM,YAAY,CAAC;AAGvF;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAY;gBAElB,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS;IAM3E;;OAEG;WACU,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAsB1C;;OAEG;WACU,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAwBtD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAY/B;;OAEG;IACH,YAAY,IAAI,SAAS;IAIzB;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAOxC;;OAEG;IACH,aAAa,IAAI,SAAS;IAI1B;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IA0C1C;;OAEG;WACU,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAwCvE;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAgDpF;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;CAwFzF"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAIX,KAAK,aAAa,EAClB,KAAK,GAAG,EACT,MAAM,MAAM,CAAC;AAcd;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B,UAAU,EAAE,UAAU,CAAC;CACxB;AAaD;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,UAAU,CAAM;gBAEZ,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG;IAM/D;;OAEG;WACU,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAO1C;;OAEG;WACU,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAWtD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAU/B;;OAEG;IACH,YAAY,IAAI,GAAG;IAInB;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAKxC;;OAEG;IACH,aAAa,IAAI,GAAG;IAIpB;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IA0C1C;;OAEG;WACU,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IAqDvE;;;;;;;;OAQG;IACG,yBAAyB,CAC7B,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;KAAE,CAAC;IAwDhE;;;;;;;;OAQG;IACG,0BAA0B,CAC9B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,QAAQ,CAAC;IAmCpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAyD5B"}