-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
verifyArbitrary
util function
- Loading branch information
Showing
8 changed files
with
254 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { base64, utf8 } from "@scure/base"; | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { verifyADR36, verifyECDSA, verifyEIP191 } from "./verify"; | ||
|
||
const DATA = utf8.decode( | ||
"Hi from CosmeES! This is a test message just to prove that the wallet is working." | ||
); | ||
// Generated using coin type "330" and seed phrase "poverty flat amazing draw goose clay sorry nothing erase switch law intact only invest find memory what weasel fan connect tilt detect trap viable" | ||
const VALID_PUBKEY_1 = base64.decode( | ||
"Ai7ZXTtRWFte/tX7Z6MlKWVd9XA49p3cDNqd61RuKTdT" | ||
); | ||
// Generated using coin type "118" and seed phrase "poverty flat amazing draw goose clay sorry nothing erase switch law intact only invest find memory what weasel fan connect tilt detect trap viable" | ||
const VALID_PUBKEY_2 = base64.decode( | ||
"A8i9vMNUGcTtUgpbmiZqcFtsIrPZ6n8ZYN4/PVRlQvGr" | ||
); | ||
// Generated using coin type "60" and seed phrase "poverty flat amazing draw goose clay sorry nothing erase switch law intact only invest find memory what weasel fan connect tilt detect trap viable" | ||
const VALID_PUBKEY_3 = base64.decode( | ||
"AmGjuPKUsuIAuGgJ3xH7KGWlSU9cwVnsesrwWwyYLbMg" | ||
); | ||
|
||
describe("verifyECDSA", () => { | ||
it("should verify correctly", () => { | ||
// Signed using Station wallet on Terra | ||
const signature = base64.decode( | ||
"Od87qNoOyXuDOVdLCGTXB6dFN7U0XF9Oegc8KDa+AWwX3jkrDXG++2nlPfsF4VJzlDHsoikPeZpxrB7v9PINnw==" | ||
); | ||
const res1 = verifyECDSA({ | ||
pubKey: VALID_PUBKEY_1, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res1).toBe(true); | ||
|
||
// Different pub key | ||
const res2 = verifyECDSA({ | ||
pubKey: VALID_PUBKEY_2, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res2).toBe(false); | ||
}); | ||
}); | ||
|
||
describe("verifyADR36", () => { | ||
it("should verify correctly", () => { | ||
// Signed using Keplr wallet on Osmosis | ||
const signature = base64.decode( | ||
"nvlcV0x0Ge8ADXLSAQGtfMw6EJkOfpmkDxgP7UI79uR8MhnAOp9T+e+ofgW9kY4bEIr0yhyBG+vSVAZRv9uCxA==" | ||
); | ||
const res1 = verifyADR36({ | ||
bech32Prefix: "osmo", | ||
pubKey: VALID_PUBKEY_2, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res1).toBe(true); | ||
|
||
// Different bech32 prefix | ||
const res2 = verifyADR36({ | ||
bech32Prefix: "terra", | ||
pubKey: VALID_PUBKEY_2, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res2).toBe(false); | ||
|
||
// Different pub key | ||
const res3 = verifyADR36({ | ||
bech32Prefix: "osmo", | ||
pubKey: VALID_PUBKEY_1, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res3).toBe(false); | ||
}); | ||
}); | ||
|
||
describe("verifyEIP191", () => { | ||
it("should verify correctly", () => { | ||
// Signed using MetaMask wallet on Injective | ||
const signature = base64.decode( | ||
"MpriWY0Kq7C+/jR3eOfNB5vUQM144tQk7KkzKyYCTFB5QHGLZjzJyeOSr8/ENFES0k+aaEF47Wepk7OHoZuLzxs=" | ||
); | ||
const res1 = verifyEIP191({ | ||
pubKey: VALID_PUBKEY_3, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res1).toBe(true); | ||
|
||
// Different pub key | ||
const res2 = verifyEIP191({ | ||
pubKey: VALID_PUBKEY_2, | ||
data: DATA, | ||
signature, | ||
}); | ||
expect(res2).toBe(false); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { sha256 } from "@noble/hashes/sha256"; | ||
import * as secp256k1 from "@noble/secp256k1"; | ||
import { base64 } from "@scure/base"; | ||
|
||
import { resolveBech32Address } from "./address"; | ||
import { serialiseSignDoc } from "./serialise"; | ||
import { recoverPubKeyFromEthSignature } from "./sign"; | ||
|
||
type VerifyArbitraryParams = { | ||
/** The public key which created the signature */ | ||
pubKey: Uint8Array; | ||
/** The bech32 account address prefix of the signer */ | ||
bech32Prefix: string; | ||
/** The arbitrary bytes that was signed */ | ||
data: Uint8Array; | ||
/** The signature bytes */ | ||
signature: Uint8Array; | ||
}; | ||
|
||
export function verifyECDSA({ | ||
pubKey, | ||
data, | ||
signature, | ||
}: Omit<VerifyArbitraryParams, "bech32Prefix">): boolean { | ||
return secp256k1.verify(signature, sha256(data), pubKey); | ||
} | ||
|
||
export function verifyADR36({ | ||
pubKey, | ||
bech32Prefix, | ||
data, | ||
signature, | ||
}: VerifyArbitraryParams): boolean { | ||
const msg = serialiseSignDoc({ | ||
chain_id: "", | ||
account_number: "0", | ||
sequence: "0", | ||
fee: { | ||
gas: "0", | ||
amount: [], | ||
}, | ||
msgs: [ | ||
{ | ||
type: "sign/MsgSignData", | ||
value: { | ||
signer: resolveBech32Address(pubKey, bech32Prefix), | ||
data: base64.encode(data), | ||
}, | ||
}, | ||
], | ||
memo: "", | ||
}); | ||
return verifyECDSA({ | ||
pubKey, | ||
data: msg, | ||
signature, | ||
}); | ||
} | ||
|
||
export function verifyEIP191({ | ||
pubKey, | ||
data, | ||
signature, | ||
}: Omit<VerifyArbitraryParams, "bech32Prefix">): boolean { | ||
const recoveredPubKey = recoverPubKeyFromEthSignature(data, signature); | ||
return ( | ||
pubKey.length === recoveredPubKey.length && | ||
pubKey.every((v, i) => v === recoveredPubKey[i]) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { | ||
base64, | ||
utf8, | ||
verifyADR36, | ||
verifyECDSA, | ||
verifyEIP191, | ||
} from "cosmes/codec"; | ||
|
||
import { WalletName } from "../constants/WalletName"; | ||
|
||
type VerifyArbitraryParams = { | ||
/** The identifier of the wallet which created the signature */ | ||
wallet: WalletName; | ||
/** The base64 encoded public key which created the signature */ | ||
pubKey: string; | ||
/** The bech32 account address prefix of the signer */ | ||
bech32Prefix: string; | ||
/** The utf-8 encoded arbitrary string that was signed */ | ||
data: string; | ||
/** The base64 encoded string of the signature */ | ||
signature: string; | ||
}; | ||
|
||
/** | ||
* Verifies the signature output of a valid call to `ConnectedWallet.signArbitrary`. | ||
* Returns `true` if and only if the signature is valid. | ||
* | ||
* @param wallet The identifier of the wallet which created the signature | ||
* @param pubKey The base64 encoded public key which created the signature | ||
* @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 | ||
*/ | ||
export function verifyArbitrary({ | ||
wallet, | ||
pubKey, | ||
bech32Prefix, | ||
data, | ||
signature, | ||
}: VerifyArbitraryParams): boolean { | ||
const params = { | ||
wallet, | ||
pubKey: base64.decode(pubKey), | ||
bech32Prefix, | ||
data: utf8.decode(data), | ||
signature: utf8.decode(signature), | ||
}; | ||
try { | ||
switch (wallet) { | ||
case WalletName.STATION: | ||
return verifyECDSA(params); | ||
case WalletName.COMPASS: | ||
case WalletName.COSMOSTATION: | ||
case WalletName.KEPLR: | ||
case WalletName.LEAP: | ||
return verifyADR36(params); | ||
case WalletName.METAMASK_INJECTIVE: | ||
return verifyEIP191(params); | ||
} | ||
} catch (err) { | ||
return false; | ||
} | ||
} |