2

Cross-posted from question

I've been working on writing a signer service for an Ethereum transaction manager and I need to sign Ethereum transactions using Google KMS Golang APIs. I'll try and summarise the problems I'm facing below.

Ethereum requires compact RLP encoded 65-byte ECDSA signatures in R || S || V format. ECDSA signatures by Google KMS on the other hand have extra header components (R length, S length, etc) along with variable length R and S components. This makes these signatures incompatible for use with Ethereum transaction signing.

A way to get around this is parsing the R and S bytes from the ecdsa signature obtained from Google KMS, compute and add the V byte to the end and use this signature to get a signed Ethereum transaction. Something like this:

var parsedSig struct{ R, S *big.Int }
_, err = asn1.Unmarshal(body, &parsedSig)
if err != nil {
    logger.WithError(err).Error("failed to parse signature bytes")
    return err
}

However this would possibly fail due to one or more of the following reasons:

  • Creating compact ECDSA signatures of 65-byte length by parsing out the R and S components and adding the V component is possibly as distrustful as it sounds. R and S components as mentioned above are not always of 32 byte length for standard ECDSA signatures, which means that the ECDSA signature created by concatenating the components might not always result in 64 bytes.
  • Currently signed transactions in Ethereum are created from Keccak-256 digest hashes after RLP encoding transactions as shown below:
    // from go-ethereum
    func rlpHash(x interface{}) (h common.Hash) {
        hw := sha3.NewLegacyKeccak256()
        rlp.Encode(hw, x)
        hw.Sum(h[:0])
        return h
    }
    
    Asymmetric ECDSA key signing in Google KMS doesn’t have support for Keccak-256 SHA3 message digests. Would using a SHA-256 digest for ethereum transactions work? IMO this would fail since all transaction signature verification happens on RLP encoded Keccak hashes.
  • At this point I am not very sure how to compute the V component of the ECDSA signature after having checked the secp256k1 implementation of the secp256k1_ecdsa_sign_recoverable() function.

How do I go about solving these above issues to be able to create verifiable signed Ethereum transactions using asymmetric elliptic curve signing algorithm by Google KMS?

2
  • Do they support secp256k1? Until a few months ago it wasn't supported.
    – Ismael
    Commented Aug 13, 2019 at 17:46
  • currently, it seems they support secp256k1, but there is no RLP message digest.
    – Lion.k
    Commented Jan 13, 2022 at 3:08

3 Answers 3

2

I did some research regarding GCP KMS, unfortunately it does not support ECDSA secp256k1 which used in Ethereum, so it is not possible to use it in the case. You may find more details here https://stackoverflow.com/questions/58053715/how-to-set-up-a-keyvault-using-secp256k1-algorithm-in-gcp

3

You can use GCP to sign Ethereum transactions (with secp256k1).

https://pkg.go.dev/github.com/pascaldekloe/[email protected]/google

Would using a SHA-256 digest for ethereum transactions work?

I was having the same doubts there. The curve calculation does not care about the hash algorithm for as far as I know. Maybe Google uses the classification for the size only? Either way, SHA-256 works just fine here.

https://github.com/pascaldekloe/etherkeyms/blob/096d712031548e601994c859637009eb53a08e34/google/google.go#L101

1

To calculate v, you can adapt this logic :

Before EIP-155 implementation, the value of v could be 27 or 28. However, since the adoption of EIP-155, the value of v is now determined based on the chain_id of the network where the transaction is being executed.

For transactions on networks where EIP-155 is active, the value of v is calculated using the formula v = chain_id * 2 + 35 or v = chain_id * 2 + 36.

To identify the correct value of v in an Ethereum signature, one can use the function ecrecover(sig, v, r, s). This function returns the public address corresponding to an Ethereum signature. Since you have already calculated the Ethereum address before, we know what the result of this equation should be. Thus, it is possible to test the two potential values of v to determine which one is correct.

function calculateV(address: Buffer, digest: Buffer, r: Buffer, s: Buffer, chainId?: bigint) : bigint {
    /**
     * This is the function to find the right v value
     * There are two matching signatues on the elliptic curve
     * we need to find the one that matches to our public key
     * it can be v = `candidate_1` or v = `candidate_2`
     */
    const candidate_1 = (chainId) ? (chainId * BigInt(2) + BigInt(35)) : BigInt(27);
    const candidate_2 = (chainId) ? (chainId * BigInt(2) + BigInt(36)) : BigInt(28);
    if (Buffer.compare(address, ethutil.publicToAddress(ethutil.ecrecover(digest, candidate_1, r, s, chainId)) === 0) {
        return candidate_1;
    } else if (Buffer.compare(address, ethutil.publicToAddress(ethutil.ecrecover(digest, candidate_2, r, s, chainId)) === 0) {
        return candidate_2;
    } else {
        return BigInt(-1);
    }
}

source : https://jonathanokz.medium.com/secure-an-ethereum-wallet-with-a-kms-provider-2914bd1e4341

3
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
    – Maka
    Commented Feb 22 at 11:37
  • 1
    @Maka I fixed it, I hope it'll be good
    – a9911b
    Commented Feb 22 at 14:36
  • It looks much better. Adds to the conversation, if a little late. But most importantly the information is still retained if the external link goes down.
    – Maka
    Commented Feb 22 at 21:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.