@hpke/hybridkem-x-wing 0.7.0 → 0.7.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 +57 -61
- package/esm/package.json +2 -1
- package/esm/src/xWing.d.ts +3 -3
- package/esm/src/xWing.d.ts.map +1 -1
- package/esm/src/xWing.js +18 -24
- package/package.json +8 -10
- package/script/src/xWing.d.ts +3 -3
- package/script/src/xWing.d.ts.map +1 -1
- package/script/src/xWing.js +18 -24
package/README.md
CHANGED
|
@@ -75,6 +75,10 @@ try {
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
Auth and AuthPSK require a KEM that supports AuthEncap/AuthDecap. ML-KEM, X-Wing
|
|
79
|
+
and X25519-Kyber768 support only Base and PSK modes; requesting authentication
|
|
80
|
+
with these KEMs throws `NotSupportedError` (RFC 9180 Section 7.1.5).
|
|
81
|
+
|
|
78
82
|
## Index
|
|
79
83
|
|
|
80
84
|
- [Installation](#installation)
|
|
@@ -133,24 +137,20 @@ Using esm.sh:
|
|
|
133
137
|
```html
|
|
134
138
|
<!-- use a specific version -->
|
|
135
139
|
<script type="module">
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
import {
|
|
141
|
+
Aes256Gcm,
|
|
142
|
+
CipherSuite,
|
|
143
|
+
HkdfSha256,
|
|
144
|
+
} from "https://esm.sh/@hpke/core@<SEMVER>";
|
|
145
|
+
import { XWing } from "https://esm.sh/@hpke/hybridkem-x-wing@<SEMVER>";
|
|
146
|
+
// ...
|
|
143
147
|
</script>
|
|
144
148
|
|
|
145
149
|
<!-- use the latest stable version -->
|
|
146
150
|
<script type="module">
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
HkdfSha256,
|
|
151
|
-
} from "https://esm.sh/@hpke/core";
|
|
152
|
-
import { XWing } from "https://esm.sh/@hpke/hybridkem-x-wing";
|
|
153
|
-
// ...
|
|
151
|
+
import { Aes256Gcm, CipherSuite, HkdfSha256 } from "https://esm.sh/@hpke/core";
|
|
152
|
+
import { XWing } from "https://esm.sh/@hpke/hybridkem-x-wing";
|
|
153
|
+
// ...
|
|
154
154
|
</script>
|
|
155
155
|
```
|
|
156
156
|
|
|
@@ -159,13 +159,13 @@ Using unpkg:
|
|
|
159
159
|
```html
|
|
160
160
|
<!-- use a specific version -->
|
|
161
161
|
<script type="module">
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
import {
|
|
163
|
+
Aes256Gcm,
|
|
164
|
+
CipherSuite,
|
|
165
|
+
HkdfSha256,
|
|
166
|
+
} from "https://unpkg.com/@hpke/core@<SEMVER>/esm/mod.js";
|
|
167
|
+
import { XWing } from "https://unpkg.com/@hpke/hybridkem-x-wing@<SEMVER>/esm/mod.js";
|
|
168
|
+
// ...
|
|
169
169
|
</script>
|
|
170
170
|
```
|
|
171
171
|
|
|
@@ -266,46 +266,42 @@ try {
|
|
|
266
266
|
<head></head>
|
|
267
267
|
<body>
|
|
268
268
|
<script type="module">
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
} catch (err) {
|
|
306
|
-
alert("failed:", err.message);
|
|
307
|
-
}
|
|
308
|
-
};
|
|
269
|
+
import { Aes256Gcm, CipherSuite, HkdfSha256 } from "https://esm.sh/@hpke/core";
|
|
270
|
+
import { XWing } from "https://esm.sh/@hpke/hybridkem-x-wing";
|
|
271
|
+
|
|
272
|
+
globalThis.doHpke = async () => {
|
|
273
|
+
try {
|
|
274
|
+
const suite = new CipherSuite({
|
|
275
|
+
kem: new XWing(),
|
|
276
|
+
kdf: new HkdfSha256(),
|
|
277
|
+
aead: new Aes256Gcm(),
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const rkp = await suite.kem.generateKeyPair();
|
|
281
|
+
|
|
282
|
+
// Note that the `ct` resulting from X-Wing::Encapsulate() is set to `sender.enc`.
|
|
283
|
+
const sender = await suite.createSenderContext({
|
|
284
|
+
recipientPublicKey: rkp.publicKey,
|
|
285
|
+
});
|
|
286
|
+
// encrypt
|
|
287
|
+
const encrypted = await sender.seal(
|
|
288
|
+
new TextEncoder().encode("Hello world!"),
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const recipient = await suite.createRecipientContext({
|
|
292
|
+
recipientKey: rkp.privateKey, // rkp (CryptoKeyPair) is also acceptable.
|
|
293
|
+
enc: sender.enc, // == `ct` (ciphertext) in the context of X-Wing
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// decrypt
|
|
297
|
+
const pt = await recipient.open(encrypted);
|
|
298
|
+
|
|
299
|
+
// Hello world!
|
|
300
|
+
alert(new TextDecoder().decode(pt));
|
|
301
|
+
} catch (err) {
|
|
302
|
+
alert("failed:", err.message);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
309
305
|
</script>
|
|
310
306
|
<button type="button" onclick="doHpke()">do HPKE</button>
|
|
311
307
|
</body>
|
package/esm/package.json
CHANGED
package/esm/src/xWing.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MlKem768 } from "mlkem";
|
|
2
2
|
import type { JsonWebKeyExtended, KemInterface, RecipientContextParams, SenderContextParams } from "@hpke/common";
|
|
3
|
-
import { KemId } from "@hpke/common";
|
|
4
|
-
import { X25519 } from "@hpke/dhkem-x25519";
|
|
3
|
+
import type { KemId } from "@hpke/common";
|
|
5
4
|
/**
|
|
6
5
|
* The Hybrid Post-Quantum KEM (X25519, Kyber768).
|
|
7
6
|
*
|
|
@@ -32,7 +31,6 @@ export declare class XWing implements KemInterface {
|
|
|
32
31
|
readonly privateKeySize: number;
|
|
33
32
|
readonly auth: boolean;
|
|
34
33
|
protected _m: MlKem768;
|
|
35
|
-
protected _x25519: X25519;
|
|
36
34
|
private _api;
|
|
37
35
|
constructor();
|
|
38
36
|
serializePublicKey(key: CryptoKey): Promise<ArrayBuffer>;
|
|
@@ -77,6 +75,7 @@ export declare class XWing implements KemInterface {
|
|
|
77
75
|
* @returns {Promise<{ sharedSecret: ArrayBuffer; enc: ArrayBuffer }>} A promise that resolves with the `ss` (shared secret) as `sharedSecret` and the `ct` (ciphertext) as `enc`.
|
|
78
76
|
* @throws {InvalidParamError} Thrown if the length of the `ekm` is not 64 bytes.
|
|
79
77
|
* @throws {EncapError} Thrown if the shared secret cannot be encapsulated.
|
|
78
|
+
* @throws {NotSupportedError} if `senderKey` is provided.
|
|
80
79
|
*/
|
|
81
80
|
encap(params: SenderContextParams): Promise<{
|
|
82
81
|
sharedSecret: ArrayBuffer;
|
|
@@ -90,6 +89,7 @@ export declare class XWing implements KemInterface {
|
|
|
90
89
|
* @returns {Promise<ArrayBuffer>} A promise that resolves with the shared secret.
|
|
91
90
|
* @throws {InvalidParamError} Thrown if the length of the `enc` is not 1120 bytes.
|
|
92
91
|
* @throws {DecapError} Thrown if the shared secret cannot be decapsulated.
|
|
92
|
+
* @throws {NotSupportedError} if `senderPublicKey` is provided.
|
|
93
93
|
*/
|
|
94
94
|
decap(params: RecipientContextParams): Promise<ArrayBuffer>;
|
|
95
95
|
/**
|
package/esm/src/xWing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xWing.d.ts","sourceRoot":"","sources":["../../src/src/xWing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAsB,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"xWing.d.ts","sourceRoot":"","sources":["../../src/src/xWing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAsB,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAmBtB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AA4B1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,KAAM,YAAW,YAAY;IACxC,SAAgB,EAAE,EAAE,KAAK,CAAU;IACnC,SAAgB,IAAI,EAAE,MAAM,CAAY;IACxC,SAAgB,UAAU,EAAE,MAAM,CAAM;IACxC,SAAgB,OAAO,EAAE,MAAM,CAAQ;IACvC,SAAgB,aAAa,EAAE,MAAM,CAAQ;IAC7C,SAAgB,cAAc,EAAE,MAAM,CAAM;IAC5C,SAAgB,IAAI,EAAE,OAAO,CAAS;IACtC,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC;IACvB,OAAO,CAAC,IAAI,CAAiC;;IAMhC,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IASxD,oBAAoB,CAC/B,GAAG,EAAE,eAAe,GAAG,eAAe,GACrC,OAAO,CAAC,SAAS,CAAC;IASR,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IASzD,qBAAqB,CAChC,GAAG,EAAE,eAAe,GAAG,eAAe,GACrC,OAAO,CAAC,SAAS,CAAC;IASrB;;;;OAIG;IACU,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC;IAkBtD;;;;;;OAMG;IACU,qBAAqB,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAc1E;;;;;;;OAOG;IACU,aAAa,CACxB,GAAG,EAAE,eAAe,GAAG,eAAe,GACrC,OAAO,CAAC,aAAa,CAAC;IAgBzB;;;;;;;OAOG;IACU,SAAS,CACpB,MAAM,EAAE,KAAK,GAAG,KAAK,EACrB,GAAG,EAAE,WAAW,GAAG,kBAAkB,EACrC,QAAQ,UAAO,GACd,OAAO,CAAC,SAAS,CAAC;IAmCrB;;;;;;;OAOG;IACU,KAAK,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC;QAAE,YAAY,EAAE,WAAW,CAAC;QAAC,GAAG,EAAE,WAAW,CAAA;KAAE,CAAC;IAkD3D;;;;;;;;;OASG;IACU,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IA+BxE;;;;OAIG;YACW,MAAM;IAOpB;;;;OAIG;YACW,sBAAsB;IAOpC;;;;OAIG;YACW,uBAAuB;IAUrC,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,UAAU;CAmCnB"}
|
package/esm/src/xWing.js
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
// @ts-ignore: Unreachable code error
|
|
2
2
|
import { MlKem768, sha3_256, shake256 } from "mlkem";
|
|
3
|
-
import { base64UrlToBytes, concat, DecapError, DeriveKeyPairError, DeserializeError, EncapError, InvalidParamError, isCryptoKeyPair, KEM_USAGES,
|
|
3
|
+
import { base64UrlToBytes, concat, DecapError, DeriveKeyPairError, DeserializeError, EncapError, InvalidParamError, isCryptoKeyPair, KEM_USAGES, loadCrypto, NotSupportedError, SerializeError, toArrayBuffer, x25519, XCryptoKey, } from "@hpke/common";
|
|
4
4
|
const ALG_NAME = "X-Wing";
|
|
5
|
-
import { HkdfSha256, X25519 } from "@hpke/dhkem-x25519";
|
|
6
|
-
// deno-fmt-ignore
|
|
7
|
-
const X25519_BASE = new Uint8Array([
|
|
8
|
-
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
9
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
10
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
11
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
12
|
-
]);
|
|
13
5
|
// XWingLabel = concat(
|
|
14
6
|
// "\./",
|
|
15
7
|
// "/^\",
|
|
@@ -52,8 +44,8 @@ export class XWing {
|
|
|
52
44
|
enumerable: true,
|
|
53
45
|
configurable: true,
|
|
54
46
|
writable: true,
|
|
55
|
-
value:
|
|
56
|
-
});
|
|
47
|
+
value: 0x647a
|
|
48
|
+
}); // KemId.XWing
|
|
57
49
|
Object.defineProperty(this, "name", {
|
|
58
50
|
enumerable: true,
|
|
59
51
|
configurable: true,
|
|
@@ -96,12 +88,6 @@ export class XWing {
|
|
|
96
88
|
writable: true,
|
|
97
89
|
value: void 0
|
|
98
90
|
});
|
|
99
|
-
Object.defineProperty(this, "_x25519", {
|
|
100
|
-
enumerable: true,
|
|
101
|
-
configurable: true,
|
|
102
|
-
writable: true,
|
|
103
|
-
value: void 0
|
|
104
|
-
});
|
|
105
91
|
Object.defineProperty(this, "_api", {
|
|
106
92
|
enumerable: true,
|
|
107
93
|
configurable: true,
|
|
@@ -109,7 +95,6 @@ export class XWing {
|
|
|
109
95
|
value: undefined
|
|
110
96
|
});
|
|
111
97
|
this._m = new MlKem768();
|
|
112
|
-
this._x25519 = new X25519(new HkdfSha256());
|
|
113
98
|
}
|
|
114
99
|
async serializePublicKey(key) {
|
|
115
100
|
await this._setup();
|
|
@@ -263,8 +248,12 @@ export class XWing {
|
|
|
263
248
|
* @returns {Promise<{ sharedSecret: ArrayBuffer; enc: ArrayBuffer }>} A promise that resolves with the `ss` (shared secret) as `sharedSecret` and the `ct` (ciphertext) as `enc`.
|
|
264
249
|
* @throws {InvalidParamError} Thrown if the length of the `ekm` is not 64 bytes.
|
|
265
250
|
* @throws {EncapError} Thrown if the shared secret cannot be encapsulated.
|
|
251
|
+
* @throws {NotSupportedError} if `senderKey` is provided.
|
|
266
252
|
*/
|
|
267
253
|
async encap(params) {
|
|
254
|
+
if (params.senderKey !== undefined) {
|
|
255
|
+
throw new NotSupportedError("Auth and AuthPSK modes are not supported by this KEM");
|
|
256
|
+
}
|
|
268
257
|
let ekm = undefined;
|
|
269
258
|
if (params.ekm !== undefined && !isCryptoKeyPair(params.ekm)) {
|
|
270
259
|
ekm = toArrayBuffer(params.ekm);
|
|
@@ -281,13 +270,14 @@ export class XWing {
|
|
|
281
270
|
ekX = ek.subarray(32, 64);
|
|
282
271
|
}
|
|
283
272
|
else {
|
|
284
|
-
|
|
273
|
+
const randomEkX = new Uint8Array(32);
|
|
285
274
|
try {
|
|
286
|
-
this._api.getRandomValues(
|
|
275
|
+
this._api.getRandomValues(randomEkX);
|
|
287
276
|
}
|
|
288
277
|
catch (e) {
|
|
289
278
|
throw new NotSupportedError(e);
|
|
290
279
|
}
|
|
280
|
+
ekX = randomEkX;
|
|
291
281
|
}
|
|
292
282
|
const pk = new Uint8Array(await this.serializePublicKey(params.recipientPublicKey));
|
|
293
283
|
if (pk.byteLength !== 1216) {
|
|
@@ -296,8 +286,8 @@ export class XWing {
|
|
|
296
286
|
try {
|
|
297
287
|
const pkM = pk.subarray(0, 1184);
|
|
298
288
|
const pkX = pk.subarray(1184, 1216);
|
|
299
|
-
const ctX =
|
|
300
|
-
const ssX =
|
|
289
|
+
const ctX = x25519.getPublicKey(ekX);
|
|
290
|
+
const ssX = x25519.getSharedSecret(ekX, pkX);
|
|
301
291
|
const [ctM, ssM] = await this._m.encap(pkM, ekM);
|
|
302
292
|
return {
|
|
303
293
|
sharedSecret: combiner(ssM, ssX, ctX, pkX).buffer,
|
|
@@ -316,8 +306,12 @@ export class XWing {
|
|
|
316
306
|
* @returns {Promise<ArrayBuffer>} A promise that resolves with the shared secret.
|
|
317
307
|
* @throws {InvalidParamError} Thrown if the length of the `enc` is not 1120 bytes.
|
|
318
308
|
* @throws {DecapError} Thrown if the shared secret cannot be decapsulated.
|
|
309
|
+
* @throws {NotSupportedError} if `senderPublicKey` is provided.
|
|
319
310
|
*/
|
|
320
311
|
async decap(params) {
|
|
312
|
+
if (params.senderPublicKey !== undefined) {
|
|
313
|
+
throw new NotSupportedError("Auth and AuthPSK modes are not supported by this KEM");
|
|
314
|
+
}
|
|
321
315
|
const enc = toArrayBuffer(params.enc);
|
|
322
316
|
const rSk = isCryptoKeyPair(params.recipientKey)
|
|
323
317
|
? params.recipientKey.privateKey
|
|
@@ -336,7 +330,7 @@ export class XWing {
|
|
|
336
330
|
const ctM = ct.subarray(0, 1088);
|
|
337
331
|
const ctX = ct.subarray(1088);
|
|
338
332
|
const ssM = await this._m.decap(ctM, skM);
|
|
339
|
-
const ssX =
|
|
333
|
+
const ssX = x25519.getSharedSecret(skX, ctX);
|
|
340
334
|
return combiner(ssM, ssX, ctX, pkX).buffer;
|
|
341
335
|
}
|
|
342
336
|
catch (e) {
|
|
@@ -372,7 +366,7 @@ export class XWing {
|
|
|
372
366
|
const expanded = shake256.create({ dkLen: 96 }).update(sk).digest();
|
|
373
367
|
const [pkM, skM] = await this._m.deriveKeyPair(expanded.subarray(0, 64));
|
|
374
368
|
const skX = expanded.subarray(64, 96);
|
|
375
|
-
const pkX =
|
|
369
|
+
const pkX = x25519.getPublicKey(skX);
|
|
376
370
|
return [skM, skX, pkM, pkX];
|
|
377
371
|
}
|
|
378
372
|
_serializePublicKey(k) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hpke/hybridkem-x-wing",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "A Hybrid Public Key Encryption (HPKE) module extension for X-Wing: general-purpose hybrid post-quantum KEM.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hpke",
|
|
@@ -33,19 +33,17 @@
|
|
|
33
33
|
},
|
|
34
34
|
"./package.json": "./package.json"
|
|
35
35
|
},
|
|
36
|
+
"funding": {
|
|
37
|
+
"type": "github",
|
|
38
|
+
"url": "https://github.com/sponsors/dajiaji"
|
|
39
|
+
},
|
|
36
40
|
"sideEffects": false,
|
|
37
41
|
"engines": {
|
|
38
42
|
"node": ">=16.0.0"
|
|
39
43
|
},
|
|
40
44
|
"dependencies": {
|
|
41
|
-
"@hpke/common": "^1.
|
|
42
|
-
"
|
|
43
|
-
"mlkem": "^2.5.0"
|
|
44
|
-
},
|
|
45
|
-
"devDependencies": {
|
|
46
|
-
"@types/node": "^20.9.0",
|
|
47
|
-
"@deno/shim-deno": "~0.18.0",
|
|
48
|
-
"picocolors": "^1.0.0"
|
|
45
|
+
"@hpke/common": "^1.13.0",
|
|
46
|
+
"mlkem": "^2.7.0"
|
|
49
47
|
},
|
|
50
48
|
"_generatedBy": "dnt@dev"
|
|
51
|
-
}
|
|
49
|
+
}
|
package/script/src/xWing.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MlKem768 } from "mlkem";
|
|
2
2
|
import type { JsonWebKeyExtended, KemInterface, RecipientContextParams, SenderContextParams } from "@hpke/common";
|
|
3
|
-
import { KemId } from "@hpke/common";
|
|
4
|
-
import { X25519 } from "@hpke/dhkem-x25519";
|
|
3
|
+
import type { KemId } from "@hpke/common";
|
|
5
4
|
/**
|
|
6
5
|
* The Hybrid Post-Quantum KEM (X25519, Kyber768).
|
|
7
6
|
*
|
|
@@ -32,7 +31,6 @@ export declare class XWing implements KemInterface {
|
|
|
32
31
|
readonly privateKeySize: number;
|
|
33
32
|
readonly auth: boolean;
|
|
34
33
|
protected _m: MlKem768;
|
|
35
|
-
protected _x25519: X25519;
|
|
36
34
|
private _api;
|
|
37
35
|
constructor();
|
|
38
36
|
serializePublicKey(key: CryptoKey): Promise<ArrayBuffer>;
|
|
@@ -77,6 +75,7 @@ export declare class XWing implements KemInterface {
|
|
|
77
75
|
* @returns {Promise<{ sharedSecret: ArrayBuffer; enc: ArrayBuffer }>} A promise that resolves with the `ss` (shared secret) as `sharedSecret` and the `ct` (ciphertext) as `enc`.
|
|
78
76
|
* @throws {InvalidParamError} Thrown if the length of the `ekm` is not 64 bytes.
|
|
79
77
|
* @throws {EncapError} Thrown if the shared secret cannot be encapsulated.
|
|
78
|
+
* @throws {NotSupportedError} if `senderKey` is provided.
|
|
80
79
|
*/
|
|
81
80
|
encap(params: SenderContextParams): Promise<{
|
|
82
81
|
sharedSecret: ArrayBuffer;
|
|
@@ -90,6 +89,7 @@ export declare class XWing implements KemInterface {
|
|
|
90
89
|
* @returns {Promise<ArrayBuffer>} A promise that resolves with the shared secret.
|
|
91
90
|
* @throws {InvalidParamError} Thrown if the length of the `enc` is not 1120 bytes.
|
|
92
91
|
* @throws {DecapError} Thrown if the shared secret cannot be decapsulated.
|
|
92
|
+
* @throws {NotSupportedError} if `senderPublicKey` is provided.
|
|
93
93
|
*/
|
|
94
94
|
decap(params: RecipientContextParams): Promise<ArrayBuffer>;
|
|
95
95
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xWing.d.ts","sourceRoot":"","sources":["../../src/src/xWing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAsB,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"xWing.d.ts","sourceRoot":"","sources":["../../src/src/xWing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAsB,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAmBtB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AA4B1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,KAAM,YAAW,YAAY;IACxC,SAAgB,EAAE,EAAE,KAAK,CAAU;IACnC,SAAgB,IAAI,EAAE,MAAM,CAAY;IACxC,SAAgB,UAAU,EAAE,MAAM,CAAM;IACxC,SAAgB,OAAO,EAAE,MAAM,CAAQ;IACvC,SAAgB,aAAa,EAAE,MAAM,CAAQ;IAC7C,SAAgB,cAAc,EAAE,MAAM,CAAM;IAC5C,SAAgB,IAAI,EAAE,OAAO,CAAS;IACtC,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC;IACvB,OAAO,CAAC,IAAI,CAAiC;;IAMhC,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IASxD,oBAAoB,CAC/B,GAAG,EAAE,eAAe,GAAG,eAAe,GACrC,OAAO,CAAC,SAAS,CAAC;IASR,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IASzD,qBAAqB,CAChC,GAAG,EAAE,eAAe,GAAG,eAAe,GACrC,OAAO,CAAC,SAAS,CAAC;IASrB;;;;OAIG;IACU,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC;IAkBtD;;;;;;OAMG;IACU,qBAAqB,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAc1E;;;;;;;OAOG;IACU,aAAa,CACxB,GAAG,EAAE,eAAe,GAAG,eAAe,GACrC,OAAO,CAAC,aAAa,CAAC;IAgBzB;;;;;;;OAOG;IACU,SAAS,CACpB,MAAM,EAAE,KAAK,GAAG,KAAK,EACrB,GAAG,EAAE,WAAW,GAAG,kBAAkB,EACrC,QAAQ,UAAO,GACd,OAAO,CAAC,SAAS,CAAC;IAmCrB;;;;;;;OAOG;IACU,KAAK,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC;QAAE,YAAY,EAAE,WAAW,CAAC;QAAC,GAAG,EAAE,WAAW,CAAA;KAAE,CAAC;IAkD3D;;;;;;;;;OASG;IACU,KAAK,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC;IA+BxE;;;;OAIG;YACW,MAAM;IAOpB;;;;OAIG;YACW,sBAAsB;IAOpC;;;;OAIG;YACW,uBAAuB;IAUrC,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,UAAU;CAmCnB"}
|
package/script/src/xWing.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
if (v !== undefined) module.exports = v;
|
|
5
5
|
}
|
|
6
6
|
else if (typeof define === "function" && define.amd) {
|
|
7
|
-
define(["require", "exports", "mlkem", "@hpke/common"
|
|
7
|
+
define(["require", "exports", "mlkem", "@hpke/common"], factory);
|
|
8
8
|
}
|
|
9
9
|
})(function (require, exports) {
|
|
10
10
|
"use strict";
|
|
@@ -14,14 +14,6 @@
|
|
|
14
14
|
const mlkem_1 = require("mlkem");
|
|
15
15
|
const common_1 = require("@hpke/common");
|
|
16
16
|
const ALG_NAME = "X-Wing";
|
|
17
|
-
const dhkem_x25519_1 = require("@hpke/dhkem-x25519");
|
|
18
|
-
// deno-fmt-ignore
|
|
19
|
-
const X25519_BASE = new Uint8Array([
|
|
20
|
-
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
21
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
22
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
23
|
-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
24
|
-
]);
|
|
25
17
|
// XWingLabel = concat(
|
|
26
18
|
// "\./",
|
|
27
19
|
// "/^\",
|
|
@@ -64,8 +56,8 @@
|
|
|
64
56
|
enumerable: true,
|
|
65
57
|
configurable: true,
|
|
66
58
|
writable: true,
|
|
67
|
-
value:
|
|
68
|
-
});
|
|
59
|
+
value: 0x647a
|
|
60
|
+
}); // KemId.XWing
|
|
69
61
|
Object.defineProperty(this, "name", {
|
|
70
62
|
enumerable: true,
|
|
71
63
|
configurable: true,
|
|
@@ -108,12 +100,6 @@
|
|
|
108
100
|
writable: true,
|
|
109
101
|
value: void 0
|
|
110
102
|
});
|
|
111
|
-
Object.defineProperty(this, "_x25519", {
|
|
112
|
-
enumerable: true,
|
|
113
|
-
configurable: true,
|
|
114
|
-
writable: true,
|
|
115
|
-
value: void 0
|
|
116
|
-
});
|
|
117
103
|
Object.defineProperty(this, "_api", {
|
|
118
104
|
enumerable: true,
|
|
119
105
|
configurable: true,
|
|
@@ -121,7 +107,6 @@
|
|
|
121
107
|
value: undefined
|
|
122
108
|
});
|
|
123
109
|
this._m = new mlkem_1.MlKem768();
|
|
124
|
-
this._x25519 = new dhkem_x25519_1.X25519(new dhkem_x25519_1.HkdfSha256());
|
|
125
110
|
}
|
|
126
111
|
async serializePublicKey(key) {
|
|
127
112
|
await this._setup();
|
|
@@ -275,8 +260,12 @@
|
|
|
275
260
|
* @returns {Promise<{ sharedSecret: ArrayBuffer; enc: ArrayBuffer }>} A promise that resolves with the `ss` (shared secret) as `sharedSecret` and the `ct` (ciphertext) as `enc`.
|
|
276
261
|
* @throws {InvalidParamError} Thrown if the length of the `ekm` is not 64 bytes.
|
|
277
262
|
* @throws {EncapError} Thrown if the shared secret cannot be encapsulated.
|
|
263
|
+
* @throws {NotSupportedError} if `senderKey` is provided.
|
|
278
264
|
*/
|
|
279
265
|
async encap(params) {
|
|
266
|
+
if (params.senderKey !== undefined) {
|
|
267
|
+
throw new common_1.NotSupportedError("Auth and AuthPSK modes are not supported by this KEM");
|
|
268
|
+
}
|
|
280
269
|
let ekm = undefined;
|
|
281
270
|
if (params.ekm !== undefined && !(0, common_1.isCryptoKeyPair)(params.ekm)) {
|
|
282
271
|
ekm = (0, common_1.toArrayBuffer)(params.ekm);
|
|
@@ -293,13 +282,14 @@
|
|
|
293
282
|
ekX = ek.subarray(32, 64);
|
|
294
283
|
}
|
|
295
284
|
else {
|
|
296
|
-
|
|
285
|
+
const randomEkX = new Uint8Array(32);
|
|
297
286
|
try {
|
|
298
|
-
this._api.getRandomValues(
|
|
287
|
+
this._api.getRandomValues(randomEkX);
|
|
299
288
|
}
|
|
300
289
|
catch (e) {
|
|
301
290
|
throw new common_1.NotSupportedError(e);
|
|
302
291
|
}
|
|
292
|
+
ekX = randomEkX;
|
|
303
293
|
}
|
|
304
294
|
const pk = new Uint8Array(await this.serializePublicKey(params.recipientPublicKey));
|
|
305
295
|
if (pk.byteLength !== 1216) {
|
|
@@ -308,8 +298,8 @@
|
|
|
308
298
|
try {
|
|
309
299
|
const pkM = pk.subarray(0, 1184);
|
|
310
300
|
const pkX = pk.subarray(1184, 1216);
|
|
311
|
-
const ctX =
|
|
312
|
-
const ssX =
|
|
301
|
+
const ctX = common_1.x25519.getPublicKey(ekX);
|
|
302
|
+
const ssX = common_1.x25519.getSharedSecret(ekX, pkX);
|
|
313
303
|
const [ctM, ssM] = await this._m.encap(pkM, ekM);
|
|
314
304
|
return {
|
|
315
305
|
sharedSecret: combiner(ssM, ssX, ctX, pkX).buffer,
|
|
@@ -328,8 +318,12 @@
|
|
|
328
318
|
* @returns {Promise<ArrayBuffer>} A promise that resolves with the shared secret.
|
|
329
319
|
* @throws {InvalidParamError} Thrown if the length of the `enc` is not 1120 bytes.
|
|
330
320
|
* @throws {DecapError} Thrown if the shared secret cannot be decapsulated.
|
|
321
|
+
* @throws {NotSupportedError} if `senderPublicKey` is provided.
|
|
331
322
|
*/
|
|
332
323
|
async decap(params) {
|
|
324
|
+
if (params.senderPublicKey !== undefined) {
|
|
325
|
+
throw new common_1.NotSupportedError("Auth and AuthPSK modes are not supported by this KEM");
|
|
326
|
+
}
|
|
333
327
|
const enc = (0, common_1.toArrayBuffer)(params.enc);
|
|
334
328
|
const rSk = (0, common_1.isCryptoKeyPair)(params.recipientKey)
|
|
335
329
|
? params.recipientKey.privateKey
|
|
@@ -348,7 +342,7 @@
|
|
|
348
342
|
const ctM = ct.subarray(0, 1088);
|
|
349
343
|
const ctX = ct.subarray(1088);
|
|
350
344
|
const ssM = await this._m.decap(ctM, skM);
|
|
351
|
-
const ssX =
|
|
345
|
+
const ssX = common_1.x25519.getSharedSecret(skX, ctX);
|
|
352
346
|
return combiner(ssM, ssX, ctX, pkX).buffer;
|
|
353
347
|
}
|
|
354
348
|
catch (e) {
|
|
@@ -384,7 +378,7 @@
|
|
|
384
378
|
const expanded = mlkem_1.shake256.create({ dkLen: 96 }).update(sk).digest();
|
|
385
379
|
const [pkM, skM] = await this._m.deriveKeyPair(expanded.subarray(0, 64));
|
|
386
380
|
const skX = expanded.subarray(64, 96);
|
|
387
|
-
const pkX =
|
|
381
|
+
const pkX = common_1.x25519.getPublicKey(skX);
|
|
388
382
|
return [skM, skX, pkM, pkX];
|
|
389
383
|
}
|
|
390
384
|
_serializePublicKey(k) {
|