Skip to content

bug: Normalize v in KMS signature#581

Merged
cbrit merged 6 commits intomainfrom
collin/remote-signer-v
Apr 1, 2025
Merged

bug: Normalize v in KMS signature#581
cbrit merged 6 commits intomainfrom
collin/remote-signer-v

Conversation

@cbrit
Copy link
Copy Markdown
Member

@cbrit cbrit commented Apr 1, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Improved the signature verification process to enhance reliability and error handling during secure transactions, ensuring smoother operations when using cloud-based signing services.
  • New Features

    • Introduced a method to normalize signatures for improved validation, specifically for cloud-based signing services.
    • Updated the message signing process to enhance compatibility with the normalization method.
    • Enhanced the signing methods to incorporate the new normalization logic for better signature handling.

@cbrit cbrit requested a review from zmanian as a code owner April 1, 2025 19:44
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2025

Walkthrough

The changes introduce a new method, normalize, to the SignerType enum in the Ethereum module. This method normalizes signatures for the GcpKms variant by adjusting the v value based on address recovery attempts. If the recovered address matches the expected address, the modified signature is returned; otherwise, an error is returned. For other variants, the original signature is returned unchanged. The sign_message, sign_transaction, and sign_typed_data methods have been updated to call the new normalize method after obtaining the signature.

Changes

File(s) Change Summary
orchestrator/.../types.rs Added new normalize method in SignerType for normalizing signatures, specifically handling the GcpKms variant with address recovery logic. Updated sign_message, sign_transaction, and sign_typed_data methods to include internal changes and call normalize.

Sequence Diagram(s)

sequenceDiagram
    participant C as Caller
    participant S as SignerType
    participant R as Recovery Logic
    C->>S: Call normalize(message, sig)
    alt SignerType is not GcpKms
      S-->>C: Return cloned signature
    else SignerType is GcpKms
      S->>S: Set sig.v = 0
      S->>R: Attempt recovery (v=0)
      alt Recovery successful
         R-->>S: Address matches expected
         S-->>C: Return normalized signature with v = 0
      else Recovery failed
         S->>S: Set sig.v = 1
         S->>R: Attempt recovery (v=1)
         alt Recovery successful
            R-->>S: Address matches expected
            S-->>C: Return normalized signature with v = 1
         else Recovery fails
            S-->>C: Return ProviderError
         end
      end
    end
Loading
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

sig: &Signature,
) -> Result<Signature, ethers::types::SignatureError> {
match self {
// Since GCP KMS doesn't produce signatures normalized for EVM, we need to correct v
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/

Suggested change
// Since GCP KMS doesn't produce signatures normalized for EVM, we need to correct v
// Gravity does not implement eip155 modifications to v so we need to recompute v to the allowed range of 0 or 1

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
orchestrator/gravity/src/ethereum/types.rs (1)

18-55: Implementation looks good but could use minor optimizations.

The set_v method correctly handles the normalization of the v value for GCP KMS signatures. The approach of trying both possible v values (0 and 1) is appropriate for EVM signature recovery.

A few suggestions:

  1. Consider using == instead of cmp(...) == Ordering::Equal for address comparison
  2. The error only includes the address recovered with v=0, but it might be helpful to also include the attempt with v=1
                let sig0_address = sig.recover(message)?;

-                if sig0_address.cmp(&expected_address) == Ordering::Equal {
+                if sig0_address == expected_address {
                    return Ok(sig);
                }

                sig.v = 1;

                let sig1_address = sig.recover(message)?;

-                if sig1_address.cmp(&expected_address) == Ordering::Equal {
+                if sig1_address == expected_address {
                    return Ok(sig);
                }

                Err(ethers::types::SignatureError::VerificationError(
                    expected_address,
                    sig0_address,
                ))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86a3d57 and 08e81ad.

📒 Files selected for processing (1)
  • orchestrator/gravity/src/ethereum/types.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: rust-test
  • GitHub Check: rust-build
  • GitHub Check: hardhat-build
  • GitHub Check: Clippy
  • GitHub Check: node-build (16.x)
  • GitHub Check: local-tests (16.x)
  • GitHub Check: go-build
🔇 Additional comments (3)
orchestrator/gravity/src/ethereum/types.rs (3)

6-6: Clean import addition for address comparison.

The import for Ordering from std::cmp is appropriate for the new address comparison logic.


26-50: Excellent handling of GCP KMS signature normalization.

The implementation correctly addresses the issue where GCP KMS doesn't produce signatures normalized for EVM. The method systematically:

  1. Tries with v=0
  2. If that fails, tries with v=1
  3. Returns an appropriate error if both fail

This is a robust solution to normalize the ECDSA recovery ID for Ethereum transactions.


51-53: Good default case handling.

The default case correctly handles other signer types by simply cloning the signature without modification, which is appropriate since the comment indicates that LocalWallet doesn't need v correction.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
orchestrator/gravity/src/ethereum/types.rs (2)

6-6: Consider using direct equality checks instead of Ordering.

Since we only need to verify equality (rather than a detailed ordering), a direct if a == b { ... } comparison is more idiomatic and slightly clearer than using cmp(...) == Ordering::Equal.


18-55: Overall Implementation Looks Good, But Simplify Equality Checks and Error Messaging.

  1. Use direct equality checks
    For lines 34 and 42, consider:

    -if sig0_address.cmp(&expected_address) == Ordering::Equal {
    +if sig0_address == expected_address {

    This approach is more explicit and avoids confusion about partial ordering semantics.

  2. Enrich verification error
    Since this logic attempts two v values, it might be helpful to clarify in the error message which attempts failed. For instance, logging that v=0 and v=1 both yielded addresses that differ from the expected address can assist in debugging.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08e81ad and 5a1467e.

📒 Files selected for processing (1)
  • orchestrator/gravity/src/ethereum/types.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: rust-test
  • GitHub Check: rust-build
  • GitHub Check: hardhat-build
  • GitHub Check: node-build (16.x)
  • GitHub Check: go-build
  • GitHub Check: local-tests (16.x)
  • GitHub Check: Clippy

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
orchestrator/gravity/src/ethereum/types.rs (2)

18-72: Consider more direct equality checks when verifying the recovered address.
Using if recovered_address == expected_address can be more straightforward than cmp(...), improving clarity.

You could replace:

if sig0_address.cmp(&expected_address) == Ordering::Equal {
    return Ok(sig);
}

with:

if sig0_address == expected_address {
    return Ok(sig);
}

83-87: Simplify the message copy step.
You can directly convert the message reference to a Vec<u8> using .to_vec(), making the code more concise.

-        let mut msg = vec![0u8; message.as_ref().len()];
-        msg.copy_from_slice(message.as_ref());
+        let msg = message.as_ref().to_vec();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a1467e and 5eb3886.

📒 Files selected for processing (1)
  • orchestrator/gravity/src/ethereum/types.rs (4 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
orchestrator/gravity/src/ethereum/types.rs (1)
orchestrator/gravity/src/utils/error.rs (16)
  • from (124-126)
  • from (130-132)
  • from (136-138)
  • from (142-144)
  • from (148-150)
  • from (154-156)
  • from (160-162)
  • from (166-168)
  • from (172-174)
  • from (178-180)
  • from (184-186)
  • from (190-192)
  • from (196-198)
  • from (202-204)
  • from (208-210)
  • from (214-216)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: hardhat-build
  • GitHub Check: go-build
  • GitHub Check: rust-test
  • GitHub Check: rust-build
  • GitHub Check: Clippy
  • GitHub Check: local-tests (16.x)
  • GitHub Check: node-build (16.x)
🔇 Additional comments (3)
orchestrator/gravity/src/ethereum/types.rs (3)

6-6: Import change looks good.
The addition of Ordering is aligned with the usage of cmp in signature comparison.


88-97: Good approach to handle signature creation for both local and GCP KMS signers.
No immediate issues found; the match statement and unified error handling are clear.


99-99:

Details

❓ Verification inconclusive

Request confirmation regarding any internal prefixing done by GcpKms.
If the GcpKms signer applies a message prefix (e.g., Ethereum’s “\x19Ethereum Signed Message:”), the recovered address might differ. Please confirm that sign_message uses the exact same bytes.


🏁 Script executed:

#!/bin/bash
# Description: Search for potential prefix usage in `ethers_gcp_kms_signer` that might affect recovery.
fd '.*\.rs' | xargs rg 'Ethereum Signed Message'

Length of output: 50


Action Required: Confirm GcpKms Signer Does Not Implicitly Prefix Messages

Our automated search for the string "Ethereum Signed Message" produced no results, which suggests that there is no obvious internal prefixing within the code. However, the lack of output prevents us from making a definitive conclusion. Please manually verify that the GcpKms signer’s implementation of sign_message does not apply any internal message prefixing (such as the Ethereum prefix) so that the bytes signed match exactly the ones used in the address recovery process.

  • Location to Check:
    • orchestrator/gravity/src/ethereum/types.rs (line 99: self.normalize(&msg, &sig))
    • The GcpKms signer implementation (likely within the ethers_gcp_kms_signer module)

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
orchestrator/gravity/src/ethereum/types.rs (1)

68-72: Use a direct vector conversion.
Instead of creating a zeroed vector and copying the slice, you could do:

-        let mut msg = vec![0u8; message.as_ref().len()];
-
-        msg.copy_from_slice(message.as_ref());
+        let msg = message.as_ref().to_vec();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5eb3886 and 3270bdd.

📒 Files selected for processing (1)
  • orchestrator/gravity/src/ethereum/types.rs (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: solidity-build
  • GitHub Check: rust-build
  • GitHub Check: rust-test
  • GitHub Check: hardhat-build
  • GitHub Check: go-build
  • GitHub Check: Clippy
  • GitHub Check: node-build (16.x)
  • GitHub Check: local-tests (16.x)
🔇 Additional comments (9)
orchestrator/gravity/src/ethereum/types.rs (9)

6-6: Imports look good.
No issues here; cmp::Ordering and Arc are both used properly.


18-24: Neat separation of normalization logic.
Defining normalize as a dedicated method makes the functionality clear and maintainable.


25-56: Consider adding targeted unit tests.
This logic correctly retries with v=0 and then v=1, but including explicit test cases for both normalized signatures would help ensure ongoing correctness.

Would you like a quick snippet that demonstrates how to test both paths?


73-82: Implementation looks good.
Mapping the error into ProviderError::CustomError is consistent for both signers.


84-84: Good addition of normalization.
Ensures the signature is adjusted for the Gravity system as soon as it’s produced.


88-88: Transaction signing flow is consistent.
Nicely mirrors the logic from sign_message while handling local vs. KMS signers.


98-101: Hash-based recovery is handled correctly.
Retrieving sighash before calling normalize is a sound approach for transaction-level verification.


129-139: Typed data flow aligns with EIP-712.
Matches the pattern used for the other sign methods, returning consistent error handling.


140-143:

Details

❓ Verification inconclusive

Validate EIP-712 hashing.
Confirm that this follows the correct EIP-712 domain separator and data struct approach across your codebase.

Below is a script to search for additional EIP-712 usage and confirm consistent hashing:


🏁 Script executed:

#!/bin/bash
# Searching for other references to encode_eip712
fd -e rs | xargs rg "encode_eip712"

Length of output: 121


EIP-712 Hashing Implementation Verification

The only occurrence of encode_eip712 in the repository is in orchestrator/gravity/src/ethereum/types.rs (lines 140–143). Please ensure that the implementation:

  • Correctly constructs the EIP‑712 domain separator and data structure as per the standard.
  • Returns a hash consistent with the specification.
  • Appropriately maps errors to ethers::providers::ProviderError::CustomError.

Manual verification of these aspects is advised to confirm consistent behavior across all intended use cases.

@cbrit cbrit merged commit b63f7ca into main Apr 1, 2025
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants