diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index 079b8b5d7..9f7056a67 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -522,7 +522,7 @@ export class Account { // Apply ERC-6492 to undeployed children const signature = await this.signDigest(digest, 0, false, 'ignore', {chainId, referenceChainId, cantValidateBehavior: "eip6492"}) const decoded = this.coders.signature.decode(signature) - const recovered = await this.coders.signature.recover(decoded, { digest, chainId, address: this.address }) + const recovered = await this.coders.signature.recover(decoded, { digest, chainId, address: this.address }, undefined, 'ignore') const signatures = this.coders.signature.signaturesOf(recovered.config) const signaturesWithReferenceChainId = signatures.map(s => ({...s, referenceChainId})) return this.tracker.saveWitnesses({ wallet: this.address, digest, chainId, signatures: signaturesWithReferenceChainId }) @@ -669,8 +669,7 @@ export class Account { await this.tracker.savePresignedConfiguration({ wallet: this.address, nextConfig: config, - signature, - validateBehavior: 'ignore' + signature }) // safety check, tracker should have a reverse lookup for the imageHash diff --git a/packages/core/src/commons/signature.ts b/packages/core/src/commons/signature.ts index bb1f1b83a..3cdd38f88 100644 --- a/packages/core/src/commons/signature.ts +++ b/packages/core/src/commons/signature.ts @@ -35,7 +35,7 @@ export interface SignatureCoder< trim: (data: string) => Promise - recover: (data: Z, payload: SignedPayload, provider?: ethers.Provider) => Promise + recover: (data: Z, payload: SignedPayload, provider?: ethers.Provider, validateBehavior?: 'ignore' | 'throw') => Promise supportsNoChainId: boolean diff --git a/packages/core/src/commons/signer.ts b/packages/core/src/commons/signer.ts index 88e37a743..c570d77f4 100644 --- a/packages/core/src/commons/signer.ts +++ b/packages/core/src/commons/signer.ts @@ -43,7 +43,7 @@ export function isValidSignature( address: string, digest: ethers.BytesLike, signature: ethers.BytesLike, - provider?: ethers.Provider + provider: ethers.Provider ) { const bytes = ethers.getBytes(signature) @@ -55,9 +55,6 @@ export function isValidSignature( } if (type === SigType.WALLET_BYTES32) { - if (!provider) { - throw new Error('Provider is required to validate EIP1271 signatures') - } return isValidEIP1271Signature(address, ethers.hexlify(digest), bytes.slice(0, -1), provider) } diff --git a/packages/core/src/v1/signature.ts b/packages/core/src/v1/signature.ts index 022d52af1..14676fe7f 100644 --- a/packages/core/src/v1/signature.ts +++ b/packages/core/src/v1/signature.ts @@ -125,7 +125,8 @@ export function encodeSignature(signature: Signature | UnrecoveredSignature | et export async function recoverSignature( data: UnrecoveredSignature, payload: base.SignedPayload, - provider?: ethers.Provider + provider?: ethers.Provider, + validateBehavior: 'ignore' | 'throw' = 'throw' ): Promise { const subdigest = base.subdigestOf(payload) const signers = await Promise.all( @@ -136,8 +137,15 @@ export async function recoverSignature( if (s.isDynamic) { if (!s.address) throw new Error('Dynamic signature part must have address') - if (!isValidSignature(s.address, subdigest, s.signature, provider)) { - throw new Error(`Invalid dynamic signature part ${s.address}`) + + if (validateBehavior !== 'ignore') { + if (!provider) { + throw new Error('Provider is required to validate EIP1271 signatures') + } + + if (!isValidSignature(s.address, subdigest, s.signature, provider)) { + throw new Error(`Invalid dynamic signature part ${s.address}`) + } } return { address: s.address, weight: s.weight, signature: s.signature } @@ -216,8 +224,13 @@ export const SignatureCoder: base.SignatureCoder => { - return recoverSignature(data, payload, provider) + recover: ( + data: UnrecoveredSignature, + payload: base.SignedPayload, + provider?: ethers.Provider, + validateBehavior: 'ignore' | 'throw' = 'throw' + ): Promise => { + return recoverSignature(data, payload, provider, validateBehavior) }, encodeSigners: ( diff --git a/packages/core/src/v2/signature.ts b/packages/core/src/v2/signature.ts index 196e23944..dc6c98c6a 100644 --- a/packages/core/src/v2/signature.ts +++ b/packages/core/src/v2/signature.ts @@ -223,11 +223,11 @@ export class InvalidSignatureLeafError extends Error { } } -// Signature validity is only checked if provider is provided export async function recoverTopology( unrecovered: UnrecoveredTopology, subdigest: string, - provider?: ethers.Provider + provider?: ethers.Provider, + validateBehavior: 'ignore' | 'throw' = 'throw' ): Promise { if (isUnrecoveredNode(unrecovered)) { const [left, right] = await Promise.all([ @@ -252,7 +252,11 @@ export async function recoverTopology( throw new Error('Dynamic signature leaf without address') } - if (provider) { + if (validateBehavior !== 'ignore') { + if (!provider) { + throw new Error('Provider is required to validate EIP1271 signatures') + } + const isValid = await isValidSignature(unrecovered.address, subdigest, unrecovered.signature, provider) if (!isValid) { throw new InvalidSignatureLeafError(unrecovered) @@ -601,7 +605,8 @@ export function setImageHashStruct(imageHash: string) { export async function recoverSignature( signature: UnrecoveredSignature | UnrecoveredChainedSignature, payload: base.SignedPayload | { subdigest: string }, - provider?: ethers.Provider + provider?: ethers.Provider, + validateBehavior: 'ignore' | 'throw' = 'throw' ): Promise { const signedPayload = (payload as { subdigest: string }).subdigest === undefined ? (payload as base.SignedPayload) : undefined @@ -613,7 +618,7 @@ export async function recoverSignature( const subdigest = signedPayload ? base.subdigestOf(signedPayload) : (payload as { subdigest: string }).subdigest if (!isUnrecoveredChainedSignature(signature)) { - const tree = await recoverTopology(signature.decoded.tree, subdigest, provider) + const tree = await recoverTopology(signature.decoded.tree, subdigest, provider, validateBehavior) return { version: 2, type: signature.type, subdigest, config: { version: 2, ...signature.decoded, tree } } } @@ -628,7 +633,7 @@ export async function recoverSignature( // NOTICE: Remove the suffix from the "first" siganture // otherwise we recurse infinitely for (const sig of [{ ...signature, suffix: undefined }, ...signature.suffix]) { - const recovered = await recoverSignature(sig, mutatedPayload, provider) + const recovered = await recoverSignature(sig, mutatedPayload, provider, validateBehavior) result.unshift(recovered) const nextMessage = setImageHashStruct(imageHash(deepestConfigOfSignature(recovered))) @@ -930,9 +935,10 @@ export const SignatureCoder: base.SignatureCoder => { - return recoverSignature(data, payload, provider) + return recoverSignature(data, payload, provider, validateBehavior) }, encodeSigners: ( diff --git a/packages/sessions/src/tracker.ts b/packages/sessions/src/tracker.ts index 7d0803e7c..95cef98d9 100644 --- a/packages/sessions/src/tracker.ts +++ b/packages/sessions/src/tracker.ts @@ -6,7 +6,6 @@ export type PresignedConfig = { nextConfig: commons.config.Config signature: string referenceChainId?: string - validateBehavior?: 'ignore' | 'throw' } export type PresignedConfigLink = Omit & { nextImageHash: string } diff --git a/packages/sessions/src/trackers/local.ts b/packages/sessions/src/trackers/local.ts index 12fa27c84..32f0dd464 100644 --- a/packages/sessions/src/trackers/local.ts +++ b/packages/sessions/src/trackers/local.ts @@ -235,11 +235,11 @@ export class LocalConfigTracker implements ConfigTracker, migrator.PresignedMigr const savePayload = this.savePayload({ payload }) const saveNextConfig = this.saveWalletConfig({ config: args.nextConfig }) - const validateBehavior = args.validateBehavior ?? 'throw' const recovered = await v2.signature.SignatureCoder.recover( decoded, payload, - validateBehavior === 'ignore' ? undefined : this.provider // Only validate if we are not ignoring + this.provider, + 'ignore' ) // Save the recovered configuration and all signature parts