Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ jobs:

steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '9.15.4'

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: "20.x"
node-version: "22.x"
cache: "pnpm"

- name: Install Dependencies
Expand All @@ -48,17 +48,17 @@ jobs:

steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '9.15.4'

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: "20.x"
node-version: "22.x"
cache: "pnpm"

- name: Install Dependencies
Expand All @@ -81,17 +81,17 @@ jobs:

steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '9.15.4'

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: "20.x"
node-version: "22.x"
cache: "pnpm"

- name: Install Dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/injective-e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ jobs:

steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '9.15.4'

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: "20.x"
node-version: "22.x"
cache: "pnpm"

- name: Install Dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/run-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '9.15.4'

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: "20.x"
node-version: "22.x"
cache: "pnpm"

- name: Install Dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/solana-unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ jobs:

steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '9.15.4'

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: "20.x"
node-version: "22.x"
cache: "pnpm"

- name: Install Dependencies
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ Then an authz example website will be created and users can take a look how sign
| ---------------------------- | ------------------------------------------------------------- |
| **Transactions** | [@interchainjs/cosmos](https://docs.hyperweb.io/interchain-js/networks/cosmos) |
| **Cosmos Types** | [@interchainjs/cosmos-types](https://docs.hyperweb.io/interchain-js/libs/cosmos-types) |
| **Migration from `@cosmjs`** | [Migration Guide](https://docs.hyperweb.io/interchain-js/advanced/migration-from-cosmjs.mdx) |
| **Migration from `@cosmjs`** | [Migration Guide](https://docs.hyperweb.io/interchain-js/advanced/migration-from-cosmjs) |

---

Expand Down
4 changes: 0 additions & 4 deletions networks/ethereum/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@
"prepack": "makage build"
},
"dependencies": {
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/hash": "^5.7.0",
"@ethersproject/transactions": "^5.7.0",
"@interchainjs/auth": "workspace:*",
"@interchainjs/encoding": "workspace:^",
"@interchainjs/types": "workspace:*",
Expand Down
1 change: 0 additions & 1 deletion networks/injective/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"prepack": "pnpm run build"
},
"dependencies": {
"@ethersproject/transactions": "^5.7.0",
"@interchainjs/auth": "workspace:*",
"@interchainjs/cosmos": "workspace:*",
"@interchainjs/cosmos-types": "workspace:*",
Expand Down
4 changes: 1 addition & 3 deletions packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,14 @@
"@interchainjs/crypto": "workspace:*",
"@interchainjs/types": "workspace:*",
"@interchainjs/utils": "workspace:*",
"@noble/curves": "^1.1.0",
"@noble/curves": "^1.9.7",
"@noble/hashes": "^1.3.1",
"@scure/bip32": "^1.0.10",
"bech32": "^2.0.0",
"elliptic": "^6.5.4",
"libsodium-wrappers-sumo": "^0.8.2"
},
"devDependencies": {
"@jest/globals": "^30.0.0",
"@types/elliptic": "^6.4.18",
"@types/libsodium-wrappers-sumo": "^0.8.2",
"makage": "^0.1.12"
},
Expand Down
7 changes: 2 additions & 5 deletions packages/auth/src/config/algorithms.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { IAlgo } from '@interchainjs/types';
import { Secp256k1, Ed25519, Secp256k1Signature } from '@interchainjs/crypto';
import elliptic from 'elliptic';
import { secp256k1 } from '@noble/curves/secp256k1.js';
import sodium from 'libsodium-wrappers-sumo';

const secp256k1 = new elliptic.ec('secp256k1');

// Secp256k1 algorithm implementation
export const Secp256k1Algo: IAlgo = {
name: 'secp256k1',
makeKeypair: (privateKey: Uint8Array) => {
// Synchronous version of Secp256k1.makeKeypair
const keyPair = secp256k1.keyFromPrivate(privateKey);
const pubkey = new Uint8Array(keyPair.getPublic(false, 'array')); // uncompressed
const pubkey = secp256k1.getPublicKey(privateKey, false); // uncompressed
return { privkey: privateKey, pubkey };
},
compress: (pubkey: Uint8Array) => {
Expand Down
3 changes: 1 addition & 2 deletions packages/crypto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,13 @@
"@interchainjs/encoding": "workspace:*",
"@interchainjs/math": "workspace:*",
"@interchainjs/utils": "workspace:*",
"@noble/curves": "^1.9.7",
"@noble/hashes": "^1",
"bn.js": "^5.2.0",
"elliptic": "^6.5.4",
"libsodium-wrappers-sumo": "^0.8.2"
},
"devDependencies": {
"@types/bn.js": "^5.2.0",
"@types/elliptic": "^6.4.18",
"@types/libsodium-wrappers-sumo": "^0.8.2",
"makage": "^0.1.12"
}
Expand Down
70 changes: 23 additions & 47 deletions packages/crypto/src/secp256k1.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { fromHex, toHex } from "@interchainjs/encoding";
import BN from "bn.js";
import elliptic from "elliptic";
import { secp256k1 } from "@noble/curves/secp256k1.js";
import { bytesToNumberBE, numberToBytesBE } from "@noble/curves/utils.js";

import { ExtendedSecp256k1Signature, Secp256k1Signature } from "./secp256k1signature";

const secp256k1 = new elliptic.ec("secp256k1");
const secp256k1N = new BN("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", "hex");
function toUnpaddedBytes(n: bigint): Uint8Array {
const bytes = numberToBytesBE(n, 32);
const firstNonZero = bytes.findIndex((byte) => byte !== 0);
return bytes.slice(firstNonZero);
}

export interface Secp256k1Keypair {
/** A 32 byte private key */
Expand Down Expand Up @@ -35,25 +37,17 @@ export class Secp256k1 {
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)) {
// not strictly smaller than N
if (!secp256k1.utils.isValidSecretKey(privkey)) {
throw new Error("input data is not a valid secp256k1 private key");
}

const out: Secp256k1Keypair = {
privkey: fromHex(keypair.getPrivate("hex")),
privkey: Uint8Array.from(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;
}
Expand All @@ -75,15 +69,9 @@ 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 { r, s, recovery } = secp256k1.sign(messageHash, privkey, { prehash: false, lowS: true });
if (typeof recovery !== "number") throw new Error("Recovery param missing");
return new ExtendedSecp256k1Signature(toUnpaddedBytes(r), toUnpaddedBytes(s), recovery);
}

public static async verifySignature(
Expand All @@ -98,33 +86,21 @@ 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());
return secp256k1.verify(new Uint8Array([...signature.r(32), ...signature.s(32)]), messageHash, pubkey, { prehash: false, lowS: false });
} catch (error) {
return 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"));
return new secp256k1.Signature(
bytesToNumberBE(signature.r()),
bytesToNumberBE(signature.s()),
signature.recovery,
)
.recoverPublicKey(messageHash)
.toBytes(false);
}

/**
Expand All @@ -137,7 +113,7 @@ export class Secp256k1 {
case 33:
return pubkey;
case 65:
return Uint8Array.from(secp256k1.keyFromPublic(pubkey).getPublic(true, "array"));
return secp256k1.Point.fromBytes(pubkey).toBytes(true);
default:
throw new Error("Invalid pubkey length");
}
Expand All @@ -151,7 +127,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.fromBytes(pubkey).toBytes(false);
case 65:
return pubkey;
default:
Expand Down
12 changes: 5 additions & 7 deletions packages/crypto/src/slip10.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fromHex, toAscii } from "@interchainjs/encoding";
import { toAscii } from "@interchainjs/encoding";
import { Uint32, Uint53 } from "@interchainjs/math";
import { secp256k1 } from "@noble/curves/secp256k1.js";
import BN from "bn.js";
import elliptic from "elliptic";

import { Hmac } from "./hmac";
import { Sha512 } from "./sha";
Expand Down Expand Up @@ -77,8 +77,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 {
Expand Down Expand Up @@ -123,7 +121,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, parentPrivkey),
...rawIndex.toBytesBigEndian(),
]);
i = new Hmac(Sha512, parentChainCode).update(data).digest();
Expand All @@ -138,10 +136,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, privkey: Uint8Array): Uint8Array {
switch (curve) {
case Slip10Curve.Secp256k1:
return fromHex(secp256k1.g.mul(p).encodeCompressed("hex"));
return secp256k1.getPublicKey(privkey, true);
default:
throw new Error("curve not supported");
}
Expand Down
Loading
Loading