routex-settlement 0.1.2 → 0.1.4
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/index.d.ts +2 -0
- package/index.js +208 -28
- package/package.json +7 -5
package/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare class KeySettlement {
|
|
|
6
6
|
private _requirements;
|
|
7
7
|
private _yaxiSigningKeys;
|
|
8
8
|
private _serverKey?;
|
|
9
|
+
private _settlementPromise?;
|
|
9
10
|
private _measurement?;
|
|
10
11
|
constructor(url: URL, onErrorResponse: (response: Response) => void, onRequestError?: (e: Error) => never);
|
|
11
12
|
private _settle;
|
|
@@ -15,6 +16,7 @@ export declare class KeySettlement {
|
|
|
15
16
|
seal(plaintext: Uint8Array, settlementHeaders?: HeadersInit): Promise<Uint8Array>;
|
|
16
17
|
unseal(ciphertext: Uint8Array): Uint8Array;
|
|
17
18
|
private _verifyAttestation;
|
|
19
|
+
private _verifyTcbVersion;
|
|
18
20
|
private _verifyReport;
|
|
19
21
|
private _verifyVcekChain;
|
|
20
22
|
}
|
package/index.js
CHANGED
|
@@ -7,26 +7,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { chacha20poly1305 } from "@noble/ciphers/chacha";
|
|
11
|
-
import { bytesToUtf8 } from "@noble/ciphers/utils";
|
|
12
|
-
import {
|
|
13
|
-
import { ed25519, x25519 } from "@noble/curves/ed25519";
|
|
14
|
-
import { blake2b } from "@noble/hashes/
|
|
15
|
-
import { p384 } from "@noble/curves/
|
|
16
|
-
import { hkdf } from "@noble/hashes/hkdf";
|
|
17
|
-
import {
|
|
18
|
-
import { sha256 } from "@noble/hashes/sha2";
|
|
10
|
+
import { chacha20poly1305 } from "@noble/ciphers/chacha.js";
|
|
11
|
+
import { bytesToUtf8 } from "@noble/ciphers/utils.js";
|
|
12
|
+
import { equalBytes } from "@noble/curves/utils.js";
|
|
13
|
+
import { ed25519, x25519 } from "@noble/curves/ed25519.js";
|
|
14
|
+
import { blake2b } from "@noble/hashes/blake2.js";
|
|
15
|
+
import { p384 } from "@noble/curves/nist.js";
|
|
16
|
+
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
17
|
+
import { createHasher } from "@noble/hashes/utils.js";
|
|
18
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
19
19
|
import * as x509 from "@peculiar/x509";
|
|
20
20
|
import { AsnConvert } from "@peculiar/asn1-schema";
|
|
21
21
|
import { SubjectPublicKeyInfo } from "@peculiar/asn1-x509";
|
|
22
22
|
const REQUIREMENTS = {
|
|
23
23
|
Genoa: {
|
|
24
24
|
minCommittedVersion: [0x1, 0x37, 0x26],
|
|
25
|
-
|
|
25
|
+
minCommittedTcbSnp: 0x16,
|
|
26
26
|
},
|
|
27
27
|
Milan: {
|
|
28
28
|
minCommittedVersion: [0x1, 0x37, 0x16],
|
|
29
|
-
|
|
29
|
+
minCommittedTcbSnp: 0x17,
|
|
30
|
+
},
|
|
31
|
+
// for Turin: SB-3019 doesn't list a TCB[SNP] version in the mitigation, only firmware version
|
|
32
|
+
Turin: {
|
|
33
|
+
minCommittedVersion: [0x1, 0x37, 0x3b],
|
|
34
|
+
minCommittedTcbSnp: 0,
|
|
30
35
|
},
|
|
31
36
|
};
|
|
32
37
|
const YAXI_SIGNING_KEYS = {
|
|
@@ -200,6 +205,86 @@ JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
|
|
|
200
205
|
CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
|
|
201
206
|
AFZEAwoKCQ==
|
|
202
207
|
-----END CERTIFICATE-----
|
|
208
|
+
`),
|
|
209
|
+
// ARK-Turin
|
|
210
|
+
new x509.X509Certificate(`
|
|
211
|
+
-----BEGIN CERTIFICATE-----
|
|
212
|
+
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
|
|
247
|
+
-----END CERTIFICATE-----
|
|
248
|
+
`),
|
|
249
|
+
// SEV-Turin
|
|
250
|
+
new x509.X509Certificate(`
|
|
251
|
+
-----BEGIN CERTIFICATE-----
|
|
252
|
+
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
|
|
286
|
+
zpacMwFusA==
|
|
287
|
+
-----END CERTIFICATE-----
|
|
203
288
|
`),
|
|
204
289
|
],
|
|
205
290
|
});
|
|
@@ -213,7 +298,7 @@ export class KeySettlement {
|
|
|
213
298
|
onRequestError !== null && onRequestError !== void 0 ? onRequestError : ((e) => {
|
|
214
299
|
throw e;
|
|
215
300
|
});
|
|
216
|
-
this._secretKey = x25519.utils.
|
|
301
|
+
this._secretKey = x25519.utils.randomSecretKey();
|
|
217
302
|
}
|
|
218
303
|
_settle(headers) {
|
|
219
304
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -223,7 +308,11 @@ export class KeySettlement {
|
|
|
223
308
|
body: JSON.stringify({
|
|
224
309
|
publicKey: btoa(bytesToBinaryString(x25519.getPublicKey(this._secretKey))),
|
|
225
310
|
}),
|
|
226
|
-
})
|
|
311
|
+
})
|
|
312
|
+
.catch(this._onRequestError)
|
|
313
|
+
.finally(() => {
|
|
314
|
+
this._settlementPromise = undefined;
|
|
315
|
+
});
|
|
227
316
|
if (response.status >= 400) {
|
|
228
317
|
throw yield this._onErrorResponse(response);
|
|
229
318
|
}
|
|
@@ -243,7 +332,10 @@ export class KeySettlement {
|
|
|
243
332
|
_getServerKey(settlementHeaders) {
|
|
244
333
|
return __awaiter(this, void 0, void 0, function* () {
|
|
245
334
|
if (this._serverKey == null) {
|
|
246
|
-
|
|
335
|
+
if (this._settlementPromise == null) {
|
|
336
|
+
this._settlementPromise = this._settle(settlementHeaders);
|
|
337
|
+
}
|
|
338
|
+
yield this._settlementPromise;
|
|
247
339
|
}
|
|
248
340
|
return this._serverKey;
|
|
249
341
|
});
|
|
@@ -258,7 +350,7 @@ export class KeySettlement {
|
|
|
258
350
|
const serverPublicKey = (yield this._getServerKey(settlementHeaders))
|
|
259
351
|
.publicKey;
|
|
260
352
|
const tagLength = 16;
|
|
261
|
-
const ephemeralSecretKey = x25519.utils.
|
|
353
|
+
const ephemeralSecretKey = x25519.utils.randomSecretKey();
|
|
262
354
|
const ephemeralPublicKey = x25519.getPublicKey(ephemeralSecretKey);
|
|
263
355
|
const nonce = _getSealNonce(ephemeralPublicKey, serverPublicKey);
|
|
264
356
|
const info = _getInfo(ephemeralPublicKey, serverPublicKey);
|
|
@@ -308,17 +400,82 @@ export class KeySettlement {
|
|
|
308
400
|
return launchMeasurementInSystemVersion;
|
|
309
401
|
});
|
|
310
402
|
}
|
|
403
|
+
_verifyTcbVersion(attestationReport, vcekTcb) {
|
|
404
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
405
|
+
const versionBytes = attestationReport.slice(0, 4);
|
|
406
|
+
const version = new DataView(versionBytes.buffer).getInt32(0, true);
|
|
407
|
+
let turinLike;
|
|
408
|
+
switch (version) {
|
|
409
|
+
case 0:
|
|
410
|
+
case 1:
|
|
411
|
+
throw new Error("Unsupported Attestation Report Version");
|
|
412
|
+
case 2: {
|
|
413
|
+
const chipId = attestationReport.slice(REPORT_OFFSETS.chipId, 64);
|
|
414
|
+
if (equalBytes(chipId, new Uint8Array(64))) {
|
|
415
|
+
throw new Error("Could not derive CPU family: MASK_CHIP_ID enabled");
|
|
416
|
+
}
|
|
417
|
+
turinLike = equalBytes(chipId.slice(8), new Uint8Array(56));
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
default: // from version 3 onwards CPUID_FAM_ID field should exist
|
|
421
|
+
turinLike = attestationReport[REPORT_OFFSETS.cpuIdFamily] == 0x1a;
|
|
422
|
+
}
|
|
423
|
+
const reportedTcb = attestationReport.slice(REPORT_OFFSETS.reportedTcb, REPORT_OFFSETS.reportedTcb + 8);
|
|
424
|
+
let tcb;
|
|
425
|
+
if (turinLike) {
|
|
426
|
+
tcb = {
|
|
427
|
+
microcode: reportedTcb[7],
|
|
428
|
+
snp: reportedTcb[3],
|
|
429
|
+
tee: reportedTcb[2],
|
|
430
|
+
bootloader: reportedTcb[1],
|
|
431
|
+
fmc: reportedTcb[0],
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
tcb = {
|
|
436
|
+
microcode: reportedTcb[7],
|
|
437
|
+
snp: reportedTcb[6],
|
|
438
|
+
tee: reportedTcb[1],
|
|
439
|
+
bootloader: reportedTcb[0],
|
|
440
|
+
fmc: 0,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
if (!(tcb.microcode === vcekTcb.microcode &&
|
|
444
|
+
tcb.snp === vcekTcb.snp &&
|
|
445
|
+
tcb.tee === vcekTcb.tee &&
|
|
446
|
+
tcb.bootloader === vcekTcb.bootloader &&
|
|
447
|
+
tcb.fmc === vcekTcb.fmc)) {
|
|
448
|
+
throw new Error("Verification failed: REPORTED_TCB doesn't match with VCEK's TCB_VERSION");
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
}
|
|
311
452
|
_verifyReport(attestationReport, vcekChain) {
|
|
312
453
|
return __awaiter(this, void 0, void 0, function* () {
|
|
313
454
|
if (attestationReport.length != 1184) {
|
|
314
455
|
throw new Error(`Attestation report has unexpected length: ${attestationReport.length}`);
|
|
315
456
|
}
|
|
316
|
-
const { requirements, publicKey } = yield this._verifyVcekChain(vcekChain);
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
457
|
+
const { requirements, publicKey, vcekTcb } = yield this._verifyVcekChain(vcekChain);
|
|
458
|
+
if (!equalBytes(attestationReport.slice(REPORT_OFFSETS.signatureR + 48, REPORT_OFFSETS.signatureR + 72), new Uint8Array(24)) ||
|
|
459
|
+
!equalBytes(attestationReport.slice(REPORT_OFFSETS.signatureS + 48, REPORT_OFFSETS.signatureS + 72), new Uint8Array(24))) {
|
|
460
|
+
throw new Error("Unexpected signature bits");
|
|
461
|
+
}
|
|
462
|
+
const sig = new Uint8Array(96);
|
|
463
|
+
sig.set(attestationReport
|
|
464
|
+
.slice(REPORT_OFFSETS.signatureR, REPORT_OFFSETS.signatureR + 48)
|
|
465
|
+
// Change endianness
|
|
466
|
+
.reverse(), 0);
|
|
467
|
+
sig.set(attestationReport
|
|
468
|
+
.slice(REPORT_OFFSETS.signatureS, REPORT_OFFSETS.signatureS + 48)
|
|
469
|
+
// Change endianness
|
|
470
|
+
.reverse(), 48);
|
|
471
|
+
let valid;
|
|
472
|
+
try {
|
|
473
|
+
valid = p384.verify(sig, attestationReport.slice(0, 672), new Uint8Array(publicKey), { lowS: false });
|
|
474
|
+
}
|
|
475
|
+
catch (_a) {
|
|
476
|
+
valid = false;
|
|
477
|
+
}
|
|
478
|
+
if (!valid) {
|
|
322
479
|
throw new Error("Verification failed: Invalid attestation report signature");
|
|
323
480
|
}
|
|
324
481
|
if ((attestationReport[REPORT_OFFSETS.platInfo] &
|
|
@@ -335,9 +492,10 @@ export class KeySettlement {
|
|
|
335
492
|
throw new Error("Verification failed: Firmware version too small");
|
|
336
493
|
}
|
|
337
494
|
if (attestationReport[REPORT_OFFSETS.committedTcbSnp] <
|
|
338
|
-
requirements.
|
|
495
|
+
requirements.minCommittedTcbSnp) {
|
|
339
496
|
throw new Error("Verification failed: SNP patch level too small");
|
|
340
497
|
}
|
|
498
|
+
yield this._verifyTcbVersion(attestationReport, vcekTcb);
|
|
341
499
|
});
|
|
342
500
|
}
|
|
343
501
|
_verifyVcekChain(vcek) {
|
|
@@ -353,11 +511,14 @@ export class KeySettlement {
|
|
|
353
511
|
if (chain.length != 3) {
|
|
354
512
|
throw new Error(`Certificate chain verification failed\n\nChain: ${chain}`);
|
|
355
513
|
}
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
514
|
+
const find_extension = (oid, type) => {
|
|
515
|
+
const ext = chain[0].extensions.find((ext) => ext.type == oid);
|
|
516
|
+
if (!ext) {
|
|
517
|
+
throw new Error(`Could not find ${type}\n\nExtensions: ${chain[0].extensions}`);
|
|
518
|
+
}
|
|
519
|
+
return new Uint8Array(ext.value);
|
|
520
|
+
};
|
|
521
|
+
const productIA5String = find_extension("1.3.6.1.4.1.3704.1.2", "product name");
|
|
361
522
|
if (productIA5String[0] !== 0x16) {
|
|
362
523
|
throw new Error(`Unexpected product extension: ${productIA5String}`);
|
|
363
524
|
}
|
|
@@ -366,9 +527,25 @@ export class KeySettlement {
|
|
|
366
527
|
throw new Error(`Unexpected product: ${product}`);
|
|
367
528
|
}
|
|
368
529
|
const subjectPublicKeyInfo = AsnConvert.parse(chain[0].publicKey.rawData, SubjectPublicKeyInfo);
|
|
530
|
+
const bootloader = find_extension("1.3.6.1.4.1.3704.1.3.1", "bootloader version")[2];
|
|
531
|
+
const tee = find_extension("1.3.6.1.4.1.3704.1.3.2", "TEE")[2];
|
|
532
|
+
const snp = find_extension("1.3.6.1.4.1.3704.1.3.3", "SNP version")[2];
|
|
533
|
+
const microcode = find_extension("1.3.6.1.4.1.3704.1.3.8", "microcode")[2];
|
|
534
|
+
const tcb = {
|
|
535
|
+
bootloader: bootloader,
|
|
536
|
+
tee: tee,
|
|
537
|
+
snp: snp,
|
|
538
|
+
microcode: microcode,
|
|
539
|
+
fmc: 0,
|
|
540
|
+
};
|
|
541
|
+
const fmc = chain[0].extensions.find((ext) => ext.type == "1.3.6.1.4.1.3704.1.3.9");
|
|
542
|
+
if (fmc) {
|
|
543
|
+
tcb.fmc = new Uint8Array(fmc.value)[2];
|
|
544
|
+
}
|
|
369
545
|
return {
|
|
370
546
|
requirements: this._requirements[product],
|
|
371
547
|
publicKey: new Uint8Array(subjectPublicKeyInfo.subjectPublicKey),
|
|
548
|
+
vcekTcb: tcb,
|
|
372
549
|
};
|
|
373
550
|
});
|
|
374
551
|
}
|
|
@@ -381,7 +558,7 @@ export function bytesToBinaryString(bytes) {
|
|
|
381
558
|
}
|
|
382
559
|
function _createCipher(publicKey, secretKey, info, nonce) {
|
|
383
560
|
const sharedSecret = x25519.getSharedSecret(secretKey, publicKey);
|
|
384
|
-
const key = hkdf(
|
|
561
|
+
const key = hkdf(createHasher(() => blake2b.create({ dkLen: 64 })), sharedSecret, Uint8Array.from([]), info, 32);
|
|
385
562
|
return chacha20poly1305(key, nonce);
|
|
386
563
|
}
|
|
387
564
|
function _getInfo(ephemeralPublicKey, recipientPublicKey) {
|
|
@@ -402,6 +579,9 @@ const REPORT_OFFSETS = {
|
|
|
402
579
|
platInfo: 64,
|
|
403
580
|
reportData: 80,
|
|
404
581
|
measurement: 144,
|
|
582
|
+
reportedTcb: 384,
|
|
583
|
+
cpuIdFamily: 392,
|
|
584
|
+
chipId: 416,
|
|
405
585
|
committedTcbSnp: 486,
|
|
406
586
|
committedBuild: 492,
|
|
407
587
|
committedMinor: 493,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "routex-settlement",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Key settlement for the YAXI routex client",
|
|
5
5
|
"homepage": "https://yaxi.tech",
|
|
6
6
|
"author": "YAXI GmbH",
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc --build",
|
|
18
18
|
"clean": "tsc --build --clean",
|
|
19
|
+
"fmt": "prettier --write .",
|
|
19
20
|
"lint": "eslint",
|
|
20
21
|
"test": "tsc --build && NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@noble/ciphers": "^
|
|
24
|
-
"@noble/curves": "^
|
|
25
|
-
"@noble/hashes": "^
|
|
24
|
+
"@noble/ciphers": "^2.0.0",
|
|
25
|
+
"@noble/curves": "^2.0.0",
|
|
26
|
+
"@noble/hashes": "^2.0.0",
|
|
26
27
|
"@peculiar/asn1-schema": "^2.3.15",
|
|
27
28
|
"@peculiar/asn1-x509": "^2.3.15",
|
|
28
29
|
"@peculiar/x509": "^1.12.3"
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
"@eslint/js": "^9.21.0",
|
|
32
33
|
"eslint": "^9.21.0",
|
|
33
34
|
"globals": "^16.0.0",
|
|
34
|
-
"jest": "^
|
|
35
|
+
"jest": "^30.1.3",
|
|
36
|
+
"prettier": "^3.6.2",
|
|
35
37
|
"typescript": "^5.7.3",
|
|
36
38
|
"typescript-eslint": "^8.24.1"
|
|
37
39
|
}
|