From 32caa633a0d2d2491c7acc79bfe31339ab932177 Mon Sep 17 00:00:00 2001 From: Roman Gascoin Date: Thu, 4 Jan 2024 17:11:57 +0100 Subject: [PATCH] Feat: Add a way to verify signature inside the Ts SDK (#135) * Feat: messages signatures can't be only verified on the Aleph's Network Solution: Add a way to verify signatures directly in the SDK * Feat: signature can't be verified without an instanced account Solution: move verify method into utils/signature * Feat: Add signature verification for Solana inside the ts sdk * Feat: Add signature verification for Substrate inside the ts sdk * Feat: Add signature verification for Tezos inside the ts sdk * verifCosmos script added without test * fix imports * rename "verif" to "verify" everywhere * use default import for verify functions --------- Co-authored-by: leirbag95 Co-authored-by: mhh --- src/accounts/avalanche.ts | 8 ++- src/accounts/ethereum.ts | 10 ++-- src/accounts/solana.ts | 6 ++- src/accounts/substrate.ts | 10 ++-- src/accounts/tezos.ts | 1 + src/index.ts | 3 +- src/utils/signature/index.ts | 8 +++ src/utils/signature/verifyAvalanche.ts | 39 +++++++++++++++ src/utils/signature/verifyCosmos.ts | 40 +++++++++++++++ src/utils/signature/verifyEthereum.ts | 24 +++++++++ src/utils/signature/verifySolana.ts | 24 +++++++++ src/utils/signature/verifySubstrate.ts | 26 ++++++++++ src/utils/signature/verifyTezos.ts | 29 +++++++++++ tests/accounts/avalanche.test.ts | 60 ++++++++++++++++++++++ tests/accounts/ethereum.test.ts | 58 ++++++++++++++++++++++ tests/accounts/solana.test.ts | 54 ++++++++++++++++++++ tests/accounts/tezos.test.ts | 57 ++++++++++++++++++++- tests/index.ts | 22 ++++++++- tests/messages/aggregate/update.test.ts | 4 +- testsAuto/accounts/substrate.auto.ts | 66 ++++++++++++++++++++++++- 20 files changed, 534 insertions(+), 15 deletions(-) create mode 100644 src/utils/signature/index.ts create mode 100644 src/utils/signature/verifyAvalanche.ts create mode 100644 src/utils/signature/verifyCosmos.ts create mode 100644 src/utils/signature/verifyEthereum.ts create mode 100644 src/utils/signature/verifySolana.ts create mode 100644 src/utils/signature/verifySubstrate.ts create mode 100644 src/utils/signature/verifyTezos.ts diff --git a/src/accounts/avalanche.ts b/src/accounts/avalanche.ts index fb52e396..b2ecfd07 100644 --- a/src/accounts/avalanche.ts +++ b/src/accounts/avalanche.ts @@ -11,6 +11,8 @@ import { BaseProviderWallet } from "./providers/BaseProviderWallet"; import { providers } from "ethers"; import { privateToAddress } from "ethereumjs-util"; import { ProviderEncryptionLabel, ProviderEncryptionLib } from "./providers/ProviderEncryptionLib"; +import verifyAvalanche from "../utils/signature/verifyAvalanche"; + /** * AvalancheAccount implements the Account class for the Avalanche protocol. * It is used to represent an Avalanche account when publishing a message on the Aleph network. @@ -129,7 +131,10 @@ export class AvalancheAccount extends ECIESAccount { const signatureBuffer = this.signer?.sign(digestBuff); const bintools = BinTools.getInstance(); - return bintools.cb58Encode(signatureBuffer); + const signature = bintools.cb58Encode(signatureBuffer); + if (await verifyAvalanche(buffer, signature, this.signer.getPublicKey().toString("hex"))) return signature; + + throw new Error("Cannot proof the integrity of the signature"); } else if (this.provider) { return await this.provider.signMessage(buffer); } @@ -176,6 +181,7 @@ export async function getKeyPair(privateKey?: string, chain = ChainType.X_CHAIN) * It creates an Avalanche keypair containing information about the account, extracted in the AvalancheAccount constructor. * * @param privateKey The private key of the account to import. + * @param chain The Avalanche subnet to use the account with. */ export async function ImportAccountFromPrivateKey( privateKey: string, diff --git a/src/accounts/ethereum.ts b/src/accounts/ethereum.ts index 642d27d8..c158e822 100644 --- a/src/accounts/ethereum.ts +++ b/src/accounts/ethereum.ts @@ -3,9 +3,10 @@ import { ethers } from "ethers"; import { ECIESAccount } from "./account"; import { GetVerificationBuffer } from "../messages"; import { BaseMessage, Chain } from "../messages/types"; +import verifyEthereum from "../utils/signature/verifyEthereum"; +import { BaseProviderWallet } from "./providers/BaseProviderWallet"; import { decrypt as secp256k1_decrypt, encrypt as secp256k1_encrypt } from "eciesjs"; import { ChangeRpcParam, JsonRPCWallet, RpcChainType } from "./providers/JsonRPCWallet"; -import { BaseProviderWallet } from "./providers/BaseProviderWallet"; import { ProviderEncryptionLabel, ProviderEncryptionLib } from "./providers/ProviderEncryptionLib"; /** @@ -109,9 +110,12 @@ export class ETHAccount extends ECIESAccount { const buffer = GetVerificationBuffer(message); const signMethod = this.wallet || this.provider; - if (signMethod) return signMethod.signMessage(buffer.toString()); + if (!signMethod) throw new Error("Cannot sign message"); + + const signature = await signMethod.signMessage(buffer.toString()); + if (verifyEthereum(buffer, signature, this.address)) return signature; - throw new Error("Cannot sign message"); + throw new Error("Cannot proof the integrity of the signature"); } } diff --git a/src/accounts/solana.ts b/src/accounts/solana.ts index 6e3d8a51..7773e9b9 100644 --- a/src/accounts/solana.ts +++ b/src/accounts/solana.ts @@ -4,6 +4,7 @@ import { GetVerificationBuffer } from "../messages"; import { Keypair, PublicKey } from "@solana/web3.js"; import nacl from "tweetnacl"; import base58 from "bs58"; +import verifySolana from "../utils/signature/verifySolana"; type WalletSignature = { signature: Uint8Array; @@ -63,10 +64,13 @@ export class SOLAccount extends Account { throw new Error("Cannot sign message"); } - return JSON.stringify({ + const parsedSignature = JSON.stringify({ signature: base58.encode(signature), publicKey: this.address, }); + if (verifySolana(buffer, parsedSignature)) return parsedSignature; + + throw new Error("Cannot proof the integrity of the signature"); } } diff --git a/src/accounts/substrate.ts b/src/accounts/substrate.ts index 19dccf79..7de04fba 100644 --- a/src/accounts/substrate.ts +++ b/src/accounts/substrate.ts @@ -6,8 +6,9 @@ import { GetVerificationBuffer } from "../messages"; import { InjectedExtension } from "@polkadot/extension-inject/types"; import { Keyring } from "@polkadot/keyring"; import { KeyringPair } from "@polkadot/keyring/types"; -import { cryptoWaitReady, signatureVerify } from "@polkadot/util-crypto"; +import { cryptoWaitReady } from "@polkadot/util-crypto"; import { generateMnemonic } from "@polkadot/util-crypto/mnemonic/bip39"; +import verifySubstrate from "../utils/signature/verifySubstrate"; import { stringToHex } from "@polkadot/util"; /** @@ -58,12 +59,13 @@ export class DOTAccount extends Account { } } - if (!signatureVerify(buffer, signed, this.address).isValid) throw new Error("Data can't be signed."); - - return JSON.stringify({ + const signature = JSON.stringify({ curve: "sr25519", data: signed, }); + if (verifySubstrate(message, signature, this.address)) return signature; + + throw new Error("Cannot proof the integrity of the signature"); } /** diff --git a/src/accounts/tezos.ts b/src/accounts/tezos.ts index 3ade1e8b..96f8f3d6 100644 --- a/src/accounts/tezos.ts +++ b/src/accounts/tezos.ts @@ -79,6 +79,7 @@ export class TEZOSAccount extends Account { } else { signature = (await this.wallet.sign(payloadBytes)).sig; } + return JSON.stringify({ signature: signature, publicKey: await this.GetPublicKey(), diff --git a/src/index.ts b/src/index.ts index a86d5b56..4fd3ad2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import * as accounts from "./accounts/index"; import * as messages from "./messages"; import * as Ledger from "./accounts/providers/Ledger"; +import * as utils from "./utils/signature"; -export { accounts, Ledger, messages }; +export { accounts, Ledger, messages, utils }; diff --git a/src/utils/signature/index.ts b/src/utils/signature/index.ts new file mode 100644 index 00000000..cadc0a99 --- /dev/null +++ b/src/utils/signature/index.ts @@ -0,0 +1,8 @@ +import verifyEthereum from "./verifyEthereum"; +import verifyAvalanche from "./verifyAvalanche"; +import verifySolana from "./verifySolana"; +import verifySubstrate from "./verifySubstrate"; +import verifyTezos from "./verifyTezos"; +import verifyCosmos from "./verifyCosmos"; + +export { verifyEthereum, verifyAvalanche, verifySolana, verifySubstrate, verifyTezos, verifyCosmos }; diff --git a/src/utils/signature/verifyAvalanche.ts b/src/utils/signature/verifyAvalanche.ts new file mode 100644 index 00000000..7337b6df --- /dev/null +++ b/src/utils/signature/verifyAvalanche.ts @@ -0,0 +1,39 @@ +import { BaseMessage } from "../../messages/types"; +import { GetVerificationBuffer } from "../../messages"; +import { Avalanche, BinTools, Buffer as AvaBuff } from "avalanche"; +import shajs from "sha.js"; + +async function digestMessage(message: Buffer) { + const msgSize = Buffer.alloc(4); + msgSize.writeUInt32BE(message.length, 0); + const msgStr = message.toString("utf-8"); + const msgBuf = Buffer.from(`\x1AAvalanche Signed Message:\n${msgSize}${msgStr}`, "utf8"); + + return new shajs.sha256().update(msgBuf).digest(); +} + +/** + * Provide a way to verify the authenticity of a signature associated with a given message. + * This method rely on the Keypair.recover() implementation. + * + * @param message The content of the signature to verify. It can be the result of GetVerificationBuffer() or directly a BaseMessage object. + * @param signature The signature associated with the first params of this method. + * @param signerPKey Optional, The publicKey associated with the signature to verify. It Needs to be under a hex serialized string. + */ +async function verifyAvalanche(message: Buffer | BaseMessage, signature: string, signerPKey: string): Promise { + if (!(message instanceof Buffer)) message = GetVerificationBuffer(message); + const ava = new Avalanche(); + const keyPair = ava.XChain().keyChain().makeKey(); + + const bintools = BinTools.getInstance(); + const readableSignature = bintools.cb58Decode(signature); + + const digest = await digestMessage(message); + const digestHex = digest.toString("hex"); + const digestBuff = AvaBuff.from(digestHex, "hex"); + + const recovered = keyPair.recover(digestBuff, readableSignature); + return signerPKey === recovered.toString("hex"); +} + +export default verifyAvalanche; diff --git a/src/utils/signature/verifyCosmos.ts b/src/utils/signature/verifyCosmos.ts new file mode 100644 index 00000000..db78fc1b --- /dev/null +++ b/src/utils/signature/verifyCosmos.ts @@ -0,0 +1,40 @@ +import { BaseMessage } from "../../messages/types"; +import { GetVerificationBuffer } from "../../messages"; +import elliptic from "elliptic"; + +/** + * Provide a way to verify the authenticity of a signature associated with a given message. + * This method rely on the ethers.utils.verifyMessage() implementation. + * + * @param message The content of the signature to verify. It can be the result of GetVerificationBuffer() or directly a BaseMessage object. + * @param serializedSignature The signature associated with the first params of this method. + */ +async function verifyCosmos(message: Buffer | BaseMessage, serializedSignature: string): Promise { + if (!(message instanceof Buffer)) message = GetVerificationBuffer(message); + + const { signature, pub_key } = JSON.parse(serializedSignature); + const secp256k1 = new elliptic.ec("secp256k1"); + + // unsupported curve checking + if (pub_key?.type !== "tendermint/PubKeySecp256k1") return false; + + // Decode the Base64-encoded signature + const publicKey = Buffer.from(pub_key.value, "base64"); + const signatureBuffer = Buffer.from(signature, "base64"); + + // Extract the r and s values from the signature + const r = signatureBuffer.slice(0, 32); + const s = signatureBuffer.slice(32, 64); + + // Create a signature object with the r and s values + const signatureObj = { r, s }; + + try { + const key = secp256k1.keyFromPublic(publicKey); + return key.verify(message, signatureObj); + } catch (e: unknown) { + return false; + } +} + +export default verifyCosmos; diff --git a/src/utils/signature/verifyEthereum.ts b/src/utils/signature/verifyEthereum.ts new file mode 100644 index 00000000..e57bd5cc --- /dev/null +++ b/src/utils/signature/verifyEthereum.ts @@ -0,0 +1,24 @@ +import { BaseMessage } from "../../messages/types"; +import { GetVerificationBuffer } from "../../messages"; +import { ethers } from "ethers"; + +/** + * Provide a way to verify the authenticity of a signature associated with a given message. + * This method rely on the ethers.utils.verifyMessage() implementation. + * + * @param message The content of the signature to verify. It can be the result of GetVerificationBuffer() or directly a BaseMessage object. + * @param signature The signature associated with the first params of this method. + * @param signerAddress Optional, The address associated with the signature to verify. The current account address is used by default. + */ +function verifyEthereum(message: Buffer | BaseMessage, signature: string, signerAddress: string): boolean { + if (!(message instanceof Buffer)) message = GetVerificationBuffer(message); + + try { + const address = ethers.utils.verifyMessage(message, signature); + return address === signerAddress; + } catch (e: unknown) { + return false; + } +} + +export default verifyEthereum; diff --git a/src/utils/signature/verifySolana.ts b/src/utils/signature/verifySolana.ts new file mode 100644 index 00000000..19d4e321 --- /dev/null +++ b/src/utils/signature/verifySolana.ts @@ -0,0 +1,24 @@ +import { BaseMessage } from "../../messages/types"; +import { GetVerificationBuffer } from "../../messages"; +import nacl from "tweetnacl"; +import bs58 from "bs58"; + +/** + * Provide a way to verify the authenticity of a signature associated with a given message. + * This method rely on the nacl.sign.detached.verify() implementation. + * + * @param message The content of the signature to verify. It can be the result of GetVerificationBuffer() or directly a BaseMessage object. + * @param serializedSignature The signature associated with the first params of this method. + */ +function verifySolana(message: Buffer | BaseMessage, serializedSignature: string): boolean { + if (!(message instanceof Buffer)) message = GetVerificationBuffer(message); + const { signature, publicKey } = JSON.parse(serializedSignature); + + try { + return nacl.sign.detached.verify(message, bs58.decode(signature), bs58.decode(publicKey)); + } catch (e: unknown) { + return false; + } +} + +export default verifySolana; diff --git a/src/utils/signature/verifySubstrate.ts b/src/utils/signature/verifySubstrate.ts new file mode 100644 index 00000000..0620bec6 --- /dev/null +++ b/src/utils/signature/verifySubstrate.ts @@ -0,0 +1,26 @@ +import { BaseMessage } from "../../messages/types"; +import { GetVerificationBuffer } from "../../messages"; +import { signatureVerify } from "@polkadot/util-crypto"; + +/** + * Provide a way to verify the authenticity of a signature associated with a given message. + * This method rely on the signatureVerify() implementation from @polkadot/util-crypto. + * + * @param message The content of the signature to verify. It can be the result of GetVerificationBuffer() or directly a BaseMessage object. + * @param signature The signature associated with the first params of this method. + * @param signerAddress Optional, The address associated with the signature to verify. The current account address is used by default. + */ +function verifySubstrate(message: Buffer | BaseMessage, signature: string, signerAddress: string): boolean { + if (!(message instanceof Buffer)) message = GetVerificationBuffer(message); + const parsedSignature = JSON.parse(signature); + + try { + const result = signatureVerify(message, parsedSignature.data, signerAddress); + + return result.isValid; + } catch (e: unknown) { + return false; + } +} + +export default verifySubstrate; diff --git a/src/utils/signature/verifyTezos.ts b/src/utils/signature/verifyTezos.ts new file mode 100644 index 00000000..bb025275 --- /dev/null +++ b/src/utils/signature/verifyTezos.ts @@ -0,0 +1,29 @@ +import { BaseMessage } from "../../messages/types"; +import { GetVerificationBuffer } from "../../messages"; +import { char2Bytes, verifySignature } from "@taquito/utils"; + +/** + * Provide a way to verify the authenticity of a signature associated with a given message. + * This method rely on the verifySignature() implementation from taquito/utils. + * + * @param message The content of the signature to verify. It needs to be a BaseMessage object. + * @param signature The signature associated with the first params of this method. + */ +function verifyTezos(message: BaseMessage, signature: string): boolean { + const { signature: parsedSignature, publicKey, dAppUrl } = JSON.parse(signature); + + const buffer = GetVerificationBuffer(message); + const ISO8601formattedTimestamp = new Date(message.time).toISOString(); + const formattedInput: string = [ + "Tezos Signed Message:", + dAppUrl, + ISO8601formattedTimestamp, + buffer.toString(), + ].join(" "); + const bytes = char2Bytes(formattedInput); + const payloadBytes = "05" + "0100" + char2Bytes(String(bytes.length)) + bytes; + + return verifySignature(payloadBytes, publicKey, parsedSignature); +} + +export default verifyTezos; diff --git a/tests/accounts/avalanche.test.ts b/tests/accounts/avalanche.test.ts index 9744b0f7..a15a6903 100644 --- a/tests/accounts/avalanche.test.ts +++ b/tests/accounts/avalanche.test.ts @@ -1,5 +1,8 @@ import { avalanche, post } from "../index"; +import { ItemType, MessageType } from "../../src/messages/types"; import { EthereumProvider } from "../providers/ethereumProvider"; +import { GetVerificationBuffer } from "../../src/messages"; +import verifyAvalanche from "../index"; import { EphAccountList } from "../testAccount/entryPoint"; import fs from "fs"; @@ -177,4 +180,61 @@ describe("Avalanche accounts", () => { expect(amends.posts[0].content).toStrictEqual(content); }); }); + + it("Should success to verif the authenticity of a signature", async () => { + const { account } = await avalanche.NewAccount(); + + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + if (!account.publicKey) throw Error(); + const signature = await account.Sign(message); + const verif = await verifyAvalanche(GetVerificationBuffer(message), signature, account.publicKey); + const verifB = await verifyAvalanche(message, signature, account.publicKey); + + expect(verif).toStrictEqual(true); + expect(verifB).toStrictEqual(true); + }); + + it("Should fail to verif the authenticity of a signature", async () => { + const { account: account } = await avalanche.NewAccount(); + const { account: fakeAccount } = await avalanche.NewAccount(); + + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + const fakeMessage = { + ...message, + item_hash: "FAKE", + }; + if (!account.publicKey || !fakeAccount.publicKey) throw Error(); + const fakeSignature = await account.Sign(fakeMessage); + const verif = await verifyAvalanche(GetVerificationBuffer(message), fakeSignature, account.publicKey); + const verifB = await verifyAvalanche(fakeMessage, fakeSignature, fakeAccount.publicKey); + + expect(verif).toStrictEqual(false); + expect(verifB).toStrictEqual(false); + }); }); diff --git a/tests/accounts/ethereum.test.ts b/tests/accounts/ethereum.test.ts index 812f3d4d..54a02c09 100644 --- a/tests/accounts/ethereum.test.ts +++ b/tests/accounts/ethereum.test.ts @@ -5,6 +5,8 @@ import { EthereumProvider } from "../providers/ethereumProvider"; import { MessageType, ItemType } from "../../src/messages/types"; import { EphAccountList } from "../testAccount/entryPoint"; import fs from "fs"; +import { GetVerificationBuffer } from "../../src/messages"; +import verifyEthereum from "../index"; describe("Ethereum accounts", () => { let ephemeralAccount: EphAccountList; @@ -155,4 +157,60 @@ describe("Ethereum accounts", () => { expect(account.Sign(message)).toStrictEqual(accountFromPrivate.Sign(message)); expect(account.Sign(message)).toStrictEqual(accountFromProvider.Sign(message)); }); + + it("Should success to verif the authenticity of a signature", async () => { + const { account } = ethereum.NewAccount(); + + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + const signature = await account.Sign(message); + const verifA = await verifyEthereum(message, signature, account.address); + const verifB = await verifyEthereum(GetVerificationBuffer(message), signature, account.address); + + expect(verifA).toStrictEqual(true); + expect(verifB).toStrictEqual(true); + }); + + it("Should fail to verif the authenticity of a signature", async () => { + const { account } = ethereum.NewAccount(); + const fakeAccount = ethereum.NewAccount(); + + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + const fakeMessage = { + ...message, + item_hash: "FAKE", + }; + + const signature = await account.Sign(message); + const verif = await verifyEthereum(GetVerificationBuffer(fakeMessage), signature, account.address); + const verifB = await verifyEthereum(GetVerificationBuffer(message), signature, fakeAccount.account.address); + + expect(verif).toStrictEqual(false); + expect(verifB).toStrictEqual(false); + }); }); diff --git a/tests/accounts/solana.test.ts b/tests/accounts/solana.test.ts index 292f220d..fa3a83bb 100644 --- a/tests/accounts/solana.test.ts +++ b/tests/accounts/solana.test.ts @@ -2,6 +2,8 @@ import { ItemType, MessageType } from "../../src/messages/types"; import { post, solana } from "../index"; import { Keypair } from "@solana/web3.js"; import { panthomLikeProvider, officialLikeProvider } from "../providers/solanaProvider"; +import verifySolana from "../index"; +import { GetVerificationBuffer } from "../../src/messages"; import { EphAccountList } from "../testAccount/entryPoint"; import fs from "fs"; @@ -89,4 +91,56 @@ describe("Solana accounts", () => { expect(amends.posts[0].content).toStrictEqual(content); }); }); + + it("Should success to verif the authenticity of a signature", async () => { + const { account } = solana.NewAccount(); + + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + const signature = await account.Sign(message); + const verifA = verifySolana(message, signature); + const verifB = verifySolana(GetVerificationBuffer(message), signature); + + expect(verifA).toStrictEqual(true); + expect(verifB).toStrictEqual(true); + }); + + it("Should fail to verif the authenticity of a signature", async () => { + const { account } = solana.NewAccount(); + + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + const fakeMessage = { + ...message, + item_hash: "FAKE", + }; + const fakeSignature = await account.Sign(fakeMessage); + const verif = verifySolana(message, JSON.stringify({ signature: fakeSignature, publicKey: account.address })); + + expect(verif).toStrictEqual(false); + }); }); diff --git a/tests/accounts/tezos.test.ts b/tests/accounts/tezos.test.ts index f1ca0671..18dfa4d3 100644 --- a/tests/accounts/tezos.test.ts +++ b/tests/accounts/tezos.test.ts @@ -1,12 +1,13 @@ /** * @jest-environment jsdom */ -import { ItemType } from "../../src/messages/types"; +import { ItemType, MessageType } from "../../src/messages/types"; import { post, tezos } from "../index"; import { DEFAULT_API_V2 } from "../../src/global"; import { b58cencode, prefix, validateSignature } from "@taquito/utils"; import { EphAccountList } from "../testAccount/entryPoint"; import fs from "fs"; +import verifyTezos from "../../src/utils/signature/verifyTezos"; if (!window) { require("localstorage-polyfill"); @@ -86,4 +87,58 @@ describe("Tezos accounts", () => { expect(amends.posts[0].content).toStrictEqual(content); }); }); + + it("Should success to verif the authenticity of a signature", async () => { + const { signerAccount } = await tezos.NewAccount(); + + const message = { + chain: signerAccount.GetChain(), + sender: signerAccount.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: signerAccount.address, time: 15 }, + }; + const signature = await signerAccount.Sign(message); + const verifA = await verifyTezos(message, signature); + + expect(verifA).toStrictEqual(true); + }); + + it("Should fail to verif the authenticity of a signature", async () => { + const { signerAccount } = await tezos.NewAccount(); + + const message = { + chain: signerAccount.GetChain(), + sender: signerAccount.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: signerAccount.address, time: 15 }, + }; + const fakeMessage = { + ...message, + item_hash: "FAKE", + }; + const signature = await signerAccount.Sign(message); + const fakeSignature = await signerAccount.Sign(fakeMessage); + + const verifA = verifyTezos(fakeMessage, signature); + const verifB = verifyTezos(message, fakeSignature); + + expect(verifA).toStrictEqual(false); + expect(verifB).toStrictEqual(false); + }); }); diff --git a/tests/index.ts b/tests/index.ts index d76d7570..86b80f17 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -12,4 +12,24 @@ import * as forget from "../src/messages/forget/index"; import * as program from "../src/messages/program/index"; import * as any from "../src/messages/any/index"; -export { avalanche, cosmos, ethereum, nuls2, solana, tezos, aggregate, post, store, forget, program, any }; +import verifyEthereum from "../src/utils/signature/verifyEthereum"; +import verifySolana from "../src/utils/signature/verifySolana"; +import verifyAvalanche from "../src/utils/signature/verifyAvalanche"; + +export { + avalanche, + cosmos, + ethereum, + nuls2, + solana, + tezos, + aggregate, + post, + store, + forget, + program, + any, + verifyEthereum, + verifySolana, + verifyAvalanche, +}; diff --git a/tests/messages/aggregate/update.test.ts b/tests/messages/aggregate/update.test.ts index cbf0c45a..a2657388 100644 --- a/tests/messages/aggregate/update.test.ts +++ b/tests/messages/aggregate/update.test.ts @@ -51,7 +51,7 @@ describe("Aggregate message update test", () => { const message = await aggregate.Get({ APIServer: DEFAULT_API_V2, address: account.address, - keys: [key], + key: key, }); const expected = { @@ -118,7 +118,7 @@ describe("Aggregate message update test", () => { }; const message = await aggregate.Get({ address: owner.address, - keys: [key], + key: key, }); const expected = { diff --git a/testsAuto/accounts/substrate.auto.ts b/testsAuto/accounts/substrate.auto.ts index 5a4341e2..fab29081 100644 --- a/testsAuto/accounts/substrate.auto.ts +++ b/testsAuto/accounts/substrate.auto.ts @@ -4,8 +4,9 @@ import { mnemonicToMiniSecret } from "@polkadot/util-crypto"; import { testsFunc } from "../index"; import { accounts, messages } from "../../src"; -import { Chain } from "../../src/messages/types"; +import { Chain, ItemType, MessageType } from "../../src/messages/types"; import fs from "fs"; +import verifySubstrate from "../../src/utils/signature"; /** * This is the first test of the test bach for substrate. @@ -101,6 +102,67 @@ async function encryptNDecrypt(): Promise { return true; } +async function signatureVerif(): Promise { + const { account } = await accounts.substrate.NewAccount(); + const message = { + chain: account.GetChain(), + sender: account.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: account.address, time: 15 }, + }; + const signature = await account.Sign(message); + try { + const isValid = verifySubstrate(message, signature, account.address); + assert.strictEqual(isValid, true); + } catch (e: unknown) { + return false; + } + return true; +} + +async function falseSignatureVerif(): Promise { + const { account: accountA } = await accounts.substrate.NewAccount(); + const { account: accountB } = await accounts.substrate.NewAccount(); + const message = { + chain: accountA.GetChain(), + sender: accountA.address, + type: MessageType.post, + channel: "TEST", + confirmed: true, + signature: "signature", + size: 15, + time: 15, + item_type: ItemType.storage, + item_content: "content", + item_hash: "hash", + content: { address: accountA.address, time: 15 }, + }; + const fakeMessage = { + ...message, + item_hash: "FAKE", + }; + const signature = await accountA.Sign(message); + const fakeSignature = await accountB.Sign(fakeMessage); + + try { + const verifA = verifySubstrate(message, fakeSignature, accountA.address); + const verifB = verifySubstrate(message, signature, accountB.address); + assert.strictEqual(verifA, false); + assert.strictEqual(verifB, false); + } catch (e: unknown) { + return false; + } + return true; +} + /** * SubstrateTests controls the flow of your custom tests for substrate protocol. * Every test is represented by a function related to the `testsFunc` Type. @@ -120,6 +182,8 @@ export default async function substrateTests(): Promise { importAccountFromPrivateKeyTest, PublishAggregate, encryptNDecrypt, + signatureVerif, + falseSignatureVerif, ]; for (let i = 0; i < testBatch.length; i++) {