2

I am signing a transaction by using a private key in AWS Cloud HSM. But not able to recover the correct public key from the signature. Every time I try to run ECRecover method, it generates different public key.

I am able to retrieve the R and S from ECDSA signature generated by AWS Cloud HSM in the following code:

import (
  "github.com/btcsuite/btcd/btcec"
  ethcrypto "github.com/ethereum/go-ethereum/crypto"
)
// SignatureValues returns signature values [R || S]
func (e *Ethereum) SignatureValues(sig []byte) (r, s *big.Int, err error) {
    r = new(big.Int).SetBytes(sig[:32])
    s = new(big.Int).SetBytes(sig[32:64])
    return r, s, nil
}

And below code snippet tries to find the correct signature to be verified:


    sig := make([]byte, 65)

    denTwo := big.NewInt(2)
    rightS := big.NewInt(0)
    curve := btcec.S256()

    /*
        URL: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md
        All transaction signatures whose s-value is greater than secp256k1n/2 are now considered invalid.
        The ECDSA recover precompiled contract remains unchanged and will keep accepting high s-values; this is useful
        e.g. if a contract recovers old Bitcoin signatures.
    */
    rightS = rightS.Div(curve.Params().N, denTwo)

    if rightS.Cmp(S) == -1 {
        S = S.Sub(curve.Params().N, S)
        log.Println("S: ", S.String())
        rbytes, sbytes := R.Bytes(), S.Bytes()
        copy(sig[32-len(rbytes):32], rbytes)
        copy(sig[64-len(sbytes):64], sbytes)
        log.Println("Result : ", verifyECRecover(hash.Bytes(), sig, expectedPubKey))
    } else {
        rbytes, sbytes := R.Bytes(), S.Bytes()
        copy(sig[32-len(rbytes):32], rbytes)
        copy(sig[64-len(sbytes):64], sbytes)
        log.Println("Else Result : ", verifyECRecover(hash.Bytes(), sig, expectedPubKey))
    }

    verification := ethcrypto.VerifySignature(expectedPubKey, hash.Bytes(), sig)

Below code method is VerifyECRecover:

func verifyECRecover(hash, sig, expectedPubKey []byte) bool {
    for v := 0; v < 2; v++ {
        sig[64] = byte(v)
        if pubkey, err := ethcrypto.Ecrecover(hash, sig); err == nil {
            if bytes.Equal(pubkey, expectedPubKey) {
                log.Println("Matched")
                return true
            }
            log.Println("Not Matched")
        } else {
            log.Println("Error: ", err)
        }
    }
    return false
}

I am not able to get the correct public key, so that I can submit the transaction to ethereum blockchain. Can anyone help me in solving this?

1
  • It works well if I sign the transaction without AWS Cloud HSM and size of sign is also of 65 bytes (64 + 1 recovery id). Cloud HSM generates the ECDSA signature with larger S value (S > Curve.N/2) Commented Feb 3, 2020 at 0:56

2 Answers 2

2

I have figured out the issue and writing the answer to help anyone who face similar issue.

The issue was due to incorrect use of asn object identifier for secp256k1 which is 1.3.132.0.10. Reference URL: secp256k1 ASN Object ID.

Using this OID has resulted in retrieving the correct public key and I am able to transmit transaction to Ethereum blockchain now. It is implemented using Golang through PKCS11 specification.

1
  • Can you elaborate a little more on this? Which part of your code did you fix to resolve the issue? Commented Mar 23, 2023 at 7:27
0

If anyone runs into this issue while using localstack kms. It is because asymmetric keys are not supported well, as per this thread: https://github.com/localstack/localstack/issues/7311

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.