From 33d7d8743647ff42f0dd4fba595a9eba9055f85a Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 8 Jul 2025 21:21:36 +0200 Subject: [PATCH 1/3] Add missing pendingWithoutSimapp()s --- packages/stargate/src/modules/authz/queries.spec.ts | 2 ++ packages/stargate/src/modules/vesting/messages.spec.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/stargate/src/modules/authz/queries.spec.ts b/packages/stargate/src/modules/authz/queries.spec.ts index 316fe886db..3c65ea251a 100644 --- a/packages/stargate/src/modules/authz/queries.spec.ts +++ b/packages/stargate/src/modules/authz/queries.spec.ts @@ -101,6 +101,7 @@ describe("AuthzExtension", () => { describe("granter grants", () => { it("works", async () => { + pendingWithoutSimapp(); const [client, cometClient] = await makeClientWithAuthz(simapp.tendermintUrlHttp); const response = await client.authz.granterGrants(granter1Address); expect(response.grants.length).toBeGreaterThanOrEqual(1); @@ -131,6 +132,7 @@ describe("AuthzExtension", () => { describe("grantee grants", () => { it("works", async () => { + pendingWithoutSimapp(); const [client, cometClient] = await makeClientWithAuthz(simapp.tendermintUrlHttp); const response = await client.authz.granteeGrants(grantee1Address); expect(response.grants.length).toEqual(1); diff --git a/packages/stargate/src/modules/vesting/messages.spec.ts b/packages/stargate/src/modules/vesting/messages.spec.ts index dcf5b0d5a9..41d5f39490 100644 --- a/packages/stargate/src/modules/vesting/messages.spec.ts +++ b/packages/stargate/src/modules/vesting/messages.spec.ts @@ -45,6 +45,7 @@ describe("vesting messages", () => { }); it("works with Amino JSON signer", async () => { + pendingWithoutSimapp(); const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic); const client = await SigningStargateClient.connectWithSigner( simapp.tendermintUrlHttp, From 530f27f6c5e90cca61d6f292fc46518748b44197 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 8 Jul 2025 21:08:20 +0200 Subject: [PATCH 2/3] Migrate from elliptic to @noble/curves --- .pnp.cjs | 113 +++--------------- ...curves-npm-1.9.2-2584df26a7-21d049ae45.zip | 3 + ...bn.js-npm-4.12.0-3ec6c884f6-9736aaa317.zip | 3 - ...rorand-npm-1.1.0-ea86634c4b-6f366d7c49.zip | 3 - ...liptic-npm-6.6.1-87bb857cbc-8b24ef782e.zip | 3 - ...ash.js-npm-1.1.7-f1ad187358-41ada59494.zip | 3 - ...c-drbg-npm-1.0.1-3499ad31cd-f3d9ba31b4.zip | 3 - ...assert-npm-1.0.1-dc8bb23d29-96730e5601.zip | 3 - ...-utils-npm-1.0.1-e66b10822e-790ecec8c5.zip | 3 - packages/crypto/package.json | 2 +- packages/crypto/src/secp256k1.ts | 83 +++++-------- packages/crypto/src/slip10.ts | 16 +-- yarn.lock | 77 ++---------- 13 files changed, 70 insertions(+), 245 deletions(-) create mode 100644 .yarn/cache/@noble-curves-npm-1.9.2-2584df26a7-21d049ae45.zip delete mode 100644 .yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-9736aaa317.zip delete mode 100644 .yarn/cache/brorand-npm-1.1.0-ea86634c4b-6f366d7c49.zip delete mode 100644 .yarn/cache/elliptic-npm-6.6.1-87bb857cbc-8b24ef782e.zip delete mode 100644 .yarn/cache/hash.js-npm-1.1.7-f1ad187358-41ada59494.zip delete mode 100644 .yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-f3d9ba31b4.zip delete mode 100644 .yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-96730e5601.zip delete mode 100644 .yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-790ecec8c5.zip diff --git a/.pnp.cjs b/.pnp.cjs index 21be6fdaae..5023f47599 100644 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -370,6 +370,10 @@ const RAW_RUNTIME_STATE = "@ledgerhq/logs",\ "npm:6.13.0"\ ],\ + [\ + "@noble/curves",\ + "npm:1.9.2"\ + ],\ [\ "@noble/hashes",\ "npm:1.8.0"\ @@ -942,10 +946,6 @@ const RAW_RUNTIME_STATE = "braces",\ "npm:3.0.2"\ ],\ - [\ - "brorand",\ - "npm:1.1.0"\ - ],\ [\ "browserslist",\ "npm:4.22.1"\ @@ -1222,10 +1222,6 @@ const RAW_RUNTIME_STATE = "electron-to-chromium",\ "npm:1.4.565"\ ],\ - [\ - "elliptic",\ - "npm:6.6.1"\ - ],\ [\ "emoji-regex",\ "npm:8.0.0"\ @@ -1634,10 +1630,6 @@ const RAW_RUNTIME_STATE = "hash-base",\ "npm:3.1.0"\ ],\ - [\ - "hash.js",\ - "npm:1.1.7"\ - ],\ [\ "hasha",\ "npm:5.2.2"\ @@ -1646,10 +1638,6 @@ const RAW_RUNTIME_STATE = "hasown",\ "npm:2.0.2"\ ],\ - [\ - "hmac-drbg",\ - "npm:1.0.1"\ - ],\ [\ "html-escaper",\ "npm:2.0.2"\ @@ -2138,14 +2126,6 @@ const RAW_RUNTIME_STATE = "mimic-response",\ "npm:3.1.0"\ ],\ - [\ - "minimalistic-assert",\ - "npm:1.0.1"\ - ],\ - [\ - "minimalistic-crypto-utils",\ - "npm:1.0.1"\ - ],\ [\ "minimatch",\ "npm:3.1.2"\ @@ -3516,6 +3496,7 @@ const RAW_RUNTIME_STATE = ["@cosmjs/math", "workspace:packages/math"],\ ["@cosmjs/utils", "workspace:packages/utils"],\ ["@istanbuljs/nyc-config-typescript", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:1.0.1"],\ + ["@noble/curves", "npm:1.9.2"],\ ["@noble/hashes", "npm:1.8.0"],\ ["@types/bn.js", "npm:5.1.0"],\ ["@types/elliptic", "npm:6.4.14"],\ @@ -3529,7 +3510,6 @@ const RAW_RUNTIME_STATE = ["@typescript-eslint/parser", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:5.54.0"],\ ["bn.js", "npm:5.2.0"],\ ["buffer", "npm:6.0.3"],\ - ["elliptic", "npm:6.6.1"],\ ["eslint", "npm:8.57.1"],\ ["eslint-config-prettier", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.3.0"],\ ["eslint-import-resolver-node", "npm:0.3.9"],\ @@ -4463,6 +4443,16 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["@noble/curves", [\ + ["npm:1.9.2", {\ + "packageLocation": "./.yarn/cache/@noble-curves-npm-1.9.2-2584df26a7-21d049ae45.zip/node_modules/@noble/curves/",\ + "packageDependencies": [\ + ["@noble/curves", "npm:1.9.2"],\ + ["@noble/hashes", "npm:1.8.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@noble/hashes", [\ ["npm:1.8.0", {\ "packageLocation": "./.yarn/cache/@noble-hashes-npm-1.8.0-a397449e64-06a0b52c81.zip/node_modules/@noble/hashes/",\ @@ -6787,13 +6777,6 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["bn.js", [\ - ["npm:4.12.0", {\ - "packageLocation": "./.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-9736aaa317.zip/node_modules/bn.js/",\ - "packageDependencies": [\ - ["bn.js", "npm:4.12.0"]\ - ],\ - "linkType": "HARD"\ - }],\ ["npm:5.2.0", {\ "packageLocation": "./.yarn/cache/bn.js-npm-5.2.0-11748c0b07-67e17b1934.zip/node_modules/bn.js/",\ "packageDependencies": [\ @@ -6852,15 +6835,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["brorand", [\ - ["npm:1.1.0", {\ - "packageLocation": "./.yarn/cache/brorand-npm-1.1.0-ea86634c4b-6f366d7c49.zip/node_modules/brorand/",\ - "packageDependencies": [\ - ["brorand", "npm:1.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["browserslist", [\ ["npm:4.22.1", {\ "packageLocation": "./.yarn/cache/browserslist-npm-4.22.1-cccae6d74e-6810f2d63f.zip/node_modules/browserslist/",\ @@ -7750,22 +7724,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["elliptic", [\ - ["npm:6.6.1", {\ - "packageLocation": "./.yarn/cache/elliptic-npm-6.6.1-87bb857cbc-8b24ef782e.zip/node_modules/elliptic/",\ - "packageDependencies": [\ - ["bn.js", "npm:4.12.0"],\ - ["brorand", "npm:1.1.0"],\ - ["elliptic", "npm:6.6.1"],\ - ["hash.js", "npm:1.1.7"],\ - ["hmac-drbg", "npm:1.0.1"],\ - ["inherits", "npm:2.0.4"],\ - ["minimalistic-assert", "npm:1.0.1"],\ - ["minimalistic-crypto-utils", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["emoji-regex", [\ ["npm:8.0.0", {\ "packageLocation": "./.yarn/cache/emoji-regex-npm-8.0.0-213764015c-b6053ad399.zip/node_modules/emoji-regex/",\ @@ -9133,17 +9091,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["hash.js", [\ - ["npm:1.1.7", {\ - "packageLocation": "./.yarn/cache/hash.js-npm-1.1.7-f1ad187358-41ada59494.zip/node_modules/hash.js/",\ - "packageDependencies": [\ - ["hash.js", "npm:1.1.7"],\ - ["inherits", "npm:2.0.4"],\ - ["minimalistic-assert", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["hasha", [\ ["npm:5.2.2", {\ "packageLocation": "./.yarn/cache/hasha-npm-5.2.2-d171116d12-9d10d4e665.zip/node_modules/hasha/",\ @@ -9165,18 +9112,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["hmac-drbg", [\ - ["npm:1.0.1", {\ - "packageLocation": "./.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-f3d9ba31b4.zip/node_modules/hmac-drbg/",\ - "packageDependencies": [\ - ["hash.js", "npm:1.1.7"],\ - ["hmac-drbg", "npm:1.0.1"],\ - ["minimalistic-assert", "npm:1.0.1"],\ - ["minimalistic-crypto-utils", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["html-escaper", [\ ["npm:2.0.2", {\ "packageLocation": "./.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-208e8a12de.zip/node_modules/html-escaper/",\ @@ -10592,24 +10527,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["minimalistic-assert", [\ - ["npm:1.0.1", {\ - "packageLocation": "./.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-96730e5601.zip/node_modules/minimalistic-assert/",\ - "packageDependencies": [\ - ["minimalistic-assert", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minimalistic-crypto-utils", [\ - ["npm:1.0.1", {\ - "packageLocation": "./.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-790ecec8c5.zip/node_modules/minimalistic-crypto-utils/",\ - "packageDependencies": [\ - ["minimalistic-crypto-utils", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["minimatch", [\ ["npm:3.1.2", {\ "packageLocation": "./.yarn/cache/minimatch-npm-3.1.2-9405269906-0262810a8f.zip/node_modules/minimatch/",\ diff --git a/.yarn/cache/@noble-curves-npm-1.9.2-2584df26a7-21d049ae45.zip b/.yarn/cache/@noble-curves-npm-1.9.2-2584df26a7-21d049ae45.zip new file mode 100644 index 0000000000..77ea6d81ba --- /dev/null +++ b/.yarn/cache/@noble-curves-npm-1.9.2-2584df26a7-21d049ae45.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fcb2145fb54acd9efbe2f888e3e63d7100d8c52099d670b13a5147c2bfc4d6e +size 1937770 diff --git a/.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-9736aaa317.zip b/.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-9736aaa317.zip deleted file mode 100644 index ae074e4265..0000000000 --- a/.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-9736aaa317.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b17dfd98d20209940cbf72de0e3d67415a13c1766c941fafc5eb995cfcdaf2f -size 96630 diff --git a/.yarn/cache/brorand-npm-1.1.0-ea86634c4b-6f366d7c49.zip b/.yarn/cache/brorand-npm-1.1.0-ea86634c4b-6f366d7c49.zip deleted file mode 100644 index 1bfab44560..0000000000 --- a/.yarn/cache/brorand-npm-1.1.0-ea86634c4b-6f366d7c49.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4626fb9efc3197e5210834dc97fec65f0fbafa67fac802147a81b6149bfc63e0 -size 4594 diff --git a/.yarn/cache/elliptic-npm-6.6.1-87bb857cbc-8b24ef782e.zip b/.yarn/cache/elliptic-npm-6.6.1-87bb857cbc-8b24ef782e.zip deleted file mode 100644 index e5b5934c06..0000000000 --- a/.yarn/cache/elliptic-npm-6.6.1-87bb857cbc-8b24ef782e.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3d6bf45f543102131960445b810961fccfcc4466cd490e61478d3921d20ed6fe -size 124143 diff --git a/.yarn/cache/hash.js-npm-1.1.7-f1ad187358-41ada59494.zip b/.yarn/cache/hash.js-npm-1.1.7-f1ad187358-41ada59494.zip deleted file mode 100644 index 643d04bbc2..0000000000 --- a/.yarn/cache/hash.js-npm-1.1.7-f1ad187358-41ada59494.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b225f7bde495cccafef8707eae66fa232946f0d70e811a25249a4df63068d91 -size 45317 diff --git a/.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-f3d9ba31b4.zip b/.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-f3d9ba31b4.zip deleted file mode 100644 index ef3ee69541..0000000000 --- a/.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-f3d9ba31b4.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:70ddf7f39145cd4f158c86d6e826027d3ddc75df2e4e3230d68e7e61e486469e -size 26711 diff --git a/.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-96730e5601.zip b/.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-96730e5601.zip deleted file mode 100644 index 5a46c56ba0..0000000000 --- a/.yarn/cache/minimalistic-assert-npm-1.0.1-dc8bb23d29-96730e5601.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac8d3d4517e3016881f7f764f247b912dcbd2f708e9d2318819bd1b673db7ce0 -size 2453 diff --git a/.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-790ecec8c5.zip b/.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-790ecec8c5.zip deleted file mode 100644 index 47714af1f6..0000000000 --- a/.yarn/cache/minimalistic-crypto-utils-npm-1.0.1-e66b10822e-790ecec8c5.zip +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d859b281394524621cfaa912e36691cec8a07c6d562c3f9cecb86722e53f9ff -size 6428 diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 5dcf4aea7c..acb12ff217 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -44,9 +44,9 @@ "@cosmjs/encoding": "workspace:^", "@cosmjs/math": "workspace:^", "@cosmjs/utils": "workspace:^", + "@noble/curves": "^1.9.2", "@noble/hashes": "^1", "bn.js": "^5.2.0", - "elliptic": "^6.6.1", "libsodium-wrappers-sumo": "^0.7.11" }, "devDependencies": { diff --git a/packages/crypto/src/secp256k1.ts b/packages/crypto/src/secp256k1.ts index b16ca917a1..bc75442fb9 100644 --- a/packages/crypto/src/secp256k1.ts +++ b/packages/crypto/src/secp256k1.ts @@ -1,12 +1,9 @@ import { fromHex, toHex } from "@cosmjs/encoding"; -import BN from "bn.js"; -import elliptic from "elliptic"; +import { assert } from "@cosmjs/utils"; +import { secp256k1 } from "@noble/curves/secp256k1"; import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature"; -const secp256k1 = new elliptic.ec("secp256k1"); -const secp256k1N = new BN("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", "hex"); - export interface Secp256k1Keypair { /** A 32 byte private key */ readonly pubkey: Uint8Array; @@ -21,6 +18,17 @@ export interface Secp256k1Keypair { readonly privkey: Uint8Array; } +function unsignedBigIntToBytes(a: bigint): Uint8Array { + assert(a >= 0n); + let hex = a.toString(16); + if (hex.length % 2) hex = "0" + hex; + return fromHex(hex); +} + +function bytesToUnsignedBigInt(a: Uint8Array): bigint { + return BigInt("0x" + toHex(a)); +} + export class Secp256k1 { /** * Takes a 32 byte private key and returns a privkey/pubkey pair. @@ -30,30 +38,21 @@ export class Secp256k1 { */ public static async makeKeypair(privkey: Uint8Array): Promise { if (privkey.length !== 32) { - // is this check missing in secp256k1.validatePrivateKey? - // https://github.com/bitjson/bitcoin-ts/issues/4 throw new Error("input data is not a valid secp256k1 private key"); } - const keypair = secp256k1.keyFromPrivate(privkey); - if (keypair.validate().result !== true) { - throw new Error("input data is not a valid secp256k1 private key"); - } - - // range test that is not part of the elliptic implementation - const privkeyAsBigInteger = new BN(privkey); - if (privkeyAsBigInteger.gte(secp256k1N)) { + if (!secp256k1.utils.isValidPrivateKey(privkey)) { // not strictly smaller than N throw new Error("input data is not a valid secp256k1 private key"); } const out: Secp256k1Keypair = { - privkey: fromHex(keypair.getPrivate("hex")), + privkey: privkey, // encodes uncompressed as // - 1-byte prefix "04" // - 32-byte x coordinate // - 32-byte y coordinate - pubkey: Uint8Array.from(keypair.getPublic("array")), + pubkey: secp256k1.getPublicKey(privkey, false), }; return out; } @@ -75,15 +74,11 @@ export class Secp256k1 { throw new Error("Message hash length must not exceed 32 bytes"); } - const keypair = secp256k1.keyFromPrivate(privkey); - // the `canonical` option ensures creation of lowS signature representations - const { r, s, recoveryParam } = keypair.sign(messageHash, { canonical: true }); - if (typeof recoveryParam !== "number") throw new Error("Recovery param missing"); - return new ExtendedSecp256k1Signature( - Uint8Array.from(r.toArray()), - Uint8Array.from(s.toArray()), - recoveryParam, - ); + const { recovery, r, s } = secp256k1.sign(messageHash, privkey, { + lowS: true, + }); + if (typeof recovery !== "number") throw new Error("Recovery param missing"); + return new ExtendedSecp256k1Signature(unsignedBigIntToBytes(r), unsignedBigIntToBytes(s), recovery); } public static async verifySignature( @@ -98,33 +93,17 @@ export class Secp256k1 { throw new Error("Message hash length must not exceed 32 bytes"); } - const keypair = secp256k1.keyFromPublic(pubkey); - - // From https://github.com/indutny/elliptic: - // - // Sign the message's hash (input must be an array, or a hex-string) - // - // Signature MUST be either: - // 1) DER-encoded signature as hex-string; or - // 2) DER-encoded signature as buffer; or - // 3) object with two hex-string properties (r and s); or - // 4) object with two buffer properties (r and s) - // - // Uint8Array is not a Buffer, but elliptic seems to be happy with the interface - // common to both types. Uint8Array is not an array of ints but the interface is - // similar - try { - return keypair.verify(messageHash, signature.toDer()); - } catch (error) { - return false; - } + const encodedSig = secp256k1.Signature.fromDER(signature.toDer()); + return secp256k1.verify(encodedSig, messageHash, pubkey, { lowS: false }); } public static recoverPubkey(signature: ExtendedSecp256k1Signature, messageHash: Uint8Array): Uint8Array { - const signatureForElliptic = { r: toHex(signature.r()), s: toHex(signature.s()) }; - const point = secp256k1.recoverPubKey(messageHash, signatureForElliptic, signature.recovery); - const keypair = secp256k1.keyFromPublic(point); - return fromHex(keypair.getPublic(false, "hex")); + const pk = new secp256k1.Signature( + bytesToUnsignedBigInt(signature.r()), + bytesToUnsignedBigInt(signature.s()), + signature.recovery, + ).recoverPublicKey(messageHash); + return pk.toBytes(false); } /** @@ -137,7 +116,7 @@ export class Secp256k1 { case 33: return pubkey; case 65: - return Uint8Array.from(secp256k1.keyFromPublic(pubkey).getPublic(true, "array")); + return secp256k1.Point.fromHex(pubkey).toRawBytes(true); default: throw new Error("Invalid pubkey length"); } @@ -151,7 +130,7 @@ export class Secp256k1 { public static uncompressPubkey(pubkey: Uint8Array): Uint8Array { switch (pubkey.length) { case 33: - return Uint8Array.from(secp256k1.keyFromPublic(pubkey).getPublic(false, "array")); + return secp256k1.Point.fromHex(pubkey).toRawBytes(false); case 65: return pubkey; default: diff --git a/packages/crypto/src/slip10.ts b/packages/crypto/src/slip10.ts index 83275e0fb1..66bfc22d49 100644 --- a/packages/crypto/src/slip10.ts +++ b/packages/crypto/src/slip10.ts @@ -1,7 +1,7 @@ -import { fromHex, toAscii } from "@cosmjs/encoding"; +import { toAscii, toHex } from "@cosmjs/encoding"; import { Uint32, Uint53 } from "@cosmjs/math"; +import { secp256k1 } from "@noble/curves/secp256k1"; import BN from "bn.js"; -import elliptic from "elliptic"; import { Hmac } from "./hmac"; import { Sha512 } from "./sha"; @@ -21,6 +21,10 @@ export enum Slip10Curve { Ed25519 = "ed25519 seed", } +function bytesToUnsignedBigInt(a: Uint8Array): bigint { + return BigInt("0x" + toHex(a)); +} + /** * Reverse mapping of Slip10Curve */ @@ -77,8 +81,6 @@ export class Slip10RawIndex extends Uint32 { */ export type HdPath = readonly Slip10RawIndex[]; -const secp256k1 = new elliptic.ec("secp256k1"); - // Universal private key derivation accoring to // https://github.com/satoshilabs/slips/blob/master/slip-0010.md export class Slip10 { @@ -123,7 +125,7 @@ export class Slip10 { // Calculate I = HMAC-SHA512(Key = c_par, Data = ser_P(point(k_par)) || ser_32(i)). // where the functions point() and ser_p() are defined in BIP-0032 const data = new Uint8Array([ - ...Slip10.serializedPoint(curve, new BN(parentPrivkey)), + ...Slip10.serializedPoint(curve, bytesToUnsignedBigInt(parentPrivkey)), ...rawIndex.toBytesBigEndian(), ]); i = new Hmac(Sha512, parentChainCode).update(data).digest(); @@ -138,10 +140,10 @@ export class Slip10 { * * @see https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki */ - private static serializedPoint(curve: Slip10Curve, p: BN): Uint8Array { + private static serializedPoint(curve: Slip10Curve, p: bigint): Uint8Array { switch (curve) { case Slip10Curve.Secp256k1: - return fromHex(secp256k1.g.mul(p).encodeCompressed("hex")); + return secp256k1.Point.BASE.multiply(p).toRawBytes(true); default: throw new Error("curve not supported"); } diff --git a/yarn.lock b/yarn.lock index 3cbdd6449f..743d20b5e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -387,6 +387,7 @@ __metadata: "@cosmjs/math": "workspace:^" "@cosmjs/utils": "workspace:^" "@istanbuljs/nyc-config-typescript": "npm:^1.0.1" + "@noble/curves": "npm:^1.9.2" "@noble/hashes": "npm:^1" "@types/bn.js": "npm:^5" "@types/elliptic": "npm:^6.4.14" @@ -400,7 +401,6 @@ __metadata: "@typescript-eslint/parser": "npm:^5.54.0" bn.js: "npm:^5.2.0" buffer: "npm:^6.0.3" - elliptic: "npm:^6.6.1" eslint: "npm:^8.57.1" eslint-config-prettier: "npm:^8.3.0" eslint-import-resolver-node: "npm:^0.3.4" @@ -1233,7 +1233,16 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:^1, @noble/hashes@npm:^1.7.1": +"@noble/curves@npm:^1.9.2": + version: 1.9.2 + resolution: "@noble/curves@npm:1.9.2" + dependencies: + "@noble/hashes": "npm:1.8.0" + checksum: 10c0/21d049ae4558beedbf5da0004407b72db84360fa29d64822d82dc9e80251e1ecb46023590cc4b20e70eed697d1b87279b4911dc39f8694c51c874289cfc8e9a7 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1, @noble/hashes@npm:^1.7.1": version: 1.8.0 resolution: "@noble/hashes@npm:1.8.0" checksum: 10c0/06a0b52c81a6fa7f04d67762e08b2c476a00285858150caeaaff4037356dd5e119f45b2a530f638b77a5eeca013168ec1b655db41bae3236cb2e9d511484fc77 @@ -2532,13 +2541,6 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^4.11.9": - version: 4.12.0 - resolution: "bn.js@npm:4.12.0" - checksum: 10c0/9736aaa317421b6b3ed038ff3d4491935a01419ac2d83ddcfebc5717385295fcfcf0c57311d90fe49926d0abbd7a9dbefdd8861e6129939177f7e67ebc645b21 - languageName: node - linkType: hard - "bn.js@npm:^5.2.0": version: 5.2.0 resolution: "bn.js@npm:5.2.0" @@ -2594,13 +2596,6 @@ __metadata: languageName: node linkType: hard -"brorand@npm:^1.1.0": - version: 1.1.0 - resolution: "brorand@npm:1.1.0" - checksum: 10c0/6f366d7c4990f82c366e3878492ba9a372a73163c09871e80d82fb4ae0d23f9f8924cb8a662330308206e6b3b76ba1d528b4601c9ef73c2166b440b2ea3b7571 - languageName: node - linkType: hard - "browserslist@npm:^4.14.5, browserslist@npm:^4.21.9": version: 4.22.1 resolution: "browserslist@npm:4.22.1" @@ -3352,21 +3347,6 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:^6.6.1": - version: 6.6.1 - resolution: "elliptic@npm:6.6.1" - dependencies: - bn.js: "npm:^4.11.9" - brorand: "npm:^1.1.0" - hash.js: "npm:^1.0.0" - hmac-drbg: "npm:^1.0.1" - inherits: "npm:^2.0.4" - minimalistic-assert: "npm:^1.0.1" - minimalistic-crypto-utils: "npm:^1.0.1" - checksum: 10c0/8b24ef782eec8b472053793ea1e91ae6bee41afffdfcb78a81c0a53b191e715cbe1292aa07165958a9bbe675bd0955142560b1a007ffce7d6c765bcaf951a867 - languageName: node - linkType: hard - "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -4509,16 +4489,6 @@ __metadata: languageName: node linkType: hard -"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": - version: 1.1.7 - resolution: "hash.js@npm:1.1.7" - dependencies: - inherits: "npm:^2.0.3" - minimalistic-assert: "npm:^1.0.1" - checksum: 10c0/41ada59494eac5332cfc1ce6b7ebdd7b88a3864a6d6b08a3ea8ef261332ed60f37f10877e0c825aaa4bddebf164fbffa618286aeeec5296675e2671cbfa746c4 - languageName: node - linkType: hard - "hasha@npm:^5.0.0": version: 5.2.2 resolution: "hasha@npm:5.2.2" @@ -4538,17 +4508,6 @@ __metadata: languageName: node linkType: hard -"hmac-drbg@npm:^1.0.1": - version: 1.0.1 - resolution: "hmac-drbg@npm:1.0.1" - dependencies: - hash.js: "npm:^1.0.3" - minimalistic-assert: "npm:^1.0.0" - minimalistic-crypto-utils: "npm:^1.0.1" - checksum: 10c0/f3d9ba31b40257a573f162176ac5930109816036c59a09f901eb2ffd7e5e705c6832bedfff507957125f2086a0ab8f853c0df225642a88bf1fcaea945f20600d - languageName: node - linkType: hard - "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -5779,20 +5738,6 @@ __metadata: languageName: node linkType: hard -"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": - version: 1.0.1 - resolution: "minimalistic-assert@npm:1.0.1" - checksum: 10c0/96730e5601cd31457f81a296f521eb56036e6f69133c0b18c13fe941109d53ad23a4204d946a0d638d7f3099482a0cec8c9bb6d642604612ce43ee536be3dddd - languageName: node - linkType: hard - -"minimalistic-crypto-utils@npm:^1.0.1": - version: 1.0.1 - resolution: "minimalistic-crypto-utils@npm:1.0.1" - checksum: 10c0/790ecec8c5c73973a4fbf2c663d911033e8494d5fb0960a4500634766ab05d6107d20af896ca2132e7031741f19888154d44b2408ada0852446705441383e9f8 - languageName: node - linkType: hard - "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" From e7d3e8d7571b2679fe5eb496ba4fcdbcc94e5ff7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 8 Jul 2025 21:43:47 +0200 Subject: [PATCH 3/3] Add CHANGELOG: Migrate from elliptic to noble-curves --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9924fe3084..4875783d4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,9 @@ and this project adheres to - Fix block events in CometBFT 0.38 API (`begin_block_events`/`end_block_events` -> `finalize_block_events`) in `RpcBlockResultsResponse` and `BlockResultsResponse` ([#1612]) +- @cosmjs/crypto: Migrate from elliptic to noble-curves ([#1272]). +[#1272]: https://github.com/cosmos/cosmjs/issues/1272 [#1612]: https://github.com/cosmos/cosmjs/pull/1612 [#1645]: https://github.com/cosmos/cosmjs/pull/1645