@cubist-labs/cubesigner-sdk-key-import 0.4.68-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.
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.newMnemonicKeyPackage = newMnemonicKeyPackage;
27
+ exports.parseDerivationPath = parseDerivationPath;
28
+ const msgpackr_1 = require("msgpackr");
29
+ const english_1 = require("@scure/bip39/wordlists/english");
30
+ const bip39 = __importStar(require("@scure/bip39"));
31
+ /**
32
+ * Create a new MnemonicKeyPackage value
33
+ *
34
+ * @param { MnemonicToImport } mne A BIP39 mnemonic and optional BIP39 password and BIP32 derivation path
35
+ * @return { Uint8Array } A serialized key package for import to CubeSigner
36
+ */
37
+ function newMnemonicKeyPackage(mne) {
38
+ const entropy = bip39.mnemonicToEntropy(mne.mnemonic, english_1.wordlist);
39
+ const path = !mne.derivationPath ? [] : parseDerivationPath(mne.derivationPath);
40
+ const password = mne.password ?? "";
41
+ const mnePkg = {
42
+ EnglishMnemonic: {
43
+ mnemonic: {
44
+ entropy,
45
+ },
46
+ der_path: {
47
+ path,
48
+ },
49
+ password,
50
+ },
51
+ };
52
+ return (0, msgpackr_1.encode)(mnePkg);
53
+ }
54
+ // constants for derivation path parsing
55
+ const DER_HARDENED = 1n << 31n;
56
+ const DER_MAX = 1n << 32n;
57
+ /**
58
+ * Parse a derivation path into a sequence of 32-bit integers
59
+ *
60
+ * @param { string } derp The derivation path to parse; must start with 'm/'
61
+ * @return { number[] } The parsed path
62
+ */
63
+ function parseDerivationPath(derp) {
64
+ derp = derp.toLowerCase();
65
+ if (derp === "m") {
66
+ return [];
67
+ }
68
+ if (!derp.startsWith("m/")) {
69
+ throw new Error('Derivation path must start with "m/"');
70
+ }
71
+ const parts = derp.slice(2).split("/");
72
+ const ret = [];
73
+ for (let part of parts) {
74
+ let hardened = false;
75
+ if (part.endsWith("'") || part.endsWith("h")) {
76
+ hardened = true;
77
+ part = part.slice(0, part.length - 1);
78
+ }
79
+ if (part === "") {
80
+ throw new Error("Invalid derivation path: empty element");
81
+ }
82
+ let value = BigInt(part);
83
+ if (value >= DER_MAX) {
84
+ throw new Error("Derivation path element greater than 2^32 is invalid");
85
+ }
86
+ if (hardened) {
87
+ value = value | DER_HARDENED;
88
+ }
89
+ ret.push(Number(value));
90
+ }
91
+ return ret;
92
+ }
93
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW5lbW9uaWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbW5lbW9uaWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWlDQSxzREFnQkM7QUFZRCxrREE2QkM7QUExRkQsdUNBQThDO0FBQzlDLDREQUEwRDtBQUMxRCxvREFBc0M7QUF5QnRDOzs7OztHQUtHO0FBQ0gsU0FBZ0IscUJBQXFCLENBQUMsR0FBcUI7SUFDekQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsa0JBQVEsQ0FBQyxDQUFDO0lBQ2hFLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDaEYsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7SUFDcEMsTUFBTSxNQUFNLEdBQUc7UUFDYixlQUFlLEVBQUU7WUFDZixRQUFRLEVBQUU7Z0JBQ1IsT0FBTzthQUNSO1lBQ0QsUUFBUSxFQUFFO2dCQUNSLElBQUk7YUFDTDtZQUNELFFBQVE7U0FDVDtLQUNGLENBQUM7SUFDRixPQUFPLElBQUEsaUJBQVEsRUFBQyxNQUFNLENBQUMsQ0FBQztBQUMxQixDQUFDO0FBRUQsd0NBQXdDO0FBQ3hDLE1BQU0sWUFBWSxHQUFHLEVBQUUsSUFBSSxHQUFHLENBQUM7QUFDL0IsTUFBTSxPQUFPLEdBQUcsRUFBRSxJQUFJLEdBQUcsQ0FBQztBQUUxQjs7Ozs7R0FLRztBQUNILFNBQWdCLG1CQUFtQixDQUFDLElBQVk7SUFDOUMsSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUMxQixJQUFJLElBQUksS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUNqQixPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQzNCLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkMsTUFBTSxHQUFHLEdBQUcsRUFBRSxDQUFDO0lBQ2YsS0FBSyxJQUFJLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUN2QixJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUM7UUFDckIsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM3QyxRQUFRLEdBQUcsSUFBSSxDQUFDO1lBQ2hCLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3hDLENBQUM7UUFDRCxJQUFJLElBQUksS0FBSyxFQUFFLEVBQUUsQ0FBQztZQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLHdDQUF3QyxDQUFDLENBQUM7UUFDNUQsQ0FBQztRQUNELElBQUksS0FBSyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixJQUFJLEtBQUssSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNyQixNQUFNLElBQUksS0FBSyxDQUFDLHNEQUFzRCxDQUFDLENBQUM7UUFDMUUsQ0FBQztRQUNELElBQUksUUFBUSxFQUFFLENBQUM7WUFDYixLQUFLLEdBQUcsS0FBSyxHQUFHLFlBQVksQ0FBQztRQUMvQixDQUFDO1FBQ0QsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUMxQixDQUFDO0lBQ0QsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZW5jb2RlIGFzIG1wRW5jb2RlIH0gZnJvbSBcIm1zZ3BhY2tyXCI7XG5pbXBvcnQgeyB3b3JkbGlzdCB9IGZyb20gXCJAc2N1cmUvYmlwMzkvd29yZGxpc3RzL2VuZ2xpc2hcIjtcbmltcG9ydCAqIGFzIGJpcDM5IGZyb20gXCJAc2N1cmUvYmlwMzlcIjtcblxuLy8gVGhlIEtleVBhY2thZ2UgdHlwZSBmcm9tIEN1YmVTaWduZXJcbmV4cG9ydCB0eXBlIE1uZW1vbmljS2V5UGFja2FnZSA9IHtcbiAgRW5nbGlzaE1uZW1vbmljOiB7XG4gICAgbW5lbW9uaWM6IHtcbiAgICAgIGVudHJvcHk6IFVpbnQ4QXJyYXk7XG4gICAgfTtcbiAgICBkZXJfcGF0aDoge1xuICAgICAgcGF0aDogbnVtYmVyW107XG4gICAgfTtcbiAgICBwYXNzd29yZDogc3RyaW5nO1xuICB9O1xufTtcblxuLyoqXG4gKiBBIEJJUDM5IG1uZW1vbmljIHRvIGJlIGltcG9ydGVkLCBwbHVzIG9wdGlvbmFsIEJJUDM5IHBhc3N3b3JkXG4gKiBhbmQgQklQMzIgZGVyaXZhdGlvbiBwYXRoLlxuICovXG5leHBvcnQgdHlwZSBNbmVtb25pY1RvSW1wb3J0ID0ge1xuICBtbmVtb25pYzogc3RyaW5nO1xuICBkZXJpdmF0aW9uUGF0aD86IHN0cmluZztcbiAgcGFzc3dvcmQ/OiBzdHJpbmc7XG59O1xuXG4vKipcbiAqIENyZWF0ZSBhIG5ldyBNbmVtb25pY0tleVBhY2thZ2UgdmFsdWVcbiAqXG4gKiBAcGFyYW0geyBNbmVtb25pY1RvSW1wb3J0IH0gbW5lIEEgQklQMzkgbW5lbW9uaWMgYW5kIG9wdGlvbmFsIEJJUDM5IHBhc3N3b3JkIGFuZCBCSVAzMiBkZXJpdmF0aW9uIHBhdGhcbiAqIEByZXR1cm4geyBVaW50OEFycmF5IH0gQSBzZXJpYWxpemVkIGtleSBwYWNrYWdlIGZvciBpbXBvcnQgdG8gQ3ViZVNpZ25lclxuICovXG5leHBvcnQgZnVuY3Rpb24gbmV3TW5lbW9uaWNLZXlQYWNrYWdlKG1uZTogTW5lbW9uaWNUb0ltcG9ydCk6IFVpbnQ4QXJyYXkge1xuICBjb25zdCBlbnRyb3B5ID0gYmlwMzkubW5lbW9uaWNUb0VudHJvcHkobW5lLm1uZW1vbmljLCB3b3JkbGlzdCk7XG4gIGNvbnN0IHBhdGggPSAhbW5lLmRlcml2YXRpb25QYXRoID8gW10gOiBwYXJzZURlcml2YXRpb25QYXRoKG1uZS5kZXJpdmF0aW9uUGF0aCk7XG4gIGNvbnN0IHBhc3N3b3JkID0gbW5lLnBhc3N3b3JkID8/IFwiXCI7XG4gIGNvbnN0IG1uZVBrZyA9IHtcbiAgICBFbmdsaXNoTW5lbW9uaWM6IHtcbiAgICAgIG1uZW1vbmljOiB7XG4gICAgICAgIGVudHJvcHksXG4gICAgICB9LFxuICAgICAgZGVyX3BhdGg6IHtcbiAgICAgICAgcGF0aCxcbiAgICAgIH0sXG4gICAgICBwYXNzd29yZCxcbiAgICB9LFxuICB9O1xuICByZXR1cm4gbXBFbmNvZGUobW5lUGtnKTtcbn1cblxuLy8gY29uc3RhbnRzIGZvciBkZXJpdmF0aW9uIHBhdGggcGFyc2luZ1xuY29uc3QgREVSX0hBUkRFTkVEID0gMW4gPDwgMzFuO1xuY29uc3QgREVSX01BWCA9IDFuIDw8IDMybjtcblxuLyoqXG4gKiBQYXJzZSBhIGRlcml2YXRpb24gcGF0aCBpbnRvIGEgc2VxdWVuY2Ugb2YgMzItYml0IGludGVnZXJzXG4gKlxuICogQHBhcmFtIHsgc3RyaW5nIH0gZGVycCBUaGUgZGVyaXZhdGlvbiBwYXRoIHRvIHBhcnNlOyBtdXN0IHN0YXJ0IHdpdGggJ20vJ1xuICogQHJldHVybiB7IG51bWJlcltdIH0gVGhlIHBhcnNlZCBwYXRoXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZURlcml2YXRpb25QYXRoKGRlcnA6IHN0cmluZyk6IG51bWJlcltdIHtcbiAgZGVycCA9IGRlcnAudG9Mb3dlckNhc2UoKTtcbiAgaWYgKGRlcnAgPT09IFwibVwiKSB7XG4gICAgcmV0dXJuIFtdO1xuICB9XG4gIGlmICghZGVycC5zdGFydHNXaXRoKFwibS9cIikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0Rlcml2YXRpb24gcGF0aCBtdXN0IHN0YXJ0IHdpdGggXCJtL1wiJyk7XG4gIH1cbiAgY29uc3QgcGFydHMgPSBkZXJwLnNsaWNlKDIpLnNwbGl0KFwiL1wiKTtcbiAgY29uc3QgcmV0ID0gW107XG4gIGZvciAobGV0IHBhcnQgb2YgcGFydHMpIHtcbiAgICBsZXQgaGFyZGVuZWQgPSBmYWxzZTtcbiAgICBpZiAocGFydC5lbmRzV2l0aChcIidcIikgfHwgcGFydC5lbmRzV2l0aChcImhcIikpIHtcbiAgICAgIGhhcmRlbmVkID0gdHJ1ZTtcbiAgICAgIHBhcnQgPSBwYXJ0LnNsaWNlKDAsIHBhcnQubGVuZ3RoIC0gMSk7XG4gICAgfVxuICAgIGlmIChwYXJ0ID09PSBcIlwiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIGRlcml2YXRpb24gcGF0aDogZW1wdHkgZWxlbWVudFwiKTtcbiAgICB9XG4gICAgbGV0IHZhbHVlID0gQmlnSW50KHBhcnQpO1xuICAgIGlmICh2YWx1ZSA+PSBERVJfTUFYKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJEZXJpdmF0aW9uIHBhdGggZWxlbWVudCBncmVhdGVyIHRoYW4gMl4zMiBpcyBpbnZhbGlkXCIpO1xuICAgIH1cbiAgICBpZiAoaGFyZGVuZWQpIHtcbiAgICAgIHZhbHVlID0gdmFsdWUgfCBERVJfSEFSREVORUQ7XG4gICAgfVxuICAgIHJldC5wdXNoKE51bWJlcih2YWx1ZSkpO1xuICB9XG4gIHJldHVybiByZXQ7XG59XG4iXX0=
package/dist/util.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Converts a bigint to a big-endian Uint8Array of a specified length
3
+ *
4
+ * @param { bigint } n The value to convert
5
+ * @param { number } l The length in bytes
6
+ * @return { Uint8Array } The big-endian bytes
7
+ */
8
+ export declare function toBigEndian(n: bigint, l: number): Uint8Array;
9
+ /**
10
+ * Concatenates an array of Uint8Arrays into a single array
11
+ *
12
+ * @param { Uint8Array[] } parts The parts to be concatenated
13
+ * @return { Uint8Array } The concatenated array
14
+ */
15
+ export declare function concatArrays(parts: Uint8Array[]): Uint8Array;
16
+ /**
17
+ * Get the current time in seconds since UNIX epoch
18
+ *
19
+ * @return { BigInt } Seconds since UNIX epoch
20
+ */
21
+ export declare function nowEpochMillis(): bigint;
22
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,CAW5D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,CAW5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
package/dist/util.js ADDED
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toBigEndian = toBigEndian;
4
+ exports.concatArrays = concatArrays;
5
+ exports.nowEpochMillis = nowEpochMillis;
6
+ /**
7
+ * Converts a bigint to a big-endian Uint8Array of a specified length
8
+ *
9
+ * @param { bigint } n The value to convert
10
+ * @param { number } l The length in bytes
11
+ * @return { Uint8Array } The big-endian bytes
12
+ */
13
+ function toBigEndian(n, l) {
14
+ if (n >= 1n << (8n * BigInt(l))) {
15
+ throw new Error(`Cannot convert ${n} to ${l} big-endian bytes (overflow)`);
16
+ }
17
+ let nn = n;
18
+ const ret = new Uint8Array(l);
19
+ for (let i = l - 1; i >= 0; --i) {
20
+ ret[i] = Number(nn % 256n);
21
+ nn = nn >> 8n;
22
+ }
23
+ return ret;
24
+ }
25
+ /**
26
+ * Concatenates an array of Uint8Arrays into a single array
27
+ *
28
+ * @param { Uint8Array[] } parts The parts to be concatenated
29
+ * @return { Uint8Array } The concatenated array
30
+ */
31
+ function concatArrays(parts) {
32
+ const totalLen = parts.reduce((len, part) => len + part.length, 0);
33
+ let lenSoFar = 0;
34
+ const ret = new Uint8Array(totalLen);
35
+ parts.forEach((part) => {
36
+ ret.set(part, lenSoFar);
37
+ lenSoFar += part.length;
38
+ });
39
+ return ret;
40
+ }
41
+ /**
42
+ * Get the current time in seconds since UNIX epoch
43
+ *
44
+ * @return { BigInt } Seconds since UNIX epoch
45
+ */
46
+ function nowEpochMillis() {
47
+ return BigInt(Date.now());
48
+ }
49
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBT0Esa0NBV0M7QUFRRCxvQ0FXQztBQU9ELHdDQUVDO0FBOUNEOzs7Ozs7R0FNRztBQUNILFNBQWdCLFdBQVcsQ0FBQyxDQUFTLEVBQUUsQ0FBUztJQUM5QyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNoQyxNQUFNLElBQUksS0FBSyxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFDRCxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDWCxNQUFNLEdBQUcsR0FBRyxJQUFJLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ2hDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQzNCLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQWdCLFlBQVksQ0FBQyxLQUFtQjtJQUM5QyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFbkUsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLE1BQU0sR0FBRyxHQUFHLElBQUksVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3JDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtRQUNyQixHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztRQUN4QixRQUFRLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUMxQixDQUFDLENBQUMsQ0FBQztJQUVILE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFnQixjQUFjO0lBQzVCLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQzVCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbnZlcnRzIGEgYmlnaW50IHRvIGEgYmlnLWVuZGlhbiBVaW50OEFycmF5IG9mIGEgc3BlY2lmaWVkIGxlbmd0aFxuICpcbiAqIEBwYXJhbSB7IGJpZ2ludCB9IG4gVGhlIHZhbHVlIHRvIGNvbnZlcnRcbiAqIEBwYXJhbSB7IG51bWJlciB9IGwgVGhlIGxlbmd0aCBpbiBieXRlc1xuICogQHJldHVybiB7IFVpbnQ4QXJyYXkgfSBUaGUgYmlnLWVuZGlhbiBieXRlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gdG9CaWdFbmRpYW4objogYmlnaW50LCBsOiBudW1iZXIpOiBVaW50OEFycmF5IHtcbiAgaWYgKG4gPj0gMW4gPDwgKDhuICogQmlnSW50KGwpKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgQ2Fubm90IGNvbnZlcnQgJHtufSB0byAke2x9IGJpZy1lbmRpYW4gYnl0ZXMgKG92ZXJmbG93KWApO1xuICB9XG4gIGxldCBubiA9IG47XG4gIGNvbnN0IHJldCA9IG5ldyBVaW50OEFycmF5KGwpO1xuICBmb3IgKGxldCBpID0gbCAtIDE7IGkgPj0gMDsgLS1pKSB7XG4gICAgcmV0W2ldID0gTnVtYmVyKG5uICUgMjU2bik7XG4gICAgbm4gPSBubiA+PiA4bjtcbiAgfVxuICByZXR1cm4gcmV0O1xufVxuXG4vKipcbiAqIENvbmNhdGVuYXRlcyBhbiBhcnJheSBvZiBVaW50OEFycmF5cyBpbnRvIGEgc2luZ2xlIGFycmF5XG4gKlxuICogQHBhcmFtIHsgVWludDhBcnJheVtdIH0gcGFydHMgVGhlIHBhcnRzIHRvIGJlIGNvbmNhdGVuYXRlZFxuICogQHJldHVybiB7IFVpbnQ4QXJyYXkgfSBUaGUgY29uY2F0ZW5hdGVkIGFycmF5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25jYXRBcnJheXMocGFydHM6IFVpbnQ4QXJyYXlbXSk6IFVpbnQ4QXJyYXkge1xuICBjb25zdCB0b3RhbExlbiA9IHBhcnRzLnJlZHVjZSgobGVuLCBwYXJ0KSA9PiBsZW4gKyBwYXJ0Lmxlbmd0aCwgMCk7XG5cbiAgbGV0IGxlblNvRmFyID0gMDtcbiAgY29uc3QgcmV0ID0gbmV3IFVpbnQ4QXJyYXkodG90YWxMZW4pO1xuICBwYXJ0cy5mb3JFYWNoKChwYXJ0KSA9PiB7XG4gICAgcmV0LnNldChwYXJ0LCBsZW5Tb0Zhcik7XG4gICAgbGVuU29GYXIgKz0gcGFydC5sZW5ndGg7XG4gIH0pO1xuXG4gIHJldHVybiByZXQ7XG59XG5cbi8qKlxuICogR2V0IHRoZSBjdXJyZW50IHRpbWUgaW4gc2Vjb25kcyBzaW5jZSBVTklYIGVwb2NoXG4gKlxuICogQHJldHVybiB7IEJpZ0ludCB9IFNlY29uZHMgc2luY2UgVU5JWCBlcG9jaFxuICovXG5leHBvcnQgZnVuY3Rpb24gbm93RXBvY2hNaWxsaXMoKTogYmlnaW50IHtcbiAgcmV0dXJuIEJpZ0ludChEYXRlLm5vdygpKTtcbn1cbiJdfQ==
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@cubist-labs/cubesigner-sdk-key-import",
3
+ "version": "0.4.68-0",
4
+ "description": "Client-side key-import machinery for CubeSigner",
5
+ "license": "MIT OR Apache-2.0",
6
+ "author": "Cubist, Inc.",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "tsconfig.json",
11
+ "src/**",
12
+ "dist/**",
13
+ "../../NOTICE",
14
+ "../../LICENSE-APACHE",
15
+ "../../LICENSE_MIT"
16
+ ],
17
+ "scripts": {
18
+ "build": "npx tsc",
19
+ "prepack": "npx tsc",
20
+ "repl": "npx node --import tsx",
21
+ "test": "npx --node-options='--experimental-vm-modules' jest --maxWorkers=1"
22
+ },
23
+ "peerDependencies": {
24
+ "@cubist-labs/cubesigner-sdk": "^0.4.68-0"
25
+ },
26
+ "devDependencies": {
27
+ "tsx": "^4.19.0"
28
+ },
29
+ "dependencies": {
30
+ "@auth0/cose": "^1.0.2",
31
+ "@hpke/core": "^1.3.1",
32
+ "@peculiar/asn1-ecc": "^2.3.13",
33
+ "@peculiar/asn1-schema": "^2.3.13",
34
+ "@peculiar/x509": "^1.12.1",
35
+ "@scure/bip39": "^1.4.0",
36
+ "cbor-x": "^1.6.0",
37
+ "msgpackr": "^1.11.0"
38
+ }
39
+ }
package/src/import.ts ADDED
@@ -0,0 +1,426 @@
1
+ import type {
2
+ CreateKeyImportKeyResponse,
3
+ ImportKeyRequest,
4
+ ImportKeyRequestMaterial,
5
+ Key,
6
+ KeyType,
7
+ Org,
8
+ } from "@cubist-labs/cubesigner-sdk";
9
+ import { loadCrypto, loadSubtleCrypto } from "@cubist-labs/cubesigner-sdk";
10
+ import { CipherSuite, Aes256Gcm, HkdfSha384, DhkemP384HkdfSha384 } from "@hpke/core";
11
+ import { ECParameters } from "@peculiar/asn1-ecc";
12
+ import { AsnParser } from "@peculiar/asn1-schema";
13
+ import {
14
+ AlgorithmProvider,
15
+ X509Certificate,
16
+ cryptoProvider as x509CryptoProvider,
17
+ } from "@peculiar/x509";
18
+
19
+ import type { MnemonicToImport } from "./mnemonic";
20
+ import { newMnemonicKeyPackage } from "./mnemonic";
21
+ import { toBigEndian, concatArrays, nowEpochMillis } from "./util";
22
+
23
+ // domain-separation tag used when generating signing hash for import key
24
+ const IMPORT_KEY_SIGNING_DST = new TextEncoder().encode("CUBESIGNER_EPHEMERAL_IMPORT_P384");
25
+
26
+ // attestation document slack times
27
+ const MAX_ATTESTATION_AGE_MINUTES = 15n;
28
+ const MAX_ATTESTATION_FUTURE_MINUTES = 5n;
29
+ const WIK_REFRESH_EARLY_MILLIS = 60_000n;
30
+
31
+ // OIDs for elliptic curve X509 certs
32
+ const EC_PUBLIC_KEY = "1.2.840.10045.2.1";
33
+ const NIST_P384 = "1.3.132.0.34";
34
+
35
+ // Maximum number of keys to import in a single API call
36
+ const MAX_IMPORTS_PER_API_CALL = 32n;
37
+
38
+ // AWS Nitro Enclaves root CA certificate
39
+ // https://aws-nitro-enclaves.amazonaws.com/AWS_NitroEnclaves_Root-G1.zip
40
+ //
41
+ // See the documentation about AWS Nitro Enclaves verification:
42
+ // https://docs.aws.amazon.com/enclaves/latest/user/verify-root.html
43
+ const AWS_CA_CERT =
44
+ "MIICETCCAZagAwIBAgIRAPkxdWgbkK/hHUbMtOTn+FYwCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMCVVMxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMRswGQYDVQQDDBJhd3Mubml0cm8tZW5jbGF2ZXMwHhcNMTkxMDI4MTMyODA1WhcNNDkxMDI4MTQyODA1WjBJMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQLDANBV1MxGzAZBgNVBAMMEmF3cy5uaXRyby1lbmNsYXZlczB2MBAGByqGSM49AgEGBSuBBAAiA2IABPwCVOumCMHzaHDimtqQvkY4MpJzbolL//Zy2YlES1BR5TSksfbb48C8WBoyt7F2Bw7eEtaaP+ohG2bnUs990d0JX28TcPQXCEPZ3BABIeTPYwEoCWZEh8l5YoQwTcU/9KNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkCW1DdkFR+eWw5b6cp3PmanfS5YwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCjfy+Rocm9Xue4YnwWmNJVA44fA0P5W2OpYow9OYCVRaEevL8uO1XYru5xtMPWrfMCMQCi85sWBbJwKKXdS6BptQFuZbT73o/gBh1qUxl/nNr12UO8Yfwr6wPLb+6NIwLz3/Y=";
45
+
46
+ /**
47
+ * The result of deserializing a CreateKeyImportKeyResponse
48
+ */
49
+ class WrappedImportKey {
50
+ readonly verifiedHash: Uint8Array;
51
+
52
+ readonly publicKey: Uint8Array;
53
+ readonly publicKeyBase64: string;
54
+
55
+ readonly skEnc: Uint8Array;
56
+ readonly skEncBase64: string;
57
+
58
+ readonly dkEnc: Uint8Array;
59
+ readonly dkEncBase64: string;
60
+
61
+ readonly expEpochSeconds: bigint;
62
+ readonly #enclaveAttestation: Uint8Array;
63
+ readonly #enclaveSignature: Uint8Array;
64
+
65
+ /**
66
+ * Constructor. This is only called from `WrappedImportKey.createAndVerify()`.
67
+ *
68
+ * @param { CreateKeyImportKeyResponse } resp The response from CubeSigner
69
+ */
70
+ private constructor(resp: CreateKeyImportKeyResponse) {
71
+ if (!resp.enclave_attestation || !resp.enclave_signature) {
72
+ throw new Error("No attestation found in CreateKeyImportKeyResponse");
73
+ }
74
+
75
+ // parse the response
76
+ this.publicKey = new Uint8Array(Buffer.from(resp.public_key, "base64"));
77
+ this.publicKeyBase64 = resp.public_key;
78
+
79
+ this.skEnc = new Uint8Array(Buffer.from(resp.sk_enc, "base64"));
80
+ this.skEncBase64 = resp.sk_enc;
81
+
82
+ this.dkEnc = new Uint8Array(Buffer.from(resp.dk_enc, "base64"));
83
+ this.dkEncBase64 = resp.dk_enc;
84
+
85
+ this.#enclaveAttestation = new Uint8Array(Buffer.from(resp.enclave_attestation, "base64"));
86
+ this.#enclaveSignature = new Uint8Array(Buffer.from(resp.enclave_signature, "base64"));
87
+ this.expEpochSeconds = BigInt(resp.expires);
88
+
89
+ // this array is updated in createAndVerify once verification succeeds
90
+ this.verifiedHash = new Uint8Array(32);
91
+ }
92
+
93
+ /**
94
+ * Create and verify an instance of this type
95
+ *
96
+ * @param { CreateKeyImportKeyResponse } resp The response from CubeSigner
97
+ * @param { SubtleCrypto } subtle An instance of SubtleCrypto used for verification
98
+ * @return { Promise<WrappedImportKey> } A newly constructed instance
99
+ */
100
+ public static async createAndVerify(
101
+ resp: CreateKeyImportKeyResponse,
102
+ subtle: SubtleCrypto,
103
+ ): Promise<WrappedImportKey> {
104
+ const ret = new WrappedImportKey(resp);
105
+ const hash = await ret.#verifyImportKey(subtle);
106
+ ret.verifiedHash.set(hash);
107
+ return ret;
108
+ }
109
+
110
+ /**
111
+ * Verify this wrapped import key.
112
+ *
113
+ * @param { SubtleCrypto } subtle An instance of SubtleCrypto used for verification
114
+ * @return { Promise<Uint8Array> } The hash of the successfully verified wrapped import key
115
+ */
116
+ async #verifyImportKey(subtle: SubtleCrypto): Promise<Uint8Array> {
117
+ // check expiration date
118
+ if (nowEpochMillis() > this.expEpochSeconds * 1000n) {
119
+ throw new Error("Import key is expired");
120
+ }
121
+
122
+ // make sure that there is an attestation
123
+ if (!this.#enclaveSignature || !this.#enclaveAttestation) {
124
+ throw new Error("No attestation found");
125
+ }
126
+ const signing_key = await verifyAttestationKey(this.#enclaveAttestation);
127
+
128
+ // we use subtlecrypto's impl of RSA-PSS verification
129
+ const rsaPssKeyParams = {
130
+ name: "RSA-PSS",
131
+ hash: "SHA-256",
132
+ };
133
+ const pubkey = await subtle.importKey("spki", signing_key, rsaPssKeyParams, true, ["verify"]);
134
+ const pubkeyAlg = pubkey.algorithm as unknown as { modulusLength: number };
135
+
136
+ // compute the signing hash and verify the signature
137
+ const message = this.#signedData();
138
+ const mlen = Number(BigInt(pubkeyAlg.modulusLength) / 8n);
139
+ const rsaPssParams = {
140
+ name: "RSA-PSS",
141
+ saltLength: mlen - 2 - 32,
142
+ };
143
+
144
+ if (await subtle.verify(rsaPssParams, pubkey, this.#enclaveSignature, message)) {
145
+ return new Uint8Array(await subtle.digest("SHA-256", message));
146
+ }
147
+ throw new Error("Import key signature verification failed");
148
+ }
149
+
150
+ /**
151
+ * Returns `true` if this WrappedImportKey needs to be refreshed.
152
+ *
153
+ * @return { boolean } True just if this key needs to be refreshed.
154
+ */
155
+ public needsRefresh(): boolean {
156
+ // force refresh if we're within WIK_REFRESH_EARLY_MILLIS of the expiration
157
+ return nowEpochMillis() + WIK_REFRESH_EARLY_MILLIS > this.expEpochSeconds * 1000n;
158
+ }
159
+
160
+ /**
161
+ * Computes the signing hash for a wrapped import key
162
+ *
163
+ * @return { Uint8Array } The signing hash
164
+ */
165
+ #signedData(): Uint8Array {
166
+ const parts: Uint8Array[] = [
167
+ // domain separation tag
168
+ toBigEndian(BigInt(IMPORT_KEY_SIGNING_DST.length), 2),
169
+ IMPORT_KEY_SIGNING_DST,
170
+
171
+ // public key
172
+ toBigEndian(BigInt(this.publicKey.length), 2),
173
+ this.publicKey,
174
+
175
+ // sk_enc
176
+ toBigEndian(BigInt(this.skEnc.length), 2),
177
+ this.skEnc,
178
+
179
+ // dk_enc
180
+ toBigEndian(BigInt(this.dkEnc.length), 2),
181
+ this.dkEnc,
182
+
183
+ // 8-byte big-endian expiration time in seconds since UNIX epoch
184
+ toBigEndian(this.expEpochSeconds, 8),
185
+ ];
186
+
187
+ return concatArrays(parts);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * The return value from KeyImporter.#getWrappedImportAndPubKey()
193
+ */
194
+ type WrappedImportAndPubKey = {
195
+ wik: WrappedImportKey;
196
+ ipk: CryptoKey;
197
+ };
198
+
199
+ /**
200
+ * An import encryption key and the corresponding attestation document
201
+ */
202
+ export class KeyImporter {
203
+ #wrappedImportKey: null | WrappedImportKey = null;
204
+ #subtleCrypto: null | SubtleCrypto = null;
205
+ #publicKeyHandle: null | CryptoKey = null;
206
+ readonly #hpkeSuite: CipherSuite;
207
+ readonly #cs: Org;
208
+
209
+ /**
210
+ * Construct from a CubeSigner `Org` instance
211
+ *
212
+ * @param { Org } cs A CubeSigner `Org` instance
213
+ */
214
+ constructor(cs: Org) {
215
+ this.#cs = cs;
216
+ this.#hpkeSuite = new CipherSuite({
217
+ kem: new DhkemP384HkdfSha384(),
218
+ kdf: new HkdfSha384(),
219
+ aead: new Aes256Gcm(),
220
+ });
221
+ }
222
+
223
+ /**
224
+ * Check that the wrapped import key is unexpired and verified. Otherwise,
225
+ * request a new one, verify it, and update the verified signing hash.
226
+ *
227
+ * @return { Promise<[WrappedImportKey, CryptoKey]> } The verified signing hash.
228
+ */
229
+ async #getWrappedImportAndPubKey(): Promise<WrappedImportAndPubKey> {
230
+ if (!this.#wrappedImportKey) {
231
+ // first time we load a WrappedImportKey, make sure the x509 crypto
232
+ // provider is set correctly.
233
+ x509CryptoProvider.set(await loadCrypto());
234
+ }
235
+ if (!this.#wrappedImportKey || this.#wrappedImportKey.needsRefresh()) {
236
+ const kikResp = await this.#cs.createKeyImportKey();
237
+ const subtle = await this.#getSubtleCrypto();
238
+ const wik = await WrappedImportKey.createAndVerify(kikResp, subtle);
239
+
240
+ // import the public key from the WrappedImportKey
241
+ const p384Params = {
242
+ name: "ECDH",
243
+ namedCurve: "P-384",
244
+ };
245
+ this.#publicKeyHandle = await subtle.importKey("raw", wik.publicKey, p384Params, true, []);
246
+ this.#wrappedImportKey = wik;
247
+ }
248
+ return {
249
+ wik: this.#wrappedImportKey,
250
+ ipk: this.#publicKeyHandle!,
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Get or create an instance of SubtleCrypto.
256
+ *
257
+ * @return { SubtleCrypto } The instance of SubtleCrypto.
258
+ */
259
+ async #getSubtleCrypto(): Promise<SubtleCrypto> {
260
+ if (!this.#subtleCrypto) {
261
+ this.#subtleCrypto = await loadSubtleCrypto();
262
+ }
263
+ return this.#subtleCrypto;
264
+ }
265
+
266
+ /**
267
+ * Encrypts a set of mnemonics and imports them.
268
+ *
269
+ * @param { KeyType } keyType The type of key to import
270
+ * @param { MnemonicToImport[] } mnes The mnemonics to import, with optional derivation paths and passwords
271
+ * @return { Promise<Key[]> } `Key` objects for each imported key.
272
+ */
273
+ public async importMnemonics(keyType: KeyType, mnes: MnemonicToImport[]): Promise<Key[]> {
274
+ const nChunks = Number(
275
+ (BigInt(mnes.length) + MAX_IMPORTS_PER_API_CALL - 1n) / MAX_IMPORTS_PER_API_CALL,
276
+ );
277
+ const keys = [];
278
+
279
+ for (let i = 0; i < nChunks; ++i) {
280
+ // first, make sure that the wrapped import key is valid, i.e., that
281
+ // we have retrieved it and that it hasn't expired. We do this here
282
+ // for a couple reasons:
283
+ //
284
+ // - all encryptions in a give request must use the same import key, and
285
+ //
286
+ // - when importing a huge number of keys the import pubkey might expire
287
+ // during the import, so we check for expiration before each request
288
+ const { wik, ipk } = await this.#getWrappedImportAndPubKey();
289
+
290
+ // next, encrypt this chunk of mnemonics
291
+ const start = Number(MAX_IMPORTS_PER_API_CALL) * i;
292
+ const end = Number(MAX_IMPORTS_PER_API_CALL) + start;
293
+ const mneSlice = mnes.slice(start, end);
294
+ const key_material = [];
295
+ for (const mne of mneSlice) {
296
+ const keyPkg = newMnemonicKeyPackage(mne);
297
+ const material = await this.#encrypt(keyPkg, wik.verifiedHash, ipk);
298
+ key_material.push(material);
299
+ }
300
+
301
+ // construct the request
302
+ const req: ImportKeyRequest = {
303
+ public_key: wik.publicKeyBase64,
304
+ sk_enc: wik.skEncBase64,
305
+ dk_enc: wik.dkEncBase64,
306
+ expires: Number(wik.expEpochSeconds),
307
+ key_type: keyType,
308
+ key_material,
309
+ };
310
+
311
+ // send it and append the result to the return value
312
+ const resp = await this.#cs.importKeys(req);
313
+ keys.push(...resp);
314
+ }
315
+
316
+ return keys;
317
+ }
318
+
319
+ /**
320
+ * Encrypt to this wrapped import key. Stores the result in `this.encrypted_keys`
321
+ *
322
+ * @param { Uint8Array } data The data to encrypt
323
+ * @param { Uint8Array } verifiedHash The verified signing hash of the wrapped import key to which to encrypt
324
+ * @param { CryptoKey } pubkey The public key to encrypt to
325
+ * @return { Promise<ImportKeyRequestMaterial> } The encrypted key material
326
+ */
327
+ async #encrypt(
328
+ data: Uint8Array,
329
+ verifiedHash: Uint8Array,
330
+ pubkey: CryptoKey,
331
+ ): Promise<ImportKeyRequestMaterial> {
332
+ // set up the HPKE sender
333
+ const sender = await this.#hpkeSuite.createSenderContext({
334
+ recipientPublicKey: pubkey,
335
+ info: verifiedHash,
336
+ });
337
+
338
+ // encrypt and construct the return value
339
+ const senderCtext = await sender.seal(data);
340
+ return {
341
+ salt: "",
342
+ client_public_key: Buffer.from(sender.enc).toString("base64"),
343
+ ikm_enc: Buffer.from(senderCtext).toString("base64"),
344
+ };
345
+ }
346
+ }
347
+
348
+ /*
349
+ * An AWS Nitro attestation document
350
+ *
351
+ * https://github.com/aws/aws-nitro-enclaves-nsm-api/blob/4b851f3006c6fa98f23dcffb2cba03b39de9b8af/src/api/mod.rs#L208
352
+ */
353
+ type AttestationDoc = {
354
+ module_id: string;
355
+ digest: "SHA256" | "SHA384" | "SHA512";
356
+ timestamp: bigint;
357
+ pcrs: Map<number, Uint8Array>;
358
+ certificate: Uint8Array;
359
+ cabundle: Uint8Array[];
360
+ public_key?: Uint8Array;
361
+ user_data?: Uint8Array;
362
+ nonce?: Uint8Array;
363
+ };
364
+
365
+ /**
366
+ * Verifies the attestation key against the AWS Nitro Enclaves signing
367
+ * key and returns the attested signing key.
368
+ *
369
+ * @param { Uint8Array } attBytes An attestation from an AWS nitro enclave
370
+ * @return { Promise<Uint8Array> } The signing key that was attested, or null if verification failed
371
+ */
372
+ async function verifyAttestationKey(attBytes: Uint8Array): Promise<Uint8Array> {
373
+ // cbor-x is being imported as ESM, so we must asynchronously import it here.
374
+ // Because we only use that and auth0/cose here, we import both this way.
375
+ const { Sign1 } = await import("@auth0/cose");
376
+ const { decode: cborDecode } = await import("cbor-x");
377
+
378
+ const att = Sign1.decode(attBytes);
379
+ const attDoc = cborDecode(att.payload) as AttestationDoc;
380
+
381
+ // check expiration date of attestation
382
+ const latest = nowEpochMillis() + MAX_ATTESTATION_FUTURE_MINUTES * 60n * 1000n;
383
+ const earliest =
384
+ latest - (MAX_ATTESTATION_FUTURE_MINUTES + MAX_ATTESTATION_AGE_MINUTES) * 60n * 1000n;
385
+ if (attDoc.timestamp < earliest || attDoc.timestamp > latest) {
386
+ throw new Error("Attestation is expired");
387
+ }
388
+
389
+ // if there's no public key in this attestation, give up
390
+ if (!attDoc.public_key) {
391
+ throw new Error("Attestation did not include a signing public key");
392
+ }
393
+
394
+ // Verify certificate chain starting with AWS Nitro CA cert
395
+ let parent = new X509Certificate(AWS_CA_CERT);
396
+ for (let i = 0; i < attDoc.cabundle.length; ++i) {
397
+ const cert = new X509Certificate(attDoc.cabundle[i]);
398
+ if (!(await cert.verify(parent))) {
399
+ throw new Error(`Attestation certificate chain failed at index ${i}`);
400
+ }
401
+ parent = cert;
402
+ }
403
+ const cert = new X509Certificate(attDoc.certificate);
404
+ if (!(await cert.verify(parent))) {
405
+ throw new Error("Attestation certificate chain failed at leaf");
406
+ }
407
+ const pubkey = cert.publicKey;
408
+
409
+ // make sure that we got the expected public key type
410
+ const alg = new AlgorithmProvider().toAsnAlgorithm(pubkey.algorithm);
411
+ if (alg.algorithm != EC_PUBLIC_KEY) {
412
+ // not the expected algorithm, i.e., elliptic curve signing
413
+ throw new Error("Attestation contained unexpected signature algorithm");
414
+ }
415
+ const params = AsnParser.parse(alg.parameters!, ECParameters);
416
+ if (!params.namedCurve || params.namedCurve !== NIST_P384) {
417
+ // not the expected params, i.e., NIST P384
418
+ throw new Error("Attestation contained unexpected signature algorithm");
419
+ }
420
+
421
+ // verify the cose signature with the key, which we verified against
422
+ // the AWS Nitro CA certificate above
423
+ await att.verify(await pubkey.export());
424
+
425
+ return attDoc.public_key;
426
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { KeyImporter } from "./import";
2
+ export { MnemonicToImport } from "./mnemonic";