From 9474d5470886dae502806b4a9841698d2c152ea1 Mon Sep 17 00:00:00 2001 From: "Fifo (Fabricius Zatti)" <62725221+fazzatti@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:32:09 -0300 Subject: [PATCH] Feat verify signature (#156) * feat: add signData feature to default account handler * feat: add verifySignature feature to base account handler * test: add unit test to signData * test: add unit test to verifySignature --- .../account/account-handler/default/errors.ts | 14 +++++++++++ .../account/account-handler/default/index.ts | 14 +++++++++++ .../default/index.unit.test.ts | 19 +++++++++++++++ src/stellar-plus/account/base/index.ts | 13 ++++++++++- .../account/base/index.unit.test.ts | 23 +++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/stellar-plus/account/account-handler/default/errors.ts b/src/stellar-plus/account/account-handler/default/errors.ts index d6396647..4049482d 100644 --- a/src/stellar-plus/account/account-handler/default/errors.ts +++ b/src/stellar-plus/account/account-handler/default/errors.ts @@ -5,6 +5,7 @@ export enum DefaultAccountHandlerErrorCodes { DAH001 = 'DAH001', DAH002 = 'DAH002', DAH003 = 'DAH003', + DAH004 = 'DAH004', } const failedToLoadSecretKeyError = (error?: Error): StellarPlusError => { @@ -55,8 +56,21 @@ const failedToSignAuthorizationEntryError = ( }) } +const failedToSignDataError = (error?: Error): StellarPlusError => { + return new StellarPlusError({ + code: DefaultAccountHandlerErrorCodes.DAH004, + message: 'Failed to sign data!', + source: 'DefaultAccountHandler', + details: 'The data could not be signed. Review the secret key.', + meta: { + error, + }, + }) +} + export const DAHError = { failedToLoadSecretKeyError, failedToSignTransactionError, failedToSignAuthorizationEntryError, + failedToSignDataError, } diff --git a/src/stellar-plus/account/account-handler/default/index.ts b/src/stellar-plus/account/account-handler/default/index.ts index 0d49b7bd..9e2ae9f4 100644 --- a/src/stellar-plus/account/account-handler/default/index.ts +++ b/src/stellar-plus/account/account-handler/default/index.ts @@ -104,4 +104,18 @@ export class DefaultAccountHandlerClient extends AccountBase implements DefaultA ) } } + + /** + * + * @param {Buffer} data - The data to sign. + * @returns {Buffer} The signature of the data. + */ + public signData(data: Buffer): Buffer { + try { + const keypair = Keypair.fromSecret(this.secretKey) + return keypair.sign(data) + } catch (e) { + throw DAHError.failedToSignDataError(e as Error) + } + } } diff --git a/src/stellar-plus/account/account-handler/default/index.unit.test.ts b/src/stellar-plus/account/account-handler/default/index.unit.test.ts index d06b29d4..21322d38 100644 --- a/src/stellar-plus/account/account-handler/default/index.unit.test.ts +++ b/src/stellar-plus/account/account-handler/default/index.unit.test.ts @@ -102,6 +102,16 @@ describe('DefaultAccountHandler', () => { TESTNET_CONFIG.networkPassphrase ) }) + + it('should sign data with its secret key', () => { + const keypair = Keypair.random() + const dah = new DefaultAccountHandlerClient({ networkConfig: TESTNET_CONFIG, secretKey: keypair.secret() }) + const data = Buffer.from('Mocked Data') + + const signature = dah.signData(data) + + expect(dah.verifySignature(data, signature)).toBe(true) + }) }) describe('Getters', () => { @@ -176,5 +186,14 @@ describe('DefaultAccountHandler', () => { ) ) }) + + it('should throw an error if data cannot be signed', () => { + const keypair = Keypair.random() + const dah = new DefaultAccountHandlerClient({ networkConfig: TESTNET_CONFIG, secretKey: keypair.secret() }) + + expect(() => dah.signData(null as unknown as Buffer)).toThrow( + DAHError.failedToSignDataError(new Error('Mocked error')) + ) + }) }) }) diff --git a/src/stellar-plus/account/base/index.ts b/src/stellar-plus/account/base/index.ts index 8a46a902..0f0b91d9 100644 --- a/src/stellar-plus/account/base/index.ts +++ b/src/stellar-plus/account/base/index.ts @@ -1,4 +1,4 @@ -import { Horizon } from '@stellar/stellar-sdk' +import { Horizon, Keypair } from '@stellar/stellar-sdk' import axios from 'axios' import { ABError } from 'stellar-plus/account/base/errors' @@ -82,6 +82,17 @@ export class AccountBase implements AccountBaseType { } } + /** + * + * @param {Buffer} data - The data to sign. + * @param {Buffer} signature - The signature to verify. + * @returns {boolean} True if the signature is valid, false otherwise. + */ + public verifySignature(data: Buffer, signature: Buffer): boolean { + const keypair = Keypair.fromPublicKey(this.publicKey) + return keypair.verify(data, signature) + } + /** * * @description - Throws an error if the network is not a test network. diff --git a/src/stellar-plus/account/base/index.unit.test.ts b/src/stellar-plus/account/base/index.unit.test.ts index 9dddb9f8..f6eafba7 100644 --- a/src/stellar-plus/account/base/index.unit.test.ts +++ b/src/stellar-plus/account/base/index.unit.test.ts @@ -7,6 +7,8 @@ import { ABError } from 'stellar-plus/account/base/errors' import { AxiosErrorTypes } from 'stellar-plus/error/helpers/axios' import { HorizonHandler } from 'stellar-plus/horizon/types' import { TestNet } from 'stellar-plus/network' +import { DefaultAccountHandlerClient } from '../account-handler/default' +import { Keypair } from '@stellar/stellar-sdk' jest.mock('axios', () => { const originalModule = jest.requireActual('axios') @@ -94,6 +96,27 @@ describe('Base Account Handler', () => { expect(balances).toBe(mockedBalances) expect(mockedLoadAccount).toHaveBeenCalledExactlyOnceWith(MOCKED_PK) }) + + it('verify if a signature is valid', () => { + const keypair = Keypair.random() + const dah = new DefaultAccountHandlerClient({ networkConfig: TESTNET_CONFIG, secretKey: keypair.secret() }) + const data = Buffer.from('Mocked Data') + + const signature = dah.signData(data) + + expect(dah.verifySignature(data, signature)).toBe(true) + }) + + it('verify if a signature is not valid', () => { + const keypair = Keypair.random() + const dah = new DefaultAccountHandlerClient({ networkConfig: TESTNET_CONFIG, secretKey: keypair.secret() }) + const secondDah = new DefaultAccountHandlerClient({ networkConfig: TESTNET_CONFIG }) + const data = Buffer.from('Mocked Data') + + const signature = dah.signData(data) + + expect(secondDah.verifySignature(data, signature)).toBe(false) + }) }) describe('Error Handling', () => {