diff --git a/apps/taquito-test-dapp/package.json b/apps/taquito-test-dapp/package.json index 4b18bc020d..10254b7da6 100644 --- a/apps/taquito-test-dapp/package.json +++ b/apps/taquito-test-dapp/package.json @@ -1,5 +1,5 @@ { - "name": "taquito-test-dapp-vite", + "name": "taquito-test-dapp", "private": true, "version": "17.1.1", "type": "module", diff --git a/apps/taquito-test-dapp/src/lib/TestContainer.svelte b/apps/taquito-test-dapp/src/lib/TestContainer.svelte index d098362361..fdeb8aa814 100644 --- a/apps/taquito-test-dapp/src/lib/TestContainer.svelte +++ b/apps/taquito-test-dapp/src/lib/TestContainer.svelte @@ -25,6 +25,7 @@ if ( test.id === "sign-payload" || test.id === "sign-payload-and-send" || + test.id === "sign-failingNoop" || test.id === "verify-signature" || test.id === "set-transaction-limits" ) { diff --git a/apps/taquito-test-dapp/src/tests.ts b/apps/taquito-test-dapp/src/tests.ts index 6e61a20513..e769e69ad0 100644 --- a/apps/taquito-test-dapp/src/tests.ts +++ b/apps/taquito-test-dapp/src/tests.ts @@ -316,6 +316,30 @@ const signPayloadAndSend = async ( } }; +const signFailingNoop = async ( + input: string, + tezos: TezosToolkit, + wallet: BeaconWallet, +): Promise => { + const bytes = char2Bytes(input); + try { + const signedPayload = await tezos.wallet.signFailingNoop({ + arbitrary: bytes, + basedOnBlock: 'head' + }); + + return { + success: true, + opHash: "", + output: signedPayload.prefixSig, + sigDetails: { input, bytes: signedPayload.bytes, formattedInput: input } + }; + } catch (error) { + console.log(error); + return { success: false, opHash: "", output: JSON.stringify(error) }; + } +}; + const verifySignatureWithTaquito = async ( input: string, wallet: BeaconWallet, @@ -535,6 +559,7 @@ export const list = [ "Use the Batch API for contract calls", "Sign the provided payload", "Sign and send the signature to the contract", + "Sign the provided payload in a failing noop", "Verify a provided signature", "Set the transaction limits", "Subscribe to confirmations", @@ -678,6 +703,18 @@ export const init = ( inputType: "string", lastResult: { option: "none", val: false } }, + { + id: "sign-failingNoop", + name: "Sign the provided payload in a failing noop", + description: "This test signs the payload provided by the user wrapped in a failing noop", + documentation: 'https://tezostaquito.io/docs/signing/#generating-a-signature-with-beacon-sdk', + keyword: 'failingNoop', + run: input => signFailingNoop(input.text, Tezos, wallet), + showExecutionTime: false, + inputRequired: true, + inputType: "string", + lastResult: { option: "none", val: false } + }, { id: "verify-signature", name: "Verify a provided signature", diff --git a/package-lock.json b/package-lock.json index 5bb55e1c16..966cf570dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ } }, "apps/taquito-test-dapp": { - "name": "taquito-test-dapp-vite", + "name": "taquito-test-dapp", "version": "17.1.1", "dependencies": { "@airgap/beacon-sdk": "4.0.2", @@ -24711,7 +24711,7 @@ "node": ">=6" } }, - "node_modules/taquito-test-dapp-vite": { + "node_modules/taquito-test-dapp": { "resolved": "apps/taquito-test-dapp", "link": true }, @@ -48094,7 +48094,7 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, - "taquito-test-dapp-vite": { + "taquito-test-dapp": { "version": "file:apps/taquito-test-dapp", "requires": { "@airgap/beacon-sdk": "4.0.2", diff --git a/package.json b/package.json index 02e2ddb36a..17f08f91b3 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,11 @@ "integration-tests" ], "scripts": { - "build": "nx run-many --target=build --exclude=@taquito/website,taquito-test-dapp-vite", - "build-test-dapp": "lerna run build --scope=taquito-test-dapp-vite", + "build": "nx run-many --target=build --exclude=@taquito/website,taquito-test-dapp", + "build-all": "nx run-many --target=build --exclude=@taquito/website", + "build-test-dapp": "lerna run build --scope=taquito-test-dapp", "rebuild": "npm run clean && npm clean-install && npm run build", - "test": "nx run-many --target=test --exclude=integration-tests,@taquito/website,taquito-test-dapp-vite --collectCoverage", + "test": "nx run-many --target=test --exclude=integration-tests,@taquito/website,taquito-test-dapp --collectCoverage", "lint": "lerna run lint", "clean": "rm -rf ./node_modules/* && lerna clean --yes", "commit": "git-cz", diff --git a/packages/taquito-beacon-wallet/src/taquito-beacon-wallet.ts b/packages/taquito-beacon-wallet/src/taquito-beacon-wallet.ts index c7349f7fcf..9d0ac36fb2 100644 --- a/packages/taquito-beacon-wallet/src/taquito-beacon-wallet.ts +++ b/packages/taquito-beacon-wallet/src/taquito-beacon-wallet.ts @@ -25,6 +25,19 @@ import { WalletProvider, WalletTransferParams, } from '@taquito/taquito'; +import { + InvalidSignatureError, + ValidationResult, + b58cdecode, + b58cencode, + buf2hex, + invalidDetail, + isValidPrefix, + prefix, + verifySignature, +} from '@taquito/utils'; +import { SignatureVerificationError } from '@taquito/core'; +import toBuffer from 'typedarray-to-buffer'; export { VERSION } from './version'; export { BeaconWalletNotInitialized, MissingRequiredScopes } from './errors'; @@ -199,11 +212,36 @@ export class BeaconWallet implements WalletProvider { } async sign(signingRequest: { payload: string }) { - const response = await this.client.requestSignPayload({ - payload: signingRequest.payload, + const watermarkedBytes = '03' + signingRequest.payload; + const { signature } = await this.client.requestSignPayload({ + payload: watermarkedBytes, signingType: SigningType.OPERATION, }); - return response.signature; + const pref = signature.startsWith('sig') + ? signature.substring(0, 3) + : signature.substring(0, 5); + + if (!isValidPrefix(pref)) { + throw new InvalidSignatureError( + signature, + invalidDetail(ValidationResult.NO_PREFIX_MATCHED) + ` from the wallet.` + ); + } + + const decoded = b58cdecode(signature, prefix[pref]); + + const pk = await this.getPublicKey(); + const signatureVerified = verifySignature(watermarkedBytes, pk, signature); + if (!signatureVerified) { + throw new SignatureVerificationError(watermarkedBytes, signature); + } + + return { + bytes: signingRequest.payload, + sig: b58cencode(decoded, prefix.sig), + prefixSig: signature, + sbytes: signingRequest.payload + buf2hex(toBuffer(decoded)), + }; } async getPublicKey(): Promise { diff --git a/packages/taquito-core/src/errors.ts b/packages/taquito-core/src/errors.ts index 49b5aca469..4b6e0d6ef0 100644 --- a/packages/taquito-core/src/errors.ts +++ b/packages/taquito-core/src/errors.ts @@ -281,3 +281,16 @@ export class PublicKeyNotFoundError extends TaquitoError { this.message = `Public key not found of this address "${pkh}" in either wallet or contract API.`; } } + +/** + * @category Error + * @description Error + */ +export class SignatureVerificationError extends TaquitoError { + public name = 'SignatureVerificationFailedError'; + constructor(public readonly bytes: string, public readonly signature: string) { + super(); + this.name = 'SignatureVerificationFailedError'; + this.message = `Invalid signature of bytes failed verification agaisnt public key.`; + } +} diff --git a/packages/taquito-remote-signer/src/errors.ts b/packages/taquito-remote-signer/src/errors.ts index 05da9c5483..e85f40e761 100644 --- a/packages/taquito-remote-signer/src/errors.ts +++ b/packages/taquito-remote-signer/src/errors.ts @@ -44,16 +44,3 @@ export class PublicKeyVerificationError extends TaquitoError { this.message = `Requested pk "${requestedPk}" has pkh "${requestedPkh}" deesn't match initialized pkh "${initializedPkh}."`; } } - -/** - * @category Error - * @description Error - */ -export class SignatureVerificationError extends TaquitoError { - public name = 'SignatureVerificationFailedError'; - constructor(public readonly bytes: string, public readonly signature: string) { - super(); - this.name = 'SignatureVerificationFailedError'; - this.message = `Invalid signature of bytes failed verification agaisnt public key.`; - } -} diff --git a/packages/taquito-remote-signer/src/taquito-remote-signer.ts b/packages/taquito-remote-signer/src/taquito-remote-signer.ts index 0aab95d7c3..2b4fc1c119 100644 --- a/packages/taquito-remote-signer/src/taquito-remote-signer.ts +++ b/packages/taquito-remote-signer/src/taquito-remote-signer.ts @@ -22,7 +22,6 @@ import { BadSigningDataError, OperationNotAuthorizedError, PublicKeyVerificationError, - SignatureVerificationError, } from './errors'; import { Signer } from '@taquito/taquito'; import { @@ -30,6 +29,7 @@ import { InvalidKeyHashError, ProhibitedActionError, PublicKeyNotFoundError, + SignatureVerificationError, } from '@taquito/core'; interface PublicKeyResponse { diff --git a/packages/taquito/src/wallet/interface.ts b/packages/taquito/src/wallet/interface.ts index 1a946e2bf6..2739ae49f4 100644 --- a/packages/taquito/src/wallet/interface.ts +++ b/packages/taquito/src/wallet/interface.ts @@ -64,7 +64,12 @@ export interface WalletProvider { /** * @description Request the wallet to sign a payload */ - sign(signingRequest: { payload: string }): Promise; + sign(signingRequest: { payload: string }): Promise<{ + bytes: string; + sig: string; + prefixSig: string; + sbytes: string; + }>; /** * @description Get the public key from the wallet diff --git a/packages/taquito/src/wallet/legacy.ts b/packages/taquito/src/wallet/legacy.ts index 05bb90c122..0c99b9eff4 100644 --- a/packages/taquito/src/wallet/legacy.ts +++ b/packages/taquito/src/wallet/legacy.ts @@ -42,12 +42,11 @@ export class LegacyWalletProvider implements WalletProvider { return op.hash; } - async sign(signingRequest: { payload: string }): Promise { - const response = await this.context.signer.sign(signingRequest.payload, new Uint8Array([3])); - return response.sbytes; + sign(signingRequest: { payload: string }) { + return this.context.signer.sign(signingRequest.payload, new Uint8Array([3])); } - getPublicKey(): Promise { + getPublicKey() { return this.context.signer.publicKey(); } } diff --git a/packages/taquito/src/wallet/wallet.ts b/packages/taquito/src/wallet/wallet.ts index ab82751dda..1b54089077 100644 --- a/packages/taquito/src/wallet/wallet.ts +++ b/packages/taquito/src/wallet/wallet.ts @@ -275,11 +275,7 @@ export class Wallet { (await this.context.readProvider.getNextProtocol('head')); const forger = new LocalForger(protocolHash); const forgedBytes = await forger.forge(forgeable); - const signature = await this.walletProvider.sign({ payload: forgedBytes }); - return { - bytes: forgedBytes, - sbytes: signature, - }; + return await this.walletProvider.sign({ payload: forgedBytes }); } /**