Skip to content

Commit

Permalink
fix: verify injective adr36 signatures correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronCQL committed Jan 13, 2024
1 parent f8a54f4 commit e4f1e1a
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 7 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## `v0.0.50`

### Fixes

- Fixed the `verifyArbitrary` function to work correctly for all chains, including Injective which uses a different key algorithm

## `v0.0.49`

### Features
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cosmes",
"version": "0.0.50-rc.0",
"version": "0.0.50",
"private": false,
"packageManager": "pnpm@8.3.0",
"sideEffects": false,
Expand Down
6 changes: 6 additions & 0 deletions src/codec/address.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { resolveBech32Address, translateEthToBech32Address } from "./address";

const PUB_KEY_1 = "A6Y9fcWSn5Av/HLHBwthTaVE/vdyRKvsTzi5U7j9bFj5"; // random pub key
const PUB_KEY_2 = "Ag/a1BOl3cdwh67Z8iCbGmAu4WWmBwtuQlQMbDaN385V"; // coinhall.org val pubkey
const PUB_KEY_3 = "AmGjuPKUsuIAuGgJ3xH7KGWlSU9cwVnsesrwWwyYLbMg"; // random pub key

const ETH_ADDRESS_1 = "0xd6E80d86483C0cF463E03cC95246bDc0FeF6cfbD"; // random eth address

Expand All @@ -29,6 +30,11 @@ describe("resolveBech32Address", () => {
"terravaloper1ge3vqn6cjkk2xkfwpg5ussjwxvahs2f6at87yp"
);
});

it("should resolve ethsecp256k1 type address correctly", () => {
const translated = resolveBech32Address(PUB_KEY_3, "inj", "ethsecp256k1");
expect(translated).toBe("inj1ys3hr2a6sn3wwqsmmrk8pgrvk58e8wrn6zn44m");
});
});

describe("translateEthToBech32Address", () => {
Expand Down
21 changes: 17 additions & 4 deletions src/codec/address.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
import { ripemd160 } from "@noble/hashes/ripemd160";
import { sha256 } from "@noble/hashes/sha256";
import { keccak_256 } from "@noble/hashes/sha3";
import { ProjectivePoint } from "@noble/secp256k1";
import { base64, bech32 } from "@scure/base";

import { ethhex } from "./ethhex";

/**
* Resolves the bech32 address from the given `publicKey` and `prefix`.
* Returns the bech32 address from the given `publicKey` and `prefix`. If needed,
* the `type` of the key should be appropriately set.
*
* @param publicKey Must be either a base64 encoded string or a `Uint8Array`.
*/
export function resolveBech32Address(
publicKey: string | Uint8Array,
prefix: string
prefix: string,
type: "secp256k1" | "ethsecp256k1" = "secp256k1"
): string {
const bytes =
const pubKey =
typeof publicKey === "string" ? base64.decode(publicKey) : publicKey;
return bech32.encode(prefix, bech32.toWords(ripemd160(sha256(bytes))));
const address =
type === "secp256k1"
? // For cosmos: take the ripemd160 of the sha256 of the public key
ripemd160(sha256(pubKey))
: // For eth: take the last 20 bytes of the keccak of the uncompressed public key without the first byte
keccak_256(
ProjectivePoint.fromHex(pubKey).toRawBytes(false).slice(1)
).slice(-20);
return bech32.encode(prefix, bech32.toWords(address));
}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/codec/verify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ describe("verifyADR36", () => {
});
expect(res3).toBe(false);
});

it("should verify ethsecp256k1 type signatures correctly", () => {
// Signed using Keplr wallet on Injective
const signature = base64.decode(
"+7PNZm4XxKtpvZA+HqxpMKJgZcqA2w3WVSheLGvzrrIBJZGOTdcpBT7wLUhluY46EokTeRRWUaBDSv2vVoEdfw=="
);
const res1 = verifyADR36({
bech32Prefix: "inj",
pubKey: VALID_PUBKEY_3,
data: DATA,
signature,
type: "ethsecp256k1",
});
expect(res1).toBe(true);
});
});

describe("verifyEIP191", () => {
Expand Down
14 changes: 12 additions & 2 deletions src/codec/verify.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { sha256 } from "@noble/hashes/sha256";
import { keccak_256 } from "@noble/hashes/sha3";
import * as secp256k1 from "@noble/secp256k1";
import { base64 } from "@scure/base";

Expand All @@ -15,21 +16,29 @@ type VerifyArbitraryParams = {
data: Uint8Array;
/** The signature bytes */
signature: Uint8Array;
/** The type of the signature */
type?: "secp256k1" | "ethsecp256k1";
};

export function verifyECDSA({
pubKey,
data,
signature,
type,
}: Omit<VerifyArbitraryParams, "bech32Prefix">): boolean {
return secp256k1.verify(signature, sha256(data), pubKey);
return secp256k1.verify(
signature,
type === "ethsecp256k1" ? keccak_256(data) : sha256(data),
pubKey
);
}

export function verifyADR36({
pubKey,
bech32Prefix,
data,
signature,
type,
}: VerifyArbitraryParams): boolean {
const msg = serialiseSignDoc({
chain_id: "",
Expand All @@ -43,7 +52,7 @@ export function verifyADR36({
{
type: "sign/MsgSignData",
value: {
signer: resolveBech32Address(pubKey, bech32Prefix),
signer: resolveBech32Address(pubKey, bech32Prefix, type),
data: base64.encode(data),
},
},
Expand All @@ -54,6 +63,7 @@ export function verifyADR36({
pubKey,
data: msg,
signature,
type,
});
}

Expand Down
5 changes: 5 additions & 0 deletions src/wallet/utils/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type VerifyArbitraryParams = {
data: string;
/** The base64 encoded string of the signature */
signature: string;
/** The type of the signature */
type?: "secp256k1" | "ethsecp256k1";
};

/**
Expand All @@ -30,20 +32,23 @@ type VerifyArbitraryParams = {
* @param bech32Prefix The bech32 account address prefix of the signer
* @param data The utf-8 encoded arbitrary string that was signed
* @param signature The base64 encoded string of the signature
* @param type The type of the signature (default: `secp256k1`)
*/
export function verifyArbitrary({
wallet,
pubKey,
bech32Prefix,
data,
signature,
type = "secp256k1",
}: VerifyArbitraryParams): boolean {
const params = {
wallet,
pubKey: base64.decode(pubKey),
bech32Prefix,
data: utf8.decode(data),
signature: base64.decode(signature),
type,
};
try {
switch (wallet) {
Expand Down

0 comments on commit e4f1e1a

Please sign in to comment.