Skip to content

Add AWS KMS signing backend - #118

Open
valentevidal wants to merge 1 commit into
hyperledger-firefly:mainfrom
valentevidal:feature/kms-signing-backend
Open

Add AWS KMS signing backend#118
valentevidal wants to merge 1 commit into
hyperledger-firefly:mainfrom
valentevidal:feature/kms-signing-backend

Conversation

@valentevidal

Copy link
Copy Markdown

Feature Request: Add AWS KMS signing backend

Summary

Add an AWS KMS signing backend so the FireFly Signer can sign Ethereum transactions using keys stored in AWS KMS, without the key material ever leaving the KMS hardware.

Motivation

Currently, FireFly Signer only supports a file-based wallet (fileWallet) where signing keys live on disk. For enterprise and regulated deployments (financial services, tokenised assets, etc.), there is a strong requirement to use hardware-backed key management:

  • Key isolation: the private key never exists in process memory or on disk — signing happens inside KMS.
  • FIPS 140-2 Level 3 compliance: KMS is FIPS-validated, which institutional auditors require.
  • Key rotation without restart: KMS keys can be rotated at the AWS level without touching the signer.
  • Audit trail: every signing operation is logged in CloudTrail with the KMS key ID.
  • IRSA integration: on EKS, the signer pod can use IAM Roles for Service Accounts to authenticate to KMS — no long-lived credentials.

AWS KMS supports asymmetric keys on the ECC_SECGP256K1 curve (the secp256k1 curve used by Ethereum), so KMS can produce valid Ethereum signatures.

Proposed Implementation

I have a working implementation ready as a PR. It adds:

  1. pkg/kmswallet/ — a new KMSWallet type that implements the existing ethsigner.Wallet interface. Internally it:

    • Calls kms:GetPublicKey at startup to derive the Ethereum address from the KMS public key.
    • Calls kms:Sign with MessageType: Digest and SigningAlgorithm: ECDSA_SHA_256 for each signing request.
    • Parses the DER-encoded ECDSA signature to extract R and S.
    • Recovers the Y-parity (V) by trying both 27 and 28 and checking which recovers to the known public key (KMS does not return a recovery ID).
  2. pkg/kmswallet/kmssigner.go — a kmsSigner that implements the secp256k1.Signer and secp256k1.SignerDirect interfaces, so it slots into the existing transaction signing path without changes.

  3. Config section kmsWallet alongside fileWallet:

    kmsWallet:
      enabled: true
      keyId: <kms-key-id-or-alias>
      region: <aws-region>
      endpoint: <optional-for-localstack-testing>

    When kmsWallet.enabled is true, it takes precedence over fileWallet. Defaults to false (backwards-compatible).

Design Decisions

  • V recovery by trial: KMS returns a DER ECDSA signature without a recovery ID. The implementation tries both parities (V=27, V=28) and checks which one recovers to the KMS public key's address. This is the same approach used by other KMS-backed Ethereum signers.
  • Pre-hashed messages: the signer receives the already-hashed (Keccak-256) transaction payload and passes it to KMS as a Digest. KMS does not re-hash it.
  • Backwards-compatible: fileWallet remains the default. kmsWallet is opt-in.
  • No multi-key support (yet): the initial implementation supports one KMS key per signer instance (one address in eth_accounts). Multi-key support can be added later by mapping addresses to KMS key IDs.

Testing

The PR includes the implementation and passes all existing tests. I plan to add integration tests with LocalStack (via the endpoint config) in a follow-up if there's interest from maintainers.

Open Questions for Maintainers

  1. Is there appetite for this feature in the project? I'm happy to maintain it long-term.
  2. Would you prefer the V-recovery approach, or should we explore whether KMS can return the recovery ID via a different API?
  3. Any preference on the config structure — should kmsWallet be under a broader wallets section, or is a top-level section fine?

Adds a new kmsWallet signing backend that uses AWS KMS with
ECC_SECGP256K1 keys for secp256k1 (Ethereum) signing. The key
material never leaves the KMS hardware — the signer calls the
KMS Sign API with a pre-hashed message and reconstructs the
Ethereum R/S/V signature from the DER-encoded response.

New config section kmsWallet (alongside fileWallet):
  kmsWallet:
    enabled: true
    keyId: <kms-key-id-or-alias>
    region: <aws-region>
    endpoint: <optional-endpoint-for-localstack>

When kmsWallet.enabled is true, the KMS wallet takes precedence
over the file wallet. Defaults to false (backwards-compatible).
@valentevidal
valentevidal requested a review from a team as a code owner August 18, 2026 10:44
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.

1 participant