routex-settlement 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.
Files changed (3) hide show
  1. package/index.d.ts +21 -0
  2. package/index.js +402 -0
  3. package/package.json +38 -0
package/index.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ export declare class KeySettlement {
2
+ private _url;
3
+ private _onErrorResponse;
4
+ private _secretKey;
5
+ private _requirements;
6
+ private _yaxiSigningKeys;
7
+ private _serverKey?;
8
+ private _measurement?;
9
+ constructor(url: URL, onErrorResponse: (response: Response) => void);
10
+ private _settle;
11
+ measurement(): Uint8Array | undefined;
12
+ private _getServerKey;
13
+ getBase64SessionId(settlementHeaders?: HeadersInit): Promise<string>;
14
+ seal(plaintext: Uint8Array, settlementHeaders?: HeadersInit): Promise<Uint8Array>;
15
+ unseal(ciphertext: Uint8Array): Uint8Array;
16
+ private _verifyAttestation;
17
+ private _verifyReport;
18
+ private _verifyVcekChain;
19
+ }
20
+ export declare function binaryStringToBytes(binaryString: string): Uint8Array<ArrayBuffer>;
21
+ export declare function bytesToBinaryString(bytes: Uint8Array): string;
package/index.js ADDED
@@ -0,0 +1,402 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { chacha20poly1305 } from "@noble/ciphers/chacha";
11
+ import { bytesToUtf8 } from "@noble/ciphers/utils";
12
+ import { bytesToNumberLE, equalBytes } from "@noble/curves/abstract/utils";
13
+ import { ed25519, x25519 } from "@noble/curves/ed25519";
14
+ import { blake2b } from "@noble/hashes/blake2b";
15
+ import { p384 } from "@noble/curves/p384";
16
+ import { hkdf } from "@noble/hashes/hkdf";
17
+ import { wrapConstructor } from "@noble/hashes/utils";
18
+ import { sha256 } from "@noble/hashes/sha2";
19
+ import * as x509 from "@peculiar/x509";
20
+ import { AsnConvert } from "@peculiar/asn1-schema";
21
+ import { SubjectPublicKeyInfo } from "@peculiar/asn1-x509";
22
+ const REQUIREMENTS = {
23
+ Genoa: {
24
+ minCommittedVersion: [0x1, 0x37, 0x26],
25
+ minCommitedTcbSnp: 0x16,
26
+ },
27
+ Milan: {
28
+ minCommittedVersion: [0x1, 0x37, 0x16],
29
+ minCommitedTcbSnp: 0x17,
30
+ },
31
+ };
32
+ const YAXI_SIGNING_KEYS = {
33
+ "AhrUXsV/XAvIE24RQ/Vt/zXoLodvjXoWD2fhLGuRM7U=": new Uint8Array([
34
+ 101, 188, 76, 33, 245, 155, 42, 79, 214, 181, 125, 49, 68, 133, 87, 232,
35
+ 123, 91, 209, 21, 239, 36, 195, 215, 82, 140, 160, 195, 236, 111, 180, 226,
36
+ ]),
37
+ "D30zRYe8Ug9732b4Pe2BAwWAXn/T5Nss2HJOp3kLC1w=": new Uint8Array([
38
+ 8, 203, 107, 97, 17, 140, 16, 240, 29, 197, 104, 26, 179, 115, 86, 2, 210,
39
+ 73, 107, 211, 46, 34, 89, 174, 117, 193, 126, 215, 12, 133, 106, 37,
40
+ ]),
41
+ };
42
+ const ROOT_STORE = new x509.X509ChainBuilder({
43
+ certificates: [
44
+ // ARK-Genoa
45
+ new x509.X509Certificate(`
46
+ -----BEGIN CERTIFICATE-----
47
+ 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
82
+ -----END CERTIFICATE-----
83
+ `),
84
+ // SEV-Genoa
85
+ new x509.X509Certificate(`
86
+ -----BEGIN CERTIFICATE-----
87
+ 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
121
+ /wMz1R1BHg==
122
+ -----END CERTIFICATE-----
123
+ `),
124
+ // ARK-Milan
125
+ new x509.X509Certificate(`
126
+ -----BEGIN CERTIFICATE-----
127
+ MIIGiTCCBDigAwIBAgIDAQABMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
128
+ BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
129
+ BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
130
+ Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
131
+ Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTgyNDIwWhcNNDUxMDIy
132
+ MTgyNDIwWjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
133
+ BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
134
+ ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJU0VWLU1pbGFuMIICIjANBgkqhkiG
135
+ 9w0BAQEFAAOCAg8AMIICCgKCAgEAnU2drrNTfbhNQIllf+W2y+ROCbSzId1aKZft
136
+ 2T9zjZQOzjGccl17i1mIKWl7NTcB0VYXt3JxZSzOZjsjLNVAEN2MGj9TiedL+Qew
137
+ KZX0JmQEuYjm+WKksLtxgdLp9E7EZNwNDqV1r0qRP5tB8OWkyQbIdLeu4aCz7j/S
138
+ l1FkBytev9sbFGzt7cwnjzi9m7noqsk+uRVBp3+In35QPdcj8YflEmnHBNvuUDJh
139
+ LCJMW8KOjP6++Phbs3iCitJcANEtW4qTNFoKW3CHlbcSCjTM8KsNbUx3A8ek5EVL
140
+ jZWH1pt9E3TfpR6XyfQKnY6kl5aEIPwdW3eFYaqCFPrIo9pQT6WuDSP4JCYJbZne
141
+ KKIbZjzXkJt3NQG32EukYImBb9SCkm9+fS5LZFg9ojzubMX3+NkBoSXI7OPvnHMx
142
+ jup9mw5se6QUV7GqpCA2TNypolmuQ+cAaxV7JqHE8dl9pWf+Y3arb+9iiFCwFt4l
143
+ AlJw5D0CTRTC1Y5YWFDBCrA/vGnmTnqG8C+jjUAS7cjjR8q4OPhyDmJRPnaC/ZG5
144
+ uP0K0z6GoO/3uen9wqshCuHegLTpOeHEJRKrQFr4PVIwVOB0+ebO5FgoyOw43nyF
145
+ D5UKBDxEB4BKo/0uAiKHLRvvgLbORbU8KARIs1EoqEjmF8UtrmQWV2hUjwzqwvHF
146
+ ei8rPxMCAwEAAaOBozCBoDAdBgNVHQ4EFgQUO8ZuGCrD/T1iZEib47dHLLT8v/gw
147
+ HwYDVR0jBBgwFoAUhawa0UP3yKxV1MUdQUir1XhK1FMwEgYDVR0TAQH/BAgwBgEB
148
+ /wIBADAOBgNVHQ8BAf8EBAMCAQQwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cHM6Ly9r
149
+ ZHNpbnRmLmFtZC5jb20vdmNlay92MS9NaWxhbi9jcmwwRgYJKoZIhvcNAQEKMDmg
150
+ DzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKID
151
+ AgEwowMCAQEDggIBAIgeUQScAf3lDYqgWU1VtlDbmIN8S2dC5kmQzsZ/HtAjQnLE
152
+ PI1jh3gJbLxL6gf3K8jxctzOWnkYcbdfMOOr28KT35IaAR20rekKRFptTHhe+DFr
153
+ 3AFzZLDD7cWK29/GpPitPJDKCvI7A4Ug06rk7J0zBe1fz/qe4i2/F12rvfwCGYhc
154
+ RxPy7QF3q8fR6GCJdB1UQ5SlwCjFxD4uezURztIlIAjMkt7DFvKRh+2zK+5plVGG
155
+ FsjDJtMz2ud9y0pvOE4j3dH5IW9jGxaSGStqNrabnnpF236ETr1/a43b8FFKL5QN
156
+ mt8Vr9xnXRpznqCRvqjr+kVrb6dlfuTlliXeQTMlBoRWFJORL8AcBJxGZ4K2mXft
157
+ l1jU5TLeh5KXL9NW7a/qAOIUs2FiOhqrtzAhJRg9Ij8QkQ9Pk+cKGzw6El3T3kFr
158
+ Eg6zkxmvMuabZOsdKfRkWfhH2ZKcTlDfmH1H0zq0Q2bG3uvaVdiCtFY1LlWyB38J
159
+ S2fNsR/Py6t5brEJCFNvzaDky6KeC4ion/cVgUai7zzS3bGQWzKDKU35SqNU2WkP
160
+ I8xCZ00WtIiKKFnXWUQxvlKmmgZBIYPe01zD0N8atFxmWiSnfJl690B9rJpNR/fI
161
+ ajxCW3Seiws6r1Zm+tCuVbMiNtpS9ThjNX4uve5thyfE2DgoxRFvY1CsoF5M
162
+ -----END CERTIFICATE-----
163
+ `),
164
+ // SEV-Milan
165
+ new x509.X509Certificate(`
166
+ -----BEGIN CERTIFICATE-----
167
+ MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
168
+ BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
169
+ BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
170
+ Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
171
+ Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy
172
+ MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
173
+ BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
174
+ ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG
175
+ 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg
176
+ W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta
177
+ 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2
178
+ SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0
179
+ 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05
180
+ gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg
181
+ bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs
182
+ +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi
183
+ Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ
184
+ eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18
185
+ fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j
186
+ WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI
187
+ rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG
188
+ KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG
189
+ SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI
190
+ AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel
191
+ ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw
192
+ STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK
193
+ dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq
194
+ zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp
195
+ KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e
196
+ pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq
197
+ HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh
198
+ 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn
199
+ JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
200
+ CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
201
+ AFZEAwoKCQ==
202
+ -----END CERTIFICATE-----
203
+ `),
204
+ ],
205
+ });
206
+ export class KeySettlement {
207
+ constructor(url, onErrorResponse) {
208
+ this._requirements = REQUIREMENTS;
209
+ this._yaxiSigningKeys = YAXI_SIGNING_KEYS;
210
+ this._url = url;
211
+ this._onErrorResponse = onErrorResponse;
212
+ this._secretKey = x25519.utils.randomPrivateKey();
213
+ }
214
+ _settle(headers) {
215
+ return __awaiter(this, void 0, void 0, function* () {
216
+ const response = yield fetch(this._url, {
217
+ method: "POST",
218
+ headers: headers,
219
+ body: JSON.stringify({
220
+ publicKey: btoa(bytesToBinaryString(x25519.getPublicKey(this._secretKey))),
221
+ }),
222
+ });
223
+ if (response.status >= 400) {
224
+ throw yield this._onErrorResponse(response);
225
+ }
226
+ const responseData = yield response.json();
227
+ const measurement = yield this._verifyAttestation(responseData);
228
+ const settlementBoxMessage = JSON.parse(bytesToUtf8(this.unseal(binaryStringToBytes(atob(responseData.chachaBox)))));
229
+ this._serverKey = [
230
+ binaryStringToBytes(atob(settlementBoxMessage.publicKey)),
231
+ settlementBoxMessage.sessionId,
232
+ ];
233
+ this._measurement = measurement;
234
+ });
235
+ }
236
+ measurement() {
237
+ return this._measurement;
238
+ }
239
+ _getServerKey(settlementHeaders) {
240
+ return __awaiter(this, void 0, void 0, function* () {
241
+ if (this._serverKey == null) {
242
+ yield this._settle(settlementHeaders);
243
+ }
244
+ return this._serverKey;
245
+ });
246
+ }
247
+ getBase64SessionId() {
248
+ return __awaiter(this, arguments, void 0, function* (settlementHeaders = {}) {
249
+ return (yield this._getServerKey(settlementHeaders))[1];
250
+ });
251
+ }
252
+ seal(plaintext_1) {
253
+ return __awaiter(this, arguments, void 0, function* (plaintext, settlementHeaders = {}) {
254
+ const serverPublicKey = (yield this._getServerKey(settlementHeaders))[0];
255
+ const tagLength = 16;
256
+ const ephemeralSecretKey = x25519.utils.randomPrivateKey();
257
+ const ephemeralPublicKey = x25519.getPublicKey(ephemeralSecretKey);
258
+ const nonce = _getSealNonce(ephemeralPublicKey, serverPublicKey);
259
+ const info = _getInfo(ephemeralPublicKey, serverPublicKey);
260
+ const cipher = _createCipher(serverPublicKey, ephemeralSecretKey, info, nonce);
261
+ const ciphertext = new Uint8Array(ephemeralPublicKey.length + tagLength + plaintext.length);
262
+ ciphertext.set(ephemeralPublicKey);
263
+ ciphertext.set(cipher.encrypt(plaintext), ephemeralPublicKey.length);
264
+ return ciphertext;
265
+ });
266
+ }
267
+ unseal(ciphertext) {
268
+ const ephemeralPublicKey = ciphertext.subarray(0, 32);
269
+ const publicKey = x25519.getPublicKey(this._secretKey);
270
+ const nonce = _getSealNonce(ephemeralPublicKey, publicKey);
271
+ const info = _getInfo(ephemeralPublicKey, publicKey);
272
+ const cipher = _createCipher(ephemeralPublicKey, this._secretKey, info, nonce);
273
+ return cipher.decrypt(ciphertext.subarray(32));
274
+ }
275
+ _verifyAttestation(response) {
276
+ return __awaiter(this, void 0, void 0, function* () {
277
+ const attestationReport = binaryStringToBytes(atob(response.attestationReport));
278
+ yield this._verifyReport(attestationReport, response.vcek);
279
+ if (!equalBytes(attestationReport.slice(REPORT_OFFSETS.reportData, REPORT_OFFSETS.reportData + 32), sha256(binaryStringToBytes(atob(response.chachaBox))))) {
280
+ throw new Error("Data in attestation report doesn't match chacha box");
281
+ }
282
+ const encoder = new TextEncoder();
283
+ const launchMeasurement = binaryStringToBytes(atob(response.systemVersion.launchMeasurement));
284
+ const input = new Uint8Array([
285
+ ...encoder.encode(response.systemVersion.kind),
286
+ ...encoder.encode(response.systemVersion.generation.toString()),
287
+ ...encoder.encode(response.systemVersion.createdAt.replace("Z", "+00:00")),
288
+ ...encoder.encode(response.systemVersion.ref),
289
+ ...launchMeasurement,
290
+ ]);
291
+ const key = this._yaxiSigningKeys[response.systemVersion.signature.keyId];
292
+ if (key == null) {
293
+ throw new Error("The system version's signature key is unknown");
294
+ }
295
+ if (!ed25519.verify(binaryStringToBytes(atob(response.systemVersion.signature.value)), input, key)) {
296
+ throw new Error("Verification failed: Invalid system version signature");
297
+ }
298
+ if (!equalBytes(attestationReport.slice(REPORT_OFFSETS.measurement, REPORT_OFFSETS.measurement + 48), launchMeasurement)) {
299
+ throw new Error("Reported measurement doesn't match expected measurement");
300
+ }
301
+ return launchMeasurement;
302
+ });
303
+ }
304
+ _verifyReport(attestationReport, vcekChain) {
305
+ return __awaiter(this, void 0, void 0, function* () {
306
+ const { requirements, publicKey } = yield this._verifyVcekChain(vcekChain);
307
+ const sig = {
308
+ r: bytesToNumberLE(attestationReport.slice(REPORT_OFFSETS.signatureR, REPORT_OFFSETS.signatureR + 48)),
309
+ s: bytesToNumberLE(attestationReport.slice(REPORT_OFFSETS.signatureS, REPORT_OFFSETS.signatureS + 48)),
310
+ };
311
+ if (!p384.verify(sig, attestationReport.slice(0, 672), new Uint8Array(publicKey), { prehash: true })) {
312
+ throw new Error("Verification failed: Invalid attestation report signature");
313
+ }
314
+ if ((attestationReport[REPORT_OFFSETS.platInfo] & (1 << 5)) == 0) {
315
+ throw new Error("Verification failed: Alias check complete is false");
316
+ }
317
+ if ([
318
+ attestationReport[REPORT_OFFSETS.committedMajor],
319
+ attestationReport[REPORT_OFFSETS.committedMinor],
320
+ attestationReport[REPORT_OFFSETS.committedBuild],
321
+ ] < requirements.minCommittedVersion) {
322
+ throw new Error("Verification failed: Firmware version too small");
323
+ }
324
+ if (attestationReport[REPORT_OFFSETS.committedTcbSnp] <
325
+ requirements.minCommitedTcbSnp) {
326
+ throw new Error("Verification failed: SNP patch level too small");
327
+ }
328
+ });
329
+ }
330
+ _verifyVcekChain(vcek) {
331
+ return __awaiter(this, void 0, void 0, function* () {
332
+ let cert;
333
+ try {
334
+ cert = new x509.X509Certificate(vcek);
335
+ }
336
+ catch (e) {
337
+ throw new Error(`Invalid VCEK: ${e}\n\nVCEK: ${vcek}`);
338
+ }
339
+ const chain = yield ROOT_STORE.build(cert);
340
+ if (chain.length != 3) {
341
+ throw new Error(`Certificate chain verification failed\n\nChain: ${chain}`);
342
+ }
343
+ const productExt = chain[0].extensions.find((ext) => ext.type == "1.3.6.1.4.1.3704.1.2");
344
+ if (!productExt) {
345
+ throw new Error(`Could not find product name\n\nExtensions: ${chain[0].extensions}`);
346
+ }
347
+ const product = String.fromCharCode(...new Uint8Array(productExt.value.slice(2))).split("-", 1)[0];
348
+ if (!(product in this._requirements)) {
349
+ throw new Error(`Unexpected product: ${product}`);
350
+ }
351
+ const subjectPublicKeyInfo = AsnConvert.parse(chain[0].publicKey.rawData, SubjectPublicKeyInfo);
352
+ return {
353
+ requirements: this._requirements[product],
354
+ publicKey: new Uint8Array(subjectPublicKeyInfo.subjectPublicKey),
355
+ };
356
+ });
357
+ }
358
+ }
359
+ export function binaryStringToBytes(binaryString) {
360
+ const byteArray = new Uint8Array(binaryString.length);
361
+ for (let i = 0; i < binaryString.length; i++) {
362
+ byteArray[i] = binaryString.charCodeAt(i);
363
+ }
364
+ return byteArray;
365
+ }
366
+ export function bytesToBinaryString(bytes) {
367
+ let binaryString = "";
368
+ for (let i = 0; i < bytes.length; i++) {
369
+ binaryString += String.fromCharCode(bytes[i]);
370
+ }
371
+ return binaryString;
372
+ }
373
+ function _createCipher(publicKey, secretKey, info, nonce) {
374
+ const sharedSecret = x25519.getSharedSecret(secretKey, publicKey);
375
+ const key = hkdf(wrapConstructor(() => blake2b.create({ dkLen: 64 })), sharedSecret, Uint8Array.from([]), info, 32);
376
+ return chacha20poly1305(key, nonce);
377
+ }
378
+ function _getInfo(ephemeralPublicKey, recipientPublicKey) {
379
+ const info = new Uint8Array(ephemeralPublicKey.length + recipientPublicKey.length);
380
+ info.set(ephemeralPublicKey);
381
+ info.set(recipientPublicKey, ephemeralPublicKey.length);
382
+ return info;
383
+ }
384
+ function _getSealNonce(ephemeralPublicKey, recipientPublicKey) {
385
+ return blake2b
386
+ .create({ dkLen: 12 })
387
+ .update(ephemeralPublicKey)
388
+ .update(recipientPublicKey)
389
+ .digest();
390
+ }
391
+ const REPORT_OFFSETS = {
392
+ sigAlgo: 52,
393
+ platInfo: 64,
394
+ reportData: 80,
395
+ measurement: 144,
396
+ committedTcbSnp: 486,
397
+ committedBuild: 492,
398
+ committedMinor: 493,
399
+ committedMajor: 494,
400
+ signatureR: 672,
401
+ signatureS: 744,
402
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "routex-settlement",
3
+ "version": "0.1.1",
4
+ "description": "Key settlement for the YAXI routex client",
5
+ "homepage": "https://yaxi.tech",
6
+ "author": "YAXI GmbH",
7
+ "license": "UNLICENSED",
8
+ "type": "module",
9
+ "module": "./index.js",
10
+ "exports": "./index.js",
11
+ "types": "./index.d.ts",
12
+ "files": [
13
+ "index.d.ts",
14
+ "index.js"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc --build",
18
+ "clean": "tsc --build --clean",
19
+ "lint": "eslint",
20
+ "test": "tsc --build && NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest"
21
+ },
22
+ "dependencies": {
23
+ "@noble/ciphers": "^1.2.1",
24
+ "@noble/curves": "^1.8.1",
25
+ "@noble/hashes": "^1.7.1",
26
+ "@peculiar/asn1-schema": "^2.3.15",
27
+ "@peculiar/asn1-x509": "^2.3.15",
28
+ "@peculiar/x509": "^1.12.3"
29
+ },
30
+ "devDependencies": {
31
+ "@eslint/js": "^9.21.0",
32
+ "eslint": "^9.21.0",
33
+ "globals": "^16.0.0",
34
+ "jest": "^29.7.0",
35
+ "typescript": "^5.7.3",
36
+ "typescript-eslint": "^8.24.1"
37
+ }
38
+ }