@ledgerhq/revault-sdk 0.28.11 → 0.28.13

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 (47) hide show
  1. package/lib/bake.cjs +3 -3
  2. package/lib/bake.cjs.map +1 -1
  3. package/lib/bake.d.cts +2 -2
  4. package/lib/bake.d.ts +2 -2
  5. package/lib/bake.js +3 -3
  6. package/lib/bake.js.map +1 -1
  7. package/lib/{crypto-BTN8FS8v.cjs → crypto-CKLQSwr3.cjs} +48 -12
  8. package/lib/crypto-CKLQSwr3.cjs.map +1 -0
  9. package/lib/{crypto-BcqzGi2f.js → crypto-hrnmToQa.js} +48 -11
  10. package/lib/crypto-hrnmToQa.js.map +1 -0
  11. package/lib/crypto.cjs +1 -1
  12. package/lib/crypto.js +1 -1
  13. package/lib/{device-DQyYUWPl.js → device-BnO26Hlr.js} +90 -34
  14. package/lib/device-BnO26Hlr.js.map +1 -0
  15. package/lib/{device-DQ32fSI-.cjs → device-CvgFoib2.cjs} +113 -33
  16. package/lib/device-CvgFoib2.cjs.map +1 -0
  17. package/lib/device.cjs +3 -1
  18. package/lib/device.d.cts +2 -2
  19. package/lib/device.d.ts +2 -2
  20. package/lib/device.js +2 -2
  21. package/lib/engine.cjs +93 -53
  22. package/lib/engine.cjs.map +1 -1
  23. package/lib/engine.d.cts +2 -2
  24. package/lib/engine.d.ts +2 -2
  25. package/lib/engine.js +94 -54
  26. package/lib/engine.js.map +1 -1
  27. package/lib/{index-xlQiIxlX.d.ts → index-16zV3onR.d.ts} +1 -1
  28. package/lib/{index-B3IUJM2r.d.cts → index-BAb2WCcL.d.cts} +25 -14
  29. package/lib/{index-Doiqe3tk.d.cts → index-CliA8Mh-.d.cts} +1 -1
  30. package/lib/{index-B1WQdM71.d.ts → index-DQgupTPR.d.ts} +25 -14
  31. package/lib/index.cjs +1 -1
  32. package/lib/index.cjs.map +1 -1
  33. package/lib/index.js +1 -1
  34. package/lib/index.js.map +1 -1
  35. package/lib/onboarding.cjs +6 -2
  36. package/lib/onboarding.cjs.map +1 -1
  37. package/lib/onboarding.d.cts +2 -2
  38. package/lib/onboarding.d.ts +2 -2
  39. package/lib/onboarding.js +6 -2
  40. package/lib/onboarding.js.map +1 -1
  41. package/lib/{types-DzZQZPXO.d.cts → types-bfqkmpgo.d.cts} +2 -1
  42. package/lib/{types-DqjcDy4H.d.ts → types-tRODcWIL.d.ts} +2 -1
  43. package/package.json +4 -5
  44. package/lib/crypto-BTN8FS8v.cjs.map +0 -1
  45. package/lib/crypto-BcqzGi2f.js.map +0 -1
  46. package/lib/device-DQ32fSI-.cjs.map +0 -1
  47. package/lib/device-DQyYUWPl.js.map +0 -1
@@ -3,10 +3,13 @@ let _noble_hashes_sha2_js = require("@noble/hashes/sha2.js");
3
3
  let _scure_bip32 = require("@scure/bip32");
4
4
  let _scure_bip39 = require("@scure/bip39");
5
5
  let _scure_bip39_wordlists_english_js = require("@scure/bip39/wordlists/english.js");
6
- let elliptic = require("elliptic");
7
- elliptic = require_rolldown_runtime.__toESM(elliptic, 1);
6
+ let _noble_curves_nist_js = require("@noble/curves/nist.js");
7
+ let _noble_curves_utils_js = require("@noble/curves/utils.js");
8
8
  let jose = require("jose");
9
9
  jose = require_rolldown_runtime.__toESM(jose, 1);
10
+ let _noble_hashes_hmac_js = require("@noble/hashes/hmac.js");
11
+ let _noble_hashes_utils_js = require("@noble/hashes/utils.js");
12
+ let _noble_curves_secp256k1_js = require("@noble/curves/secp256k1.js");
10
13
  let asn1_js = require("asn1.js");
11
14
  asn1_js = require_rolldown_runtime.__toESM(asn1_js, 1);
12
15
  let bn_js = require("bn.js");
@@ -45,6 +48,40 @@ const getMasterSeed = async (salt) => {
45
48
  return masterSeed;
46
49
  };
47
50
  //#endregion
51
+ //#region src/crypto/deterministic-key.ts
52
+ const CURVE_ORDER = _noble_curves_nist_js.p256.Point.Fn.ORDER;
53
+ const hmacSha256 = (key, ...messages) => (0, _noble_hashes_hmac_js.hmac)(_noble_hashes_sha2_js.sha256, key, (0, _noble_curves_utils_js.concatBytes)(...messages));
54
+ const drbg = (0, _noble_curves_utils_js.createHmacDrbg)(32, 32, hmacSha256);
55
+ /**
56
+ * Derive a secp256r1 secret key from `entropy`, as a hex string.
57
+ *
58
+ * This reproduces byte-for-byte what `elliptic`'s `ec.genKeyPair({ entropy })` used to return,
59
+ * down to the unpadded hex encoding of `BN.toString(16, 2)`. The derivation is load-bearing:
60
+ * API users already provisioned in HSMs and baked workspaces are identified by keys derived
61
+ * from it, so any change here would orphan them. Pinned by golden vectors in
62
+ * `__tests__/crypto/utils.test.ts`.
63
+ */
64
+ const deriveDeterministicSecretHex = (entropy) => {
65
+ const seed = (0, _noble_curves_utils_js.concatBytes)((0, _noble_hashes_sha2_js.sha256)((0, _noble_hashes_utils_js.utf8ToBytes)(entropy)), (0, _noble_curves_utils_js.numberToBytesBE)(CURVE_ORDER, 32));
66
+ const hex = drbg(seed, (bytes) => {
67
+ const candidate = (0, _noble_curves_utils_js.bytesToNumberBE)(bytes);
68
+ /* v8 ignore next -- @preserve: rejection sampling, unreachable in practice */
69
+ if (candidate > CURVE_ORDER - 2n) return void 0;
70
+ return candidate + 1n;
71
+ }).toString(16);
72
+ return hex.length % 2 === 0 ? hex : `0${hex}`;
73
+ };
74
+ /**
75
+ * Pad a secret key hex to the 32 bytes the curve API requires.
76
+ *
77
+ * Rejects anything that is not hex. `elliptic` used to accept such input and silently derive a
78
+ * public key from garbage.
79
+ */
80
+ const secretHexToBytes = (hex) => {
81
+ if (!/^[0-9a-fA-F]{1,64}$/.test(hex)) throw new Error("private key must be a hex string of at most 32 bytes");
82
+ return (0, _noble_curves_utils_js.numberToBytesBE)(BigInt(`0x${hex}`), 32);
83
+ };
84
+ //#endregion
48
85
  //#region src/crypto/key-encoder.ts
49
86
  const ECPrivateKeyASN = asn1_js.default.define("ECPrivateKey", function() {
50
87
  const self = this;
@@ -73,7 +110,7 @@ const curves = {
73
110
  ],
74
111
  privatePEMOptions: { label: "EC PRIVATE KEY" },
75
112
  publicPEMOptions: { label: "PUBLIC KEY" },
76
- curve: new elliptic.default.ec("secp256k1")
113
+ curve: _noble_curves_secp256k1_js.secp256k1
77
114
  },
78
115
  p256: {
79
116
  curveParameters: [
@@ -87,7 +124,7 @@ const curves = {
87
124
  ],
88
125
  privatePEMOptions: { label: "EC PRIVATE KEY" },
89
126
  publicPEMOptions: { label: "PUBLIC KEY" },
90
- curve: new elliptic.default.ec("p256")
127
+ curve: _noble_curves_nist_js.p256
91
128
  }
92
129
  };
93
130
  var KeyEncoder = class {
@@ -151,7 +188,7 @@ var KeyEncoder = class {
151
188
  switch (originalFormat) {
152
189
  case "raw": {
153
190
  if (typeof privateKey !== "string") throw new Error("private key must be a string");
154
- const rawPublicKey = this.options.curve.keyFromPrivate(privateKey, "hex").getPublic("hex");
191
+ const rawPublicKey = (0, _noble_curves_utils_js.bytesToHex)(this.options.curve.getPublicKey(secretHexToBytes(privateKey), false));
155
192
  privateKeyObject = this.privateKeyObject(privateKey, rawPublicKey);
156
193
  break;
157
194
  }
@@ -235,13 +272,12 @@ function hexToPem(hexString, { isPrivate = true } = {}) {
235
272
  */
236
273
  function genKeys(entropy) {
237
274
  if (entropy) {
238
- const ec = new elliptic.default.ec("p256");
239
- const hashedEntropy = node_crypto.default.createHash("sha256").update(entropy).digest("hex");
240
- const keyPair = ec.genKeyPair({ entropy: Buffer.from(hashedEntropy, "hex") });
275
+ const secretHex = deriveDeterministicSecretHex(entropy);
276
+ const publicHex = (0, _noble_curves_utils_js.bytesToHex)(_noble_curves_nist_js.p256.getPublicKey(secretHexToBytes(secretHex), false));
241
277
  return {
242
- privateKey: String(hexToPem(keyPair.getPrivate("hex"))),
243
- publicKey: String(hexToPem(keyPair.getPublic("hex"), { isPrivate: false })),
244
- hexPubKey: extractPublicKey(String(hexToPem(keyPair.getPublic("hex"), { isPrivate: false })))
278
+ privateKey: String(hexToPem(secretHex)),
279
+ publicKey: String(hexToPem(publicHex, { isPrivate: false })),
280
+ hexPubKey: extractPublicKey(String(hexToPem(publicHex, { isPrivate: false })))
245
281
  };
246
282
  }
247
283
  const { publicKey, privateKey } = node_crypto.default.generateKeyPairSync("ec", {
@@ -295,4 +331,4 @@ Object.defineProperty(exports, "sign", {
295
331
  }
296
332
  });
297
333
 
298
- //# sourceMappingURL=crypto-BTN8FS8v.cjs.map
334
+ //# sourceMappingURL=crypto-CKLQSwr3.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto-CKLQSwr3.cjs","names":["sha256","entropyToMnemonic","wordlist","mnemonicToSeed","HDKey","p256","hmac","sha256","concatBytes","createHmacDrbg","utf8ToBytes","numberToBytesBE","bytesToNumberBE","asn1","secp256k1","p256","BN","bytesToHex","bytesToHex","p256","crypto"],"sources":["../src/crypto/gen-seed.ts","../src/crypto/deterministic-key.ts","../src/crypto/key-encoder.ts","../src/crypto/utils.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { HDKey } from \"@scure/bip32\";\nimport { entropyToMnemonic, mnemonicToSeed } from \"@scure/bip39\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\n\n/**\n * Generate a deterministic BIP39 mnemonic from a salt and device index.\n *\n * Produces `entropyToMnemonic(SHA256(salt + \"_\" + index))`, matching\n * the behavior of `@ledgerhq/vault-common`'s `genSeed`.\n *\n * @param salt The workspace salt\n * @param index The device index (1-based)\n * @returns A 24-word BIP39 mnemonic string\n */\nexport function genSeed(salt: string, index: number): string {\n const input = new TextEncoder().encode(`${salt}_${index}`);\n const entropy = sha256(input);\n return entropyToMnemonic(entropy, wordlist);\n}\n\nconst getSeedFragment = async (salt: string, index: number): Promise<Uint8Array> => {\n const seed = await mnemonicToSeed(genSeed(salt, index));\n const secpKey = HDKey.fromMasterSeed(seed);\n const fragment = secpKey.derive(\"m/5655636'/4932953'\");\n return Buffer.concat([fragment.privateKey!, fragment.chainCode!]);\n};\n\nexport const getMasterSeed = async (salt: string): Promise<Uint8Array> => {\n const fragments = await Promise.all([\n getSeedFragment(salt, 7),\n getSeedFragment(salt, 8),\n getSeedFragment(salt, 9),\n ]);\n\n const masterSeed = new Uint8Array(fragments[0].length);\n for (let i = 0; i < fragments[0].length; i++) {\n masterSeed[i] = fragments[0][i]! ^ fragments[1][i]! ^ fragments[2][i]!;\n }\n\n return masterSeed;\n};\n","import { p256 } from \"@noble/curves/nist.js\";\nimport {\n bytesToNumberBE,\n concatBytes,\n createHmacDrbg,\n numberToBytesBE,\n} from \"@noble/curves/utils.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { utf8ToBytes } from \"@noble/hashes/utils.js\";\n\nconst CURVE_ORDER = p256.Point.Fn.ORDER;\n\nconst hmacSha256 = (key: Uint8Array, ...messages: Uint8Array[]): Uint8Array =>\n hmac(sha256, key, concatBytes(...messages));\n\nconst drbg = createHmacDrbg<bigint>(32, 32, hmacSha256);\n\n/**\n * Derive a secp256r1 secret key from `entropy`, as a hex string.\n *\n * This reproduces byte-for-byte what `elliptic`'s `ec.genKeyPair({ entropy })` used to return,\n * down to the unpadded hex encoding of `BN.toString(16, 2)`. The derivation is load-bearing:\n * API users already provisioned in HSMs and baked workspaces are identified by keys derived\n * from it, so any change here would orphan them. Pinned by golden vectors in\n * `__tests__/crypto/utils.test.ts`.\n */\nexport const deriveDeterministicSecretHex = (entropy: string): string => {\n const seed = concatBytes(sha256(utf8ToBytes(entropy)), numberToBytesBE(CURVE_ORDER, 32));\n const secret = drbg(seed, (bytes) => {\n const candidate = bytesToNumberBE(bytes);\n /* v8 ignore next -- @preserve: rejection sampling, unreachable in practice */\n if (candidate > CURVE_ORDER - 2n) return undefined;\n return candidate + 1n;\n });\n const hex = secret.toString(16);\n return hex.length % 2 === 0 ? hex : `0${hex}`;\n};\n\n/**\n * Pad a secret key hex to the 32 bytes the curve API requires.\n *\n * Rejects anything that is not hex. `elliptic` used to accept such input and silently derive a\n * public key from garbage.\n */\nexport const secretHexToBytes = (hex: string): Uint8Array => {\n if (!/^[0-9a-fA-F]{1,64}$/.test(hex)) {\n throw new Error(\"private key must be a hex string of at most 32 bytes\");\n }\n return numberToBytesBE(BigInt(`0x${hex}`), 32);\n};\n","// ========================================================================== //\n// //\n// DISCLAIMER //\n// //\n// This code has been blindly copy-pasted from: //\n// https://github.com/LedgerHQ/pkey-ts //\n// //\n// ========================================================================== //\nimport { p256 } from \"@noble/curves/nist.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { bytesToHex } from \"@noble/curves/utils.js\";\n// @ts-expect-error this library does not ship types\nimport asn1 from \"asn1.js\";\nimport BN from \"bn.js\";\n\nimport { secretHexToBytes } from \"./deterministic-key\";\n\n/* cspell:disable */\nconst ECPrivateKeyASN = asn1.define(\"ECPrivateKey\", function () {\n // @ts-expect-error pure black box\n // biome-ignore lint/suspicious/noExplicitAny: black box\n const self = this as any;\n self\n .seq()\n .obj(\n self.key(\"version\").int(),\n self.key(\"privateKey\").octstr(),\n self.key(\"parameters\").explicit(0).objid().optional(),\n self.key(\"publicKey\").explicit(1).bitstr().optional(),\n );\n});\n\nconst ECPrivateKey8ASN = asn1.define(\n \"ECPrivateKey\",\n /* v8 ignore next -- @preserve */\n function () {\n // @ts-expect-error pure black box\n // biome-ignore lint/suspicious/noExplicitAny: black box\n const self = this as any;\n self\n .seq()\n .obj(\n self.key(\"version\").int(),\n self\n .key(\"privateKeyAlgorithm\")\n .seq()\n .obj(self.key(\"ecPublicKey\").objid(), self.key(\"curve\").objid()),\n self.key(\"privateKey\").octstr().contains(ECPrivateKeyASN),\n self.key(\"attributes\").explicit(0).bitstr().optional(),\n );\n },\n);\n\nconst SubjectPublicKeyInfoASN = asn1.define(\"SubjectPublicKeyInfo\", function () {\n // @ts-expect-error pure black box\n // biome-ignore lint/suspicious/noExplicitAny: black box\n const self = this as any;\n self\n .seq()\n .obj(\n self.key(\"algorithm\").seq().obj(self.key(\"id\").objid(), self.key(\"curve\").objid()),\n self.key(\"pub\").bitstr(),\n );\n});\n\ntype CurveOptions = {\n curveParameters: number[];\n privatePEMOptions: { label: string };\n publicPEMOptions: { label: string };\n curve: { getPublicKey: (secretKey: Uint8Array, isCompressed?: boolean) => Uint8Array };\n};\n\nconst curves: Record<string, CurveOptions> = {\n secp256k1: {\n curveParameters: [1, 3, 132, 0, 10],\n privatePEMOptions: { label: \"EC PRIVATE KEY\" },\n publicPEMOptions: { label: \"PUBLIC KEY\" },\n curve: secp256k1,\n },\n p256: {\n curveParameters: [1, 2, 840, 10045, 3, 1, 7], // OID for p256 curve\n privatePEMOptions: { label: \"EC PRIVATE KEY\" },\n publicPEMOptions: { label: \"PUBLIC KEY\" },\n curve: p256,\n },\n};\n\ntype PrivateKeyPKCS1 = {\n version: BN;\n privateKey: Buffer;\n parameters: number[];\n publicKey?: {\n unused: number;\n data: Buffer;\n };\n};\n\ntype PrivateKeyPKCS8 = {\n version: BN;\n privateKey: PrivateKeyPKCS1;\n privateKeyAlgorithm: { ecPublicKey: number[]; curve: number[] };\n};\n\nexport type KeyFormat = \"raw\" | \"pem\" | \"der\";\n\nexport class KeyEncoder {\n algorithmID: number[];\n options: CurveOptions;\n\n constructor(options: string | CurveOptions) {\n /* v8 ignore else -- @preserve */\n if (typeof options === \"string\") {\n /* v8 ignore if -- @preserve */\n if (options !== \"secp256k1\" && options !== \"p256\") {\n throw new Error(`Unknown curve ${options}`);\n }\n options = curves[options]!;\n }\n this.options = options;\n this.algorithmID = [1, 2, 840, 10045, 2, 1];\n }\n\n /* v8 ignore next -- @preserve */\n private PKCS1toPKCS8(privateKey: PrivateKeyPKCS1): PrivateKeyPKCS8 {\n return {\n version: new BN(0),\n privateKey,\n privateKeyAlgorithm: {\n ecPublicKey: this.algorithmID,\n curve: privateKey.parameters,\n },\n };\n }\n\n privateKeyObject(rawPrivateKey: string, rawPublicKey: string): PrivateKeyPKCS1 {\n const privateKeyObject: PrivateKeyPKCS1 = {\n version: new BN(1),\n privateKey: Buffer.from(rawPrivateKey, \"hex\"),\n parameters: this.options.curveParameters,\n };\n\n /* v8 ignore else -- @preserve */\n if (rawPublicKey) {\n privateKeyObject.publicKey = {\n unused: 0,\n data: Buffer.from(rawPublicKey, \"hex\"),\n };\n }\n\n return privateKeyObject;\n }\n\n publicKeyObject(rawPublicKey: string): {\n algorithm: { id: number[]; curve: number[] };\n pub: { unused: number; data: Buffer };\n } {\n return {\n algorithm: {\n id: this.algorithmID,\n curve: this.options.curveParameters,\n },\n pub: {\n unused: 0,\n data: Buffer.from(rawPublicKey, \"hex\"),\n },\n };\n }\n\n encodePrivate(\n privateKey: string | Buffer,\n originalFormat: KeyFormat,\n destinationFormat: KeyFormat,\n destinationFormatType: \"pkcs8\" | \"pkcs1\" = \"pkcs1\",\n ): string {\n let privateKeyObject: PrivateKeyPKCS1;\n\n /* Parse the incoming private key and convert it to a private key object */\n switch (originalFormat) {\n case \"raw\": {\n if (typeof privateKey !== \"string\") {\n throw new Error(\"private key must be a string\");\n }\n const rawPublicKey = bytesToHex(\n this.options.curve.getPublicKey(secretHexToBytes(privateKey), false),\n );\n privateKeyObject = this.privateKeyObject(privateKey, rawPublicKey);\n break;\n }\n /* v8 ignore next -- @preserve */\n case \"der\":\n if (typeof privateKey === \"string\") {\n privateKey = Buffer.from(privateKey, \"hex\");\n }\n privateKeyObject = ECPrivateKeyASN.decode(privateKey, \"der\");\n break;\n case \"pem\":\n if (typeof privateKey !== \"string\") {\n throw new Error(\"private key must be a string\");\n }\n privateKeyObject = ECPrivateKeyASN.decode(\n privateKey,\n \"pem\",\n this.options.privatePEMOptions,\n );\n break;\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid private key format\");\n }\n\n /* Export the private key object to the desired format */\n switch (destinationFormat) {\n case \"raw\":\n if (destinationFormatType === \"pkcs1\") {\n return privateKeyObject.privateKey.toString(\"hex\");\n } else if (destinationFormatType === \"pkcs8\") {\n return this.PKCS1toPKCS8(privateKeyObject).privateKey.privateKey.toString(\"hex\");\n }\n break;\n case \"der\":\n if (destinationFormatType === \"pkcs1\") {\n return ECPrivateKeyASN.encode(privateKeyObject, \"der\").toString(\"hex\");\n } else if (destinationFormatType === \"pkcs8\") {\n return ECPrivateKey8ASN.encode(this.PKCS1toPKCS8(privateKeyObject), \"der\").toString(\n \"hex\",\n );\n }\n break;\n case \"pem\":\n if (destinationFormatType === \"pkcs1\") {\n return ECPrivateKeyASN.encode(privateKeyObject, \"pem\", this.options.privatePEMOptions);\n } else if (destinationFormatType === \"pkcs8\") {\n return ECPrivateKey8ASN.encode(this.PKCS1toPKCS8(privateKeyObject), \"pem\", {\n ...this.options.privatePEMOptions,\n label: \"PRIVATE KEY\",\n });\n }\n break;\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid destination format for private key\");\n }\n\n throw new Error(\"unsupported destination format or type\");\n }\n\n encodePublic(\n publicKey: string | Buffer,\n originalFormat: KeyFormat,\n destinationFormat: KeyFormat,\n ): string {\n // biome-ignore lint/suspicious/noImplicitAnyLet: melting pot object\n let publicKeyObject;\n\n switch (originalFormat) {\n case \"raw\":\n if (typeof publicKey !== \"string\") {\n throw new Error(\"public key must be a string\");\n }\n publicKeyObject = this.publicKeyObject(publicKey);\n break;\n /* v8 ignore next -- @preserve */\n case \"der\":\n if (typeof publicKey === \"string\") {\n publicKey = Buffer.from(publicKey, \"hex\");\n }\n publicKeyObject = SubjectPublicKeyInfoASN.decode(publicKey, \"der\");\n break;\n case \"pem\":\n if (typeof publicKey !== \"string\") {\n throw new Error(\"public key must be a string\");\n }\n publicKeyObject = SubjectPublicKeyInfoASN.decode(\n publicKey,\n \"pem\",\n this.options.publicPEMOptions,\n );\n break;\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid public key format\");\n }\n\n /* Export the private key object to the desired format */\n switch (destinationFormat) {\n case \"raw\":\n return publicKeyObject.pub.data.toString(\"hex\");\n case \"der\":\n return SubjectPublicKeyInfoASN.encode(publicKeyObject, \"der\").toString(\"hex\");\n case \"pem\":\n return SubjectPublicKeyInfoASN.encode(\n publicKeyObject,\n \"pem\",\n this.options.publicPEMOptions,\n );\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid destination format for public key\");\n }\n }\n}\n/* cspell:enable */\n","import { p256 } from \"@noble/curves/nist.js\";\nimport { bytesToHex } from \"@noble/curves/utils.js\";\nimport * as jose from \"jose\";\n\nimport { deriveDeterministicSecretHex, secretHexToBytes } from \"./deterministic-key\";\nimport { KeyEncoder } from \"./key-encoder\";\nimport type { GeneratedKeys } from \"./types\";\nimport crypto from \"node:crypto\";\n\nfunction extractPublicKey(pemKey: string): string {\n // convert the PEM formatted public key to raw public key in X9.62 uncompressed format\n // this is an oversimplified method and may not work with different curves or PEM formats\n const pem = pemKey.split(\"\\n\");\n pem.shift(); // remove the first line (BEGIN PUBLIC KEY)\n pem.pop(); // remove the last line (END PUBLIC KEY)\n // convert base64 to buffer\n const keyBuffer = Buffer.from(pem.join(\"\"), \"base64\");\n // assuming the first 26 bytes are the header for the SPKI formatted key.\n // the actual key should begin after this header.\n const actualKeyBuffer = keyBuffer.slice(26);\n return actualKeyBuffer.toString(\"hex\").toUpperCase();\n}\n\nfunction hexToPem(hexString: string, { isPrivate = true } = {}): string {\n const keyEncoder = new KeyEncoder(\"p256\");\n if (isPrivate) {\n return keyEncoder.encodePrivate(hexString, \"raw\", \"pem\");\n }\n return keyEncoder.encodePublic(hexString, \"raw\", \"pem\");\n}\n\n/**\n * If `entropy` is passed, will generate keys pair deterministically\n * else, random keys pair generated\n */\nexport function genKeys(entropy?: string): GeneratedKeys {\n if (entropy) {\n const secretHex = deriveDeterministicSecretHex(entropy);\n const publicHex = bytesToHex(p256.getPublicKey(secretHexToBytes(secretHex), false));\n return {\n privateKey: String(hexToPem(secretHex)),\n publicKey: String(hexToPem(publicHex, { isPrivate: false })),\n hexPubKey: extractPublicKey(String(hexToPem(publicHex, { isPrivate: false }))),\n };\n }\n const { publicKey, privateKey } = crypto.generateKeyPairSync(\"ec\", {\n namedCurve: \"P-256\",\n publicKeyEncoding: { type: \"spki\", format: \"pem\" },\n privateKeyEncoding: { type: \"sec1\", format: \"pem\" },\n });\n const hexPubKey = extractPublicKey(publicKey);\n return {\n privateKey,\n publicKey,\n hexPubKey,\n };\n}\n\nexport const sign = async (privateKeyPem: string, base64Challenge: string): Promise<string> => {\n // Decode base64url challenge to bytes\n const challengeDataBytes = jose.base64url.decode(base64Challenge);\n\n // For EC private key format, we need to use a different import method\n // Since jose doesn't support SEC1 format directly, let's use Node.js crypto\n // Create a KeyObject from the EC private key\n const privateKey = crypto.createPrivateKey(privateKeyPem);\n\n // Use jose with the KeyObject\n return new jose.CompactSign(challengeDataBytes)\n .setProtectedHeader({ alg: \"ES256\", typ: \"JWT\" })\n .sign(privateKey);\n};\n"],"mappings":"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"}
@@ -2,8 +2,12 @@ import { sha256 } from "@noble/hashes/sha2.js";
2
2
  import { HDKey } from "@scure/bip32";
3
3
  import { entropyToMnemonic, mnemonicToSeed } from "@scure/bip39";
4
4
  import { wordlist } from "@scure/bip39/wordlists/english.js";
5
- import elliptic from "elliptic";
5
+ import { p256 } from "@noble/curves/nist.js";
6
+ import { bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, numberToBytesBE } from "@noble/curves/utils.js";
6
7
  import * as jose from "jose";
8
+ import { hmac } from "@noble/hashes/hmac.js";
9
+ import { utf8ToBytes } from "@noble/hashes/utils.js";
10
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
7
11
  import asn1 from "asn1.js";
8
12
  import BN from "bn.js";
9
13
  import crypto from "node:crypto";
@@ -39,6 +43,40 @@ const getMasterSeed = async (salt) => {
39
43
  return masterSeed;
40
44
  };
41
45
  //#endregion
46
+ //#region src/crypto/deterministic-key.ts
47
+ const CURVE_ORDER = p256.Point.Fn.ORDER;
48
+ const hmacSha256 = (key, ...messages) => hmac(sha256, key, concatBytes(...messages));
49
+ const drbg = createHmacDrbg(32, 32, hmacSha256);
50
+ /**
51
+ * Derive a secp256r1 secret key from `entropy`, as a hex string.
52
+ *
53
+ * This reproduces byte-for-byte what `elliptic`'s `ec.genKeyPair({ entropy })` used to return,
54
+ * down to the unpadded hex encoding of `BN.toString(16, 2)`. The derivation is load-bearing:
55
+ * API users already provisioned in HSMs and baked workspaces are identified by keys derived
56
+ * from it, so any change here would orphan them. Pinned by golden vectors in
57
+ * `__tests__/crypto/utils.test.ts`.
58
+ */
59
+ const deriveDeterministicSecretHex = (entropy) => {
60
+ const seed = concatBytes(sha256(utf8ToBytes(entropy)), numberToBytesBE(CURVE_ORDER, 32));
61
+ const hex = drbg(seed, (bytes) => {
62
+ const candidate = bytesToNumberBE(bytes);
63
+ /* v8 ignore next -- @preserve: rejection sampling, unreachable in practice */
64
+ if (candidate > CURVE_ORDER - 2n) return void 0;
65
+ return candidate + 1n;
66
+ }).toString(16);
67
+ return hex.length % 2 === 0 ? hex : `0${hex}`;
68
+ };
69
+ /**
70
+ * Pad a secret key hex to the 32 bytes the curve API requires.
71
+ *
72
+ * Rejects anything that is not hex. `elliptic` used to accept such input and silently derive a
73
+ * public key from garbage.
74
+ */
75
+ const secretHexToBytes = (hex) => {
76
+ if (!/^[0-9a-fA-F]{1,64}$/.test(hex)) throw new Error("private key must be a hex string of at most 32 bytes");
77
+ return numberToBytesBE(BigInt(`0x${hex}`), 32);
78
+ };
79
+ //#endregion
42
80
  //#region src/crypto/key-encoder.ts
43
81
  const ECPrivateKeyASN = asn1.define("ECPrivateKey", function() {
44
82
  const self = this;
@@ -67,7 +105,7 @@ const curves = {
67
105
  ],
68
106
  privatePEMOptions: { label: "EC PRIVATE KEY" },
69
107
  publicPEMOptions: { label: "PUBLIC KEY" },
70
- curve: new elliptic.ec("secp256k1")
108
+ curve: secp256k1
71
109
  },
72
110
  p256: {
73
111
  curveParameters: [
@@ -81,7 +119,7 @@ const curves = {
81
119
  ],
82
120
  privatePEMOptions: { label: "EC PRIVATE KEY" },
83
121
  publicPEMOptions: { label: "PUBLIC KEY" },
84
- curve: new elliptic.ec("p256")
122
+ curve: p256
85
123
  }
86
124
  };
87
125
  var KeyEncoder = class {
@@ -145,7 +183,7 @@ var KeyEncoder = class {
145
183
  switch (originalFormat) {
146
184
  case "raw": {
147
185
  if (typeof privateKey !== "string") throw new Error("private key must be a string");
148
- const rawPublicKey = this.options.curve.keyFromPrivate(privateKey, "hex").getPublic("hex");
186
+ const rawPublicKey = bytesToHex(this.options.curve.getPublicKey(secretHexToBytes(privateKey), false));
149
187
  privateKeyObject = this.privateKeyObject(privateKey, rawPublicKey);
150
188
  break;
151
189
  }
@@ -229,13 +267,12 @@ function hexToPem(hexString, { isPrivate = true } = {}) {
229
267
  */
230
268
  function genKeys(entropy) {
231
269
  if (entropy) {
232
- const ec = new elliptic.ec("p256");
233
- const hashedEntropy = crypto.createHash("sha256").update(entropy).digest("hex");
234
- const keyPair = ec.genKeyPair({ entropy: Buffer.from(hashedEntropy, "hex") });
270
+ const secretHex = deriveDeterministicSecretHex(entropy);
271
+ const publicHex = bytesToHex(p256.getPublicKey(secretHexToBytes(secretHex), false));
235
272
  return {
236
- privateKey: String(hexToPem(keyPair.getPrivate("hex"))),
237
- publicKey: String(hexToPem(keyPair.getPublic("hex"), { isPrivate: false })),
238
- hexPubKey: extractPublicKey(String(hexToPem(keyPair.getPublic("hex"), { isPrivate: false })))
273
+ privateKey: String(hexToPem(secretHex)),
274
+ publicKey: String(hexToPem(publicHex, { isPrivate: false })),
275
+ hexPubKey: extractPublicKey(String(hexToPem(publicHex, { isPrivate: false })))
239
276
  };
240
277
  }
241
278
  const { publicKey, privateKey } = crypto.generateKeyPairSync("ec", {
@@ -266,4 +303,4 @@ const sign = async (privateKeyPem, base64Challenge) => {
266
303
  //#endregion
267
304
  export { getMasterSeed as i, sign as n, genSeed as r, genKeys as t };
268
305
 
269
- //# sourceMappingURL=crypto-BcqzGi2f.js.map
306
+ //# sourceMappingURL=crypto-hrnmToQa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto-hrnmToQa.js","names":[],"sources":["../src/crypto/gen-seed.ts","../src/crypto/deterministic-key.ts","../src/crypto/key-encoder.ts","../src/crypto/utils.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { HDKey } from \"@scure/bip32\";\nimport { entropyToMnemonic, mnemonicToSeed } from \"@scure/bip39\";\nimport { wordlist } from \"@scure/bip39/wordlists/english.js\";\n\n/**\n * Generate a deterministic BIP39 mnemonic from a salt and device index.\n *\n * Produces `entropyToMnemonic(SHA256(salt + \"_\" + index))`, matching\n * the behavior of `@ledgerhq/vault-common`'s `genSeed`.\n *\n * @param salt The workspace salt\n * @param index The device index (1-based)\n * @returns A 24-word BIP39 mnemonic string\n */\nexport function genSeed(salt: string, index: number): string {\n const input = new TextEncoder().encode(`${salt}_${index}`);\n const entropy = sha256(input);\n return entropyToMnemonic(entropy, wordlist);\n}\n\nconst getSeedFragment = async (salt: string, index: number): Promise<Uint8Array> => {\n const seed = await mnemonicToSeed(genSeed(salt, index));\n const secpKey = HDKey.fromMasterSeed(seed);\n const fragment = secpKey.derive(\"m/5655636'/4932953'\");\n return Buffer.concat([fragment.privateKey!, fragment.chainCode!]);\n};\n\nexport const getMasterSeed = async (salt: string): Promise<Uint8Array> => {\n const fragments = await Promise.all([\n getSeedFragment(salt, 7),\n getSeedFragment(salt, 8),\n getSeedFragment(salt, 9),\n ]);\n\n const masterSeed = new Uint8Array(fragments[0].length);\n for (let i = 0; i < fragments[0].length; i++) {\n masterSeed[i] = fragments[0][i]! ^ fragments[1][i]! ^ fragments[2][i]!;\n }\n\n return masterSeed;\n};\n","import { p256 } from \"@noble/curves/nist.js\";\nimport {\n bytesToNumberBE,\n concatBytes,\n createHmacDrbg,\n numberToBytesBE,\n} from \"@noble/curves/utils.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { utf8ToBytes } from \"@noble/hashes/utils.js\";\n\nconst CURVE_ORDER = p256.Point.Fn.ORDER;\n\nconst hmacSha256 = (key: Uint8Array, ...messages: Uint8Array[]): Uint8Array =>\n hmac(sha256, key, concatBytes(...messages));\n\nconst drbg = createHmacDrbg<bigint>(32, 32, hmacSha256);\n\n/**\n * Derive a secp256r1 secret key from `entropy`, as a hex string.\n *\n * This reproduces byte-for-byte what `elliptic`'s `ec.genKeyPair({ entropy })` used to return,\n * down to the unpadded hex encoding of `BN.toString(16, 2)`. The derivation is load-bearing:\n * API users already provisioned in HSMs and baked workspaces are identified by keys derived\n * from it, so any change here would orphan them. Pinned by golden vectors in\n * `__tests__/crypto/utils.test.ts`.\n */\nexport const deriveDeterministicSecretHex = (entropy: string): string => {\n const seed = concatBytes(sha256(utf8ToBytes(entropy)), numberToBytesBE(CURVE_ORDER, 32));\n const secret = drbg(seed, (bytes) => {\n const candidate = bytesToNumberBE(bytes);\n /* v8 ignore next -- @preserve: rejection sampling, unreachable in practice */\n if (candidate > CURVE_ORDER - 2n) return undefined;\n return candidate + 1n;\n });\n const hex = secret.toString(16);\n return hex.length % 2 === 0 ? hex : `0${hex}`;\n};\n\n/**\n * Pad a secret key hex to the 32 bytes the curve API requires.\n *\n * Rejects anything that is not hex. `elliptic` used to accept such input and silently derive a\n * public key from garbage.\n */\nexport const secretHexToBytes = (hex: string): Uint8Array => {\n if (!/^[0-9a-fA-F]{1,64}$/.test(hex)) {\n throw new Error(\"private key must be a hex string of at most 32 bytes\");\n }\n return numberToBytesBE(BigInt(`0x${hex}`), 32);\n};\n","// ========================================================================== //\n// //\n// DISCLAIMER //\n// //\n// This code has been blindly copy-pasted from: //\n// https://github.com/LedgerHQ/pkey-ts //\n// //\n// ========================================================================== //\nimport { p256 } from \"@noble/curves/nist.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { bytesToHex } from \"@noble/curves/utils.js\";\n// @ts-expect-error this library does not ship types\nimport asn1 from \"asn1.js\";\nimport BN from \"bn.js\";\n\nimport { secretHexToBytes } from \"./deterministic-key\";\n\n/* cspell:disable */\nconst ECPrivateKeyASN = asn1.define(\"ECPrivateKey\", function () {\n // @ts-expect-error pure black box\n // biome-ignore lint/suspicious/noExplicitAny: black box\n const self = this as any;\n self\n .seq()\n .obj(\n self.key(\"version\").int(),\n self.key(\"privateKey\").octstr(),\n self.key(\"parameters\").explicit(0).objid().optional(),\n self.key(\"publicKey\").explicit(1).bitstr().optional(),\n );\n});\n\nconst ECPrivateKey8ASN = asn1.define(\n \"ECPrivateKey\",\n /* v8 ignore next -- @preserve */\n function () {\n // @ts-expect-error pure black box\n // biome-ignore lint/suspicious/noExplicitAny: black box\n const self = this as any;\n self\n .seq()\n .obj(\n self.key(\"version\").int(),\n self\n .key(\"privateKeyAlgorithm\")\n .seq()\n .obj(self.key(\"ecPublicKey\").objid(), self.key(\"curve\").objid()),\n self.key(\"privateKey\").octstr().contains(ECPrivateKeyASN),\n self.key(\"attributes\").explicit(0).bitstr().optional(),\n );\n },\n);\n\nconst SubjectPublicKeyInfoASN = asn1.define(\"SubjectPublicKeyInfo\", function () {\n // @ts-expect-error pure black box\n // biome-ignore lint/suspicious/noExplicitAny: black box\n const self = this as any;\n self\n .seq()\n .obj(\n self.key(\"algorithm\").seq().obj(self.key(\"id\").objid(), self.key(\"curve\").objid()),\n self.key(\"pub\").bitstr(),\n );\n});\n\ntype CurveOptions = {\n curveParameters: number[];\n privatePEMOptions: { label: string };\n publicPEMOptions: { label: string };\n curve: { getPublicKey: (secretKey: Uint8Array, isCompressed?: boolean) => Uint8Array };\n};\n\nconst curves: Record<string, CurveOptions> = {\n secp256k1: {\n curveParameters: [1, 3, 132, 0, 10],\n privatePEMOptions: { label: \"EC PRIVATE KEY\" },\n publicPEMOptions: { label: \"PUBLIC KEY\" },\n curve: secp256k1,\n },\n p256: {\n curveParameters: [1, 2, 840, 10045, 3, 1, 7], // OID for p256 curve\n privatePEMOptions: { label: \"EC PRIVATE KEY\" },\n publicPEMOptions: { label: \"PUBLIC KEY\" },\n curve: p256,\n },\n};\n\ntype PrivateKeyPKCS1 = {\n version: BN;\n privateKey: Buffer;\n parameters: number[];\n publicKey?: {\n unused: number;\n data: Buffer;\n };\n};\n\ntype PrivateKeyPKCS8 = {\n version: BN;\n privateKey: PrivateKeyPKCS1;\n privateKeyAlgorithm: { ecPublicKey: number[]; curve: number[] };\n};\n\nexport type KeyFormat = \"raw\" | \"pem\" | \"der\";\n\nexport class KeyEncoder {\n algorithmID: number[];\n options: CurveOptions;\n\n constructor(options: string | CurveOptions) {\n /* v8 ignore else -- @preserve */\n if (typeof options === \"string\") {\n /* v8 ignore if -- @preserve */\n if (options !== \"secp256k1\" && options !== \"p256\") {\n throw new Error(`Unknown curve ${options}`);\n }\n options = curves[options]!;\n }\n this.options = options;\n this.algorithmID = [1, 2, 840, 10045, 2, 1];\n }\n\n /* v8 ignore next -- @preserve */\n private PKCS1toPKCS8(privateKey: PrivateKeyPKCS1): PrivateKeyPKCS8 {\n return {\n version: new BN(0),\n privateKey,\n privateKeyAlgorithm: {\n ecPublicKey: this.algorithmID,\n curve: privateKey.parameters,\n },\n };\n }\n\n privateKeyObject(rawPrivateKey: string, rawPublicKey: string): PrivateKeyPKCS1 {\n const privateKeyObject: PrivateKeyPKCS1 = {\n version: new BN(1),\n privateKey: Buffer.from(rawPrivateKey, \"hex\"),\n parameters: this.options.curveParameters,\n };\n\n /* v8 ignore else -- @preserve */\n if (rawPublicKey) {\n privateKeyObject.publicKey = {\n unused: 0,\n data: Buffer.from(rawPublicKey, \"hex\"),\n };\n }\n\n return privateKeyObject;\n }\n\n publicKeyObject(rawPublicKey: string): {\n algorithm: { id: number[]; curve: number[] };\n pub: { unused: number; data: Buffer };\n } {\n return {\n algorithm: {\n id: this.algorithmID,\n curve: this.options.curveParameters,\n },\n pub: {\n unused: 0,\n data: Buffer.from(rawPublicKey, \"hex\"),\n },\n };\n }\n\n encodePrivate(\n privateKey: string | Buffer,\n originalFormat: KeyFormat,\n destinationFormat: KeyFormat,\n destinationFormatType: \"pkcs8\" | \"pkcs1\" = \"pkcs1\",\n ): string {\n let privateKeyObject: PrivateKeyPKCS1;\n\n /* Parse the incoming private key and convert it to a private key object */\n switch (originalFormat) {\n case \"raw\": {\n if (typeof privateKey !== \"string\") {\n throw new Error(\"private key must be a string\");\n }\n const rawPublicKey = bytesToHex(\n this.options.curve.getPublicKey(secretHexToBytes(privateKey), false),\n );\n privateKeyObject = this.privateKeyObject(privateKey, rawPublicKey);\n break;\n }\n /* v8 ignore next -- @preserve */\n case \"der\":\n if (typeof privateKey === \"string\") {\n privateKey = Buffer.from(privateKey, \"hex\");\n }\n privateKeyObject = ECPrivateKeyASN.decode(privateKey, \"der\");\n break;\n case \"pem\":\n if (typeof privateKey !== \"string\") {\n throw new Error(\"private key must be a string\");\n }\n privateKeyObject = ECPrivateKeyASN.decode(\n privateKey,\n \"pem\",\n this.options.privatePEMOptions,\n );\n break;\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid private key format\");\n }\n\n /* Export the private key object to the desired format */\n switch (destinationFormat) {\n case \"raw\":\n if (destinationFormatType === \"pkcs1\") {\n return privateKeyObject.privateKey.toString(\"hex\");\n } else if (destinationFormatType === \"pkcs8\") {\n return this.PKCS1toPKCS8(privateKeyObject).privateKey.privateKey.toString(\"hex\");\n }\n break;\n case \"der\":\n if (destinationFormatType === \"pkcs1\") {\n return ECPrivateKeyASN.encode(privateKeyObject, \"der\").toString(\"hex\");\n } else if (destinationFormatType === \"pkcs8\") {\n return ECPrivateKey8ASN.encode(this.PKCS1toPKCS8(privateKeyObject), \"der\").toString(\n \"hex\",\n );\n }\n break;\n case \"pem\":\n if (destinationFormatType === \"pkcs1\") {\n return ECPrivateKeyASN.encode(privateKeyObject, \"pem\", this.options.privatePEMOptions);\n } else if (destinationFormatType === \"pkcs8\") {\n return ECPrivateKey8ASN.encode(this.PKCS1toPKCS8(privateKeyObject), \"pem\", {\n ...this.options.privatePEMOptions,\n label: \"PRIVATE KEY\",\n });\n }\n break;\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid destination format for private key\");\n }\n\n throw new Error(\"unsupported destination format or type\");\n }\n\n encodePublic(\n publicKey: string | Buffer,\n originalFormat: KeyFormat,\n destinationFormat: KeyFormat,\n ): string {\n // biome-ignore lint/suspicious/noImplicitAnyLet: melting pot object\n let publicKeyObject;\n\n switch (originalFormat) {\n case \"raw\":\n if (typeof publicKey !== \"string\") {\n throw new Error(\"public key must be a string\");\n }\n publicKeyObject = this.publicKeyObject(publicKey);\n break;\n /* v8 ignore next -- @preserve */\n case \"der\":\n if (typeof publicKey === \"string\") {\n publicKey = Buffer.from(publicKey, \"hex\");\n }\n publicKeyObject = SubjectPublicKeyInfoASN.decode(publicKey, \"der\");\n break;\n case \"pem\":\n if (typeof publicKey !== \"string\") {\n throw new Error(\"public key must be a string\");\n }\n publicKeyObject = SubjectPublicKeyInfoASN.decode(\n publicKey,\n \"pem\",\n this.options.publicPEMOptions,\n );\n break;\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid public key format\");\n }\n\n /* Export the private key object to the desired format */\n switch (destinationFormat) {\n case \"raw\":\n return publicKeyObject.pub.data.toString(\"hex\");\n case \"der\":\n return SubjectPublicKeyInfoASN.encode(publicKeyObject, \"der\").toString(\"hex\");\n case \"pem\":\n return SubjectPublicKeyInfoASN.encode(\n publicKeyObject,\n \"pem\",\n this.options.publicPEMOptions,\n );\n /* v8 ignore next -- @preserve */\n default:\n throw new Error(\"invalid destination format for public key\");\n }\n }\n}\n/* cspell:enable */\n","import { p256 } from \"@noble/curves/nist.js\";\nimport { bytesToHex } from \"@noble/curves/utils.js\";\nimport * as jose from \"jose\";\n\nimport { deriveDeterministicSecretHex, secretHexToBytes } from \"./deterministic-key\";\nimport { KeyEncoder } from \"./key-encoder\";\nimport type { GeneratedKeys } from \"./types\";\nimport crypto from \"node:crypto\";\n\nfunction extractPublicKey(pemKey: string): string {\n // convert the PEM formatted public key to raw public key in X9.62 uncompressed format\n // this is an oversimplified method and may not work with different curves or PEM formats\n const pem = pemKey.split(\"\\n\");\n pem.shift(); // remove the first line (BEGIN PUBLIC KEY)\n pem.pop(); // remove the last line (END PUBLIC KEY)\n // convert base64 to buffer\n const keyBuffer = Buffer.from(pem.join(\"\"), \"base64\");\n // assuming the first 26 bytes are the header for the SPKI formatted key.\n // the actual key should begin after this header.\n const actualKeyBuffer = keyBuffer.slice(26);\n return actualKeyBuffer.toString(\"hex\").toUpperCase();\n}\n\nfunction hexToPem(hexString: string, { isPrivate = true } = {}): string {\n const keyEncoder = new KeyEncoder(\"p256\");\n if (isPrivate) {\n return keyEncoder.encodePrivate(hexString, \"raw\", \"pem\");\n }\n return keyEncoder.encodePublic(hexString, \"raw\", \"pem\");\n}\n\n/**\n * If `entropy` is passed, will generate keys pair deterministically\n * else, random keys pair generated\n */\nexport function genKeys(entropy?: string): GeneratedKeys {\n if (entropy) {\n const secretHex = deriveDeterministicSecretHex(entropy);\n const publicHex = bytesToHex(p256.getPublicKey(secretHexToBytes(secretHex), false));\n return {\n privateKey: String(hexToPem(secretHex)),\n publicKey: String(hexToPem(publicHex, { isPrivate: false })),\n hexPubKey: extractPublicKey(String(hexToPem(publicHex, { isPrivate: false }))),\n };\n }\n const { publicKey, privateKey } = crypto.generateKeyPairSync(\"ec\", {\n namedCurve: \"P-256\",\n publicKeyEncoding: { type: \"spki\", format: \"pem\" },\n privateKeyEncoding: { type: \"sec1\", format: \"pem\" },\n });\n const hexPubKey = extractPublicKey(publicKey);\n return {\n privateKey,\n publicKey,\n hexPubKey,\n };\n}\n\nexport const sign = async (privateKeyPem: string, base64Challenge: string): Promise<string> => {\n // Decode base64url challenge to bytes\n const challengeDataBytes = jose.base64url.decode(base64Challenge);\n\n // For EC private key format, we need to use a different import method\n // Since jose doesn't support SEC1 format directly, let's use Node.js crypto\n // Create a KeyObject from the EC private key\n const privateKey = crypto.createPrivateKey(privateKeyPem);\n\n // Use jose with the KeyObject\n return new jose.CompactSign(challengeDataBytes)\n .setProtectedHeader({ alg: \"ES256\", typ: \"JWT\" })\n .sign(privateKey);\n};\n"],"mappings":"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"}
package/lib/crypto.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_crypto = require("./crypto-BTN8FS8v.cjs");
2
+ const require_crypto = require("./crypto-CKLQSwr3.cjs");
3
3
  exports.genKeys = require_crypto.genKeys;
4
4
  exports.genSeed = require_crypto.genSeed;
5
5
  exports.getMasterSeed = require_crypto.getMasterSeed;
package/lib/crypto.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as getMasterSeed, n as sign, r as genSeed, t as genKeys } from "./crypto-BcqzGi2f.js";
1
+ import { i as getMasterSeed, n as sign, r as genSeed, t as genKeys } from "./crypto-hrnmToQa.js";
2
2
  export { genKeys, genSeed, getMasterSeed, sign };
@@ -26,6 +26,55 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  enumerable: true
27
27
  }) : target, mod));
28
28
  //#endregion
29
+ //#region src/device/abort.ts
30
+ /** Error returned when the application aborts a device operation. */
31
+ var DeviceOperationAbortedError = class extends Error {
32
+ constructor() {
33
+ super("Device operation aborted");
34
+ this.name = "DeviceOperationAbortedError";
35
+ }
36
+ };
37
+ const isDeviceOperationAbortedError = (error) => error instanceof Error && error.name === "DeviceOperationAbortedError";
38
+ const throwIfDeviceOperationAborted = (signal) => {
39
+ if (signal?.aborted) throw new DeviceOperationAbortedError();
40
+ };
41
+ const withDeviceOperationSignal = (operation, signal) => {
42
+ try {
43
+ throwIfDeviceOperationAborted(signal);
44
+ } catch (error) {
45
+ return Promise.reject(error);
46
+ }
47
+ if (!signal) return Promise.resolve().then(operation);
48
+ return new Promise((resolve, reject) => {
49
+ const onAbort = () => reject(new DeviceOperationAbortedError());
50
+ signal.addEventListener("abort", onAbort, { once: true });
51
+ Promise.resolve().then(() => {
52
+ throwIfDeviceOperationAborted(signal);
53
+ return operation();
54
+ }).then(resolve, (error) => {
55
+ reject(signal.aborted ? new DeviceOperationAbortedError() : error);
56
+ }).finally(() => signal.removeEventListener("abort", onAbort));
57
+ });
58
+ };
59
+ const deviceOperationDelay = (delay, signal) => {
60
+ try {
61
+ throwIfDeviceOperationAborted(signal);
62
+ } catch (error) {
63
+ return Promise.reject(error);
64
+ }
65
+ return new Promise((resolve, reject) => {
66
+ const timeout = setTimeout(() => {
67
+ signal?.removeEventListener("abort", onAbort);
68
+ resolve();
69
+ }, delay);
70
+ const onAbort = () => {
71
+ clearTimeout(timeout);
72
+ reject(new DeviceOperationAbortedError());
73
+ };
74
+ signal?.addEventListener("abort", onAbort, { once: true });
75
+ });
76
+ };
77
+ //#endregion
29
78
  //#region ../../node_modules/.pnpm/axios-retry@4.5.0_axios@1.19.0/node_modules/axios-retry/dist/esm/index.js
30
79
  var import_is_retry_allowed = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
31
80
  const denyList = /* @__PURE__ */ new Set([
@@ -271,6 +320,7 @@ function createApiDevice(options, logger) {
271
320
  if (typeof __seed === "string") return Promise.resolve(__seed);
272
321
  return __seed;
273
322
  };
323
+ const postWithSignal = async (url, data, signal) => withDeviceOperationSignal(() => signal ? deviceApi.post(url, data, { signal }) : deviceApi.post(url, data), signal);
274
324
  const authenticate = async ({ challenge, workspaceName }) => {
275
325
  const role = challenge.role;
276
326
  if (role === "READ_ONLY_API_KEY") throw new Error("Invalid role for authenticate: READ_ONLY_API_KEY");
@@ -313,8 +363,8 @@ function createApiDevice(options, logger) {
313
363
  const res = await deviceApi.post("/generate-key-fragments", payload);
314
364
  return Buffer.from(res.data, "hex").toString("base64");
315
365
  };
316
- const getAttestation = async () => {
317
- const { data: attestationStr } = await deviceApi.post("/get-attestation", { seed: await getSeed() });
366
+ const getAttestation = async (opts = {}) => {
367
+ const { data: attestationStr } = await postWithSignal("/get-attestation", { seed: await getSeed() }, opts.signal);
318
368
  const attestationBuffer = Buffer.from(attestationStr, "hex");
319
369
  const certLen = attestationBuffer.readInt8(98);
320
370
  return {
@@ -326,8 +376,8 @@ function createApiDevice(options, logger) {
326
376
  buffer: attestationBuffer
327
377
  };
328
378
  };
329
- const getConfidentialityKey = async () => {
330
- const res = await deviceApi.post("/get-confidentiality-key", { seed: await getSeed() });
379
+ const getConfidentialityKey = async (opts = {}) => {
380
+ const res = await postWithSignal("/get-confidentiality-key", { seed: await getSeed() }, opts.signal);
331
381
  return {
332
382
  confidentialityKeyCurve25519: res.data.confidentiality_key_curve25519,
333
383
  confidentialityKeySignature: res.data.confidentiality_key_signature
@@ -345,13 +395,13 @@ function createApiDevice(options, logger) {
345
395
  const getValidationPubKey = async () => {
346
396
  return getPublicKey({ path: PATHS.VALIDATION });
347
397
  };
348
- const hasPairing = async () => {
398
+ const hasPairing = async (opts = {}) => {
349
399
  const seed = await getSeed();
350
- if ((await deviceApi.post("/get-partition-ids", { seed })).data.length === 0) return "NO_PAIRING";
351
- return (await deviceApi.post("/partition-psk-status", { seed })).data.psk_missing ? "PSK_MISSING" : "OK";
400
+ if ((await postWithSignal("/get-partition-ids", { seed }, opts.signal)).data.length === 0) return "NO_PAIRING";
401
+ return (await postWithSignal("/partition-psk-status", { seed }, opts.signal)).data.psk_missing ? "PSK_MISSING" : "OK";
352
402
  };
353
- const initPairingHandshake = async () => {
354
- const { data } = await deviceApi.post("/initiate-pairing-handshake", { seed: await getSeed() });
403
+ const initPairingHandshake = async (opts = {}) => {
404
+ const { data } = await postWithSignal("/initiate-pairing-handshake", { seed: await getSeed() }, opts.signal);
355
405
  return {
356
406
  pubKey: data.pubKey,
357
407
  signature: Buffer.from(data.attestation, "hex").toString("base64")
@@ -416,7 +466,7 @@ function createApiDevice(options, logger) {
416
466
  };
417
467
  return (await deviceApi.post("/u2f-register-data", payload)).data;
418
468
  };
419
- const setPairing = async ({ pairing }) => {
469
+ const setPairing = async ({ pairing, signal }) => {
420
470
  const payload = {
421
471
  seed: await getSeed(),
422
472
  blob: pairing.blob,
@@ -432,13 +482,13 @@ function createApiDevice(options, logger) {
432
482
  signature: pairing.partitionKeyAttestation.signature
433
483
  }
434
484
  };
435
- await deviceApi.post("/set-partition-key", payload);
485
+ await postWithSignal("/set-partition-key", payload, signal);
436
486
  };
437
- const signRefreshPskAuth = async ({ ephemeralKey }) => {
438
- const { data } = await deviceApi.post("/sign-refresh-psk-auth", {
487
+ const signRefreshPskAuth = async ({ ephemeralKey, signal }) => {
488
+ const { data } = await postWithSignal("/sign-refresh-psk-auth", {
439
489
  seed: await getSeed(),
440
490
  ephemeral_key: ephemeralKey
441
- });
491
+ }, signal);
442
492
  return Buffer.from(data, "hex").toString("base64");
443
493
  };
444
494
  const finalizePskRefresh = async (params) => {
@@ -447,7 +497,7 @@ function createApiDevice(options, logger) {
447
497
  handshake: Buffer.from(params.handshake, "base64").toString("hex"),
448
498
  ciphertext: Buffer.from(params.encryptedPairingPayload, "base64").toString("hex")
449
499
  };
450
- await deviceApi.post("/finalize-psk-refresh", payload);
500
+ await postWithSignal("/finalize-psk-refresh", payload, params.signal);
451
501
  };
452
502
  const startKKPatternAsResponder = async (params) => {
453
503
  const payload = {
@@ -474,7 +524,7 @@ function createApiDevice(options, logger) {
474
524
  handshake_attestation: "",
475
525
  partition_id: partitionId
476
526
  };
477
- const { data: deviceResponse } = await deviceApi.post("/start-k-pattern-as-responder", payload);
527
+ const { data: deviceResponse } = await postWithSignal("/start-k-pattern-as-responder", payload, params.signal);
478
528
  if (deviceResponse !== "9000") throw new Error(`device-api device failed with code: ${deviceResponse}`);
479
529
  };
480
530
  const startPskSession = async (params) => {
@@ -486,7 +536,7 @@ function createApiDevice(options, logger) {
486
536
  nonce: Buffer.from(nonce, "hex").toString("base64"),
487
537
  partition_id: Buffer.from(partitionId, "hex").toString("base64")
488
538
  };
489
- const { data: deviceResponse } = await deviceApi.post("/start-psk-session", payload);
539
+ const { data: deviceResponse } = await postWithSignal("/start-psk-session", payload, params.signal);
490
540
  if (deviceResponse !== "9000") throw new Error(`device-api device failed with code: ${deviceResponse}`);
491
541
  };
492
542
  const getPublicKey = async (params) => {
@@ -509,22 +559,20 @@ function createApiDevice(options, logger) {
509
559
  path: PATHS.VALIDATION,
510
560
  actions
511
561
  };
512
- const { data: signature } = await deviceApi.post("/validate-op", payload);
562
+ const { data: signature } = await postWithSignal("/validate-op", payload, params.signal);
513
563
  return signature;
514
564
  };
515
565
  const sendAPDU = (_apdu) => {
516
566
  logger.info("Can't send arbitrary APDU to software device");
517
567
  return Promise.resolve("9000");
518
568
  };
519
- const getVersion = async () => {
520
- return "1.2.3";
521
- };
522
- const poll = async () => {
523
- await new Promise((r) => setTimeout(r, 1e3));
569
+ const getVersion = async (opts = {}) => withDeviceOperationSignal(() => Promise.resolve("1.2.3"), opts.signal);
570
+ const poll = async (opts = {}) => {
571
+ await deviceOperationDelay(1e3, opts.signal);
524
572
  return { isAppOpened: true };
525
573
  };
526
- const pollApp = async () => {
527
- await new Promise((r) => setTimeout(r, 1e3));
574
+ const pollApp = async (_name, opts = {}) => {
575
+ await deviceOperationDelay(1e3, opts.signal);
528
576
  return "9000";
529
577
  };
530
578
  return {
@@ -916,7 +964,14 @@ const withDevice = (deviceId) => (job) => new Observable((o) => {
916
964
  deviceQueues[deviceId] = new Promise((resolve) => {
917
965
  finish = resolve;
918
966
  });
919
- deviceQueue.then(() => open(deviceId)).then(async (transport) => {
967
+ deviceQueue.then(() => {
968
+ if (unsubscribed) {
969
+ finish();
970
+ return;
971
+ }
972
+ return open(deviceId);
973
+ }).then(async (transport) => {
974
+ if (!transport) return;
920
975
  if (unsubscribed) return finalize(transport, [finish]);
921
976
  if (transport.requestConnectionPriority && typeof transport.requestConnectionPriority === "function") await transport.requestConnectionPriority("High");
922
977
  return transport;
@@ -957,12 +1012,6 @@ const CANCEL_STATUS_CODES = [
957
1012
  45075,
958
1013
  21761
959
1014
  ];
960
- const isDeviceCancelError = (error) => {
961
- return isTransportStatusError(error) && CANCEL_STATUS_CODES.includes(error.statusCode);
962
- };
963
- const namedErrorPredicate = (name) => (error) => error instanceof Error && error.name === name;
964
- const isCantOpenDeviceError = namedErrorPredicate("CantOpenDevice");
965
- const isLockedDeviceError = namedErrorPredicate("LockedDeviceError");
966
1015
  const isDeviceApiSwError = (error, sw) => {
967
1016
  if (typeof error !== "object" || error === null || !("response" in error)) return false;
968
1017
  const { response } = error;
@@ -972,6 +1021,13 @@ const isDeviceApiSwError = (error, sw) => {
972
1021
  const hasSw = (obj) => typeof obj === "object" && obj !== null && "sw" in obj && obj.sw === sw;
973
1022
  return hasSw(data) || hasSw(data.data);
974
1023
  };
1024
+ const isDeviceCancelError = (error) => {
1025
+ if (isTransportStatusError(error)) return CANCEL_STATUS_CODES.includes(error.statusCode);
1026
+ return CANCEL_STATUS_CODES.some((status) => isDeviceApiSwError(error, status.toString(16)));
1027
+ };
1028
+ const namedErrorPredicate = (name) => (error) => error instanceof Error && error.name === name;
1029
+ const isCantOpenDeviceError = namedErrorPredicate("CantOpenDevice");
1030
+ const isLockedDeviceError = namedErrorPredicate("LockedDeviceError");
975
1031
  const isPskMissingError = (error) => {
976
1032
  return isTransportStatusError(error) && error.statusCode === 24677 || isDeviceApiSwError(error, "6065");
977
1033
  };
@@ -979,6 +1035,6 @@ const isPskMismatchError = (error) => {
979
1035
  return isTransportStatusError(error) && error.statusCode === 24679 || isDeviceApiSwError(error, "6067");
980
1036
  };
981
1037
  //#endregion
982
- export { isPskMissingError as a, withDevicePolling as c, createApiDevice as d, APPID_VAULT_ADMINISTRATOR as f, STR_ROLES as g, SCRIPT_IDS as h, isPskMismatchError as i, DisconnectedDevice as l, PATHS as m, isDeviceCancelError as n, isTransportStatusError as o, PAGINATED_STATUS as p, isLockedDeviceError as r, registerTransportModule as s, isCantOpenDeviceError as t, LockedDeviceError as u };
1038
+ export { DeviceOperationAbortedError as _, isPskMissingError as a, throwIfDeviceOperationAborted as b, withDevicePolling as c, createApiDevice as d, APPID_VAULT_ADMINISTRATOR as f, STR_ROLES as g, SCRIPT_IDS as h, isPskMismatchError as i, DisconnectedDevice as l, PATHS as m, isDeviceCancelError as n, isTransportStatusError as o, PAGINATED_STATUS as p, isLockedDeviceError as r, registerTransportModule as s, isCantOpenDeviceError as t, LockedDeviceError as u, deviceOperationDelay as v, isDeviceOperationAbortedError as y };
983
1039
 
984
- //# sourceMappingURL=device-DQyYUWPl.js.map
1040
+ //# sourceMappingURL=device-BnO26Hlr.js.map