From 0db5a668ac880219eb23af11ef2e018b70f11c05 Mon Sep 17 00:00:00 2001 From: Sarah Schwartz <58856580+sarahschwartz@users.noreply.github.com> Date: Thu, 21 Nov 2024 03:56:58 -0700 Subject: [PATCH] docs: add ecrecover example (#256) adds an example for using `ecrecover` --- .../50.signature-validation.md | 95 ++++++++++++++++--- 1 file changed, 83 insertions(+), 12 deletions(-) diff --git a/content/00.build/65.developer-reference/40.account-abstraction/50.signature-validation.md b/content/00.build/65.developer-reference/40.account-abstraction/50.signature-validation.md index 373233fc..bbf78ec4 100644 --- a/content/00.build/65.developer-reference/40.account-abstraction/50.signature-validation.md +++ b/content/00.build/65.developer-reference/40.account-abstraction/50.signature-validation.md @@ -55,24 +55,76 @@ contract TestSignatureChecker { } ``` -## Verifying AA signatures +## Validating Signatures with ECRecover -Our SDK provides two methods within `utils` to verify the signature of an account: -[`isMessageSignatureCorrect`](https://sdk.zksync.io/js/ethers/api/v6/utilities#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect`](https://sdk.zksync.io/js/ethers/api/v6/utilities#istypeddatasignaturecorrect). +The [`ecrecover`](/zk-stack/components/prover/circuits/ecrecover) +method is also available to validate standard secp256k1 signatures. +This method recovers the signer’s public key from a given digital signature. -```ts -import { utils, EIP712Signer } from "zksync-ethers"; +Below is an example implementation of how `ecrecover` can be used to validate a given signature in a smart account. -const isValidMessageSignature = await utils.isMessageSignatureCorrect(provider, ADDRESS, message, messageSignature); +```solidity +function isValidSignature( + bytes32 _hash, + bytes memory _signature +) public view override returns (bytes4 magic) { + magic = EIP1271_SUCCESS_RETURN_VALUE; + + if (_signature.length != 65) { + // Signature is invalid anyway, but we need to proceed with the signature verification as usual + // in order for the fee estimation to work correctly + _signature = new bytes(65); + + // Making sure that the signatures look like a valid ECDSA signature and are not rejected rightaway + // while skipping the main verification process. + _signature[64] = bytes1(uint8(27)); + } -const isValidTypesSignature = await utils.isTypedDataSignatureCorrect(provider, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), typedSignature); -``` + // extract ECDSA signature + uint8 v; + bytes32 r; + bytes32 s; + // Signature loading code + // we jump 32 (0x20) as the first slot of bytes contains the length + // we jump 65 (0x41) per signature + // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask + assembly { + r := mload(add(_signature, 0x20)) + s := mload(add(_signature, 0x40)) + v := and(mload(add(_signature, 0x41)), 0xff) + } -Currently these methods only support verifying ECDSA signatures, but very soon they will support EIP1271 signature verification as well. + if (v != 27 && v != 28) { + magic = bytes4(0); + } -Both of these methods return `true` or `false` depending on whether the message signature is correct. + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if ( + uint256(s) > + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + ) { + magic = bytes4(0); + } -It is **not recommended** to use the `ethers.js` library to verify user signatures. + address recoveredAddress = ecrecover(_hash, v, r, s); + + // Note, that we should abstain from using the require here in order to allow for fee estimation to work + if (recoveredAddress != owner) { + magic = bytes4(0); + } + if(recoveredAddress == address(0)){ + magic = bytes4(0); + } +} +``` ## Validating Secp256r1 Signatures @@ -99,10 +151,29 @@ bytes memory input = abi.encodePacked( (bool __, bytes memory output) = P256.staticcall(input); // if signature is valid: -// output = 0x0000000000000000000000000000000000000000000000000000000000000001 +// output == 0x0000000000000000000000000000000000000000000000000000000000000001 // if signature is NOT valid: // output.length == 0 ``` You can find a more in-depth example showing how it can be used in the ["Signing Transactions with WebAuthn"](https://code.zksync.io/tutorials/signing-transactions-with-webauthn) tutorial. + +## Offchain Signature Verification + +The `zksync-ethers` SDK provides two methods within `utils` to verify standard signatures of an account: +[`isMessageSignatureCorrect`](https://sdk.zksync.io/js/ethers/api/v6/utilities#ismessagesignaturecorrect) and [`isTypedDataSignatureCorrect`](https://sdk.zksync.io/js/ethers/api/v6/utilities#istypeddatasignaturecorrect). + +```ts +import { utils, EIP712Signer } from "zksync-ethers"; + +const isValidMessageSignature = await utils.isMessageSignatureCorrect(provider, ADDRESS, message, messageSignature); + +const isValidTypesSignature = await utils.isTypedDataSignatureCorrect(provider, ADDRESS, await eip712Signer.getDomain(), utils.EIP712_TYPES, EIP712Signer.getSignInput(tx), typedSignature); +``` + +Currently these methods only support verifying ECDSA and EIP1271 signatures. + +Both of these methods return `true` or `false` depending on whether the message signature is correct. + +It is **not recommended** to use the `ethers.js` library to verify user signatures, as it does not support verifying EIP1271 signatures.