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) Feb 3, 2020 at 0:56

1 Answer 1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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