Search code examples
authenticationcryptographybouncycastlesignatureecdsa

Signing ECDSA with private key with curve secp224k1


I want to get sign by ECDSA secp224k1. I cannot get the same signature as in the CoinFLEX Authentication Process example from the manual. I am using C# BouncyCastle.

Why can't I get manual page's signature ​​by my code?

// manual page's step 7
byte[] fortyByteMessage  = fromHexStringToByteArr("0x00000000 00000001").Concat(fromHexStringToByteArr("0x6b347302 2e6b9b5a f2fe5d1d ae7cf5bf")).Concat(fromHexStringToByteArr("0xf08c98ca f1fd82e8 cea9825d bff04fd0")).ToArray();

// manual page's step 9
byte[] privateKey = fromHexStringToByteArr("0xb89ea7fc d22cc059 c2673dc2 4ff40b97 83074646 86560d0a d7561b83");

// manual page's step 10
X9ECParameters spec = ECNamedCurveTable.GetByName("secp224k1");
ECDomainParameters domain = new ECDomainParameters(spec.Curve, spec.G, spec.N);
ECDsaSigner signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha224Digest()));
signer.Init(true, new ECPrivateKeyParameters(new BigInteger(privateKey), domain));
BigInteger[] signature = signer.GenerateSignature(fortyByteMessage);
byte[] r = signature[0].ToByteArray().SkipWhile(b => b == 0x00).Reverse().ToArray(); // (r) r should be 0x3fb77a9d 7b5b2a68 209e76f6 872078c5 791340d5 989854ad a3ab735e, but not.

Console.WriteLine(BitConverter.ToString(r).Replace("-", string.Empty).ToLower());

Expected byteArr ( step 10 r value ):

r = 0x3fb77a9d 7b5b2a68 209e76f6 872078c5 791340d5 989854ad a3ab735e<br>

My byteArr (this is wrong value, because it is different from step 10 r value )

r = 0x1e3b3f4f 7401ff9d 827b7222 47823919 452d3adb effa7aa4 52a0879e<br>

Another function:

static byte[] fromHexStringToByteArr(string paramHexString)
{
    string hexString = paramHexString.Substring(2).Replace(" ", "");
    byte[] result = new byte[hexString.Length / 2];

    int cur = 0;

    for (int i = 0; i < hexString.Length; i = i + 2)
    {
        string w = hexString.Substring(i, 2);
        result[cur] = Convert.ToByte(w, 16);
        cur++;
    }
    return result;
}

Solution

    • According to step 10 of the instructions, not the 40-byte-message should be signed, but the SHA224-hash of this message: The client signs the 28-byte SHA-224 digest of the 40-byte message.... Note, that the data are not automatically hashed by the GenerateSignature-method, i.e. it must be hashed explicitly, see also here and these examples.

    • The Org.BouncyCastle.Math.BigInteger.ToByteArray-method (which is used in the C#-code) outputs the byte-array in big-endian-format (unlike .Net, whose System.Numerics.BigInteger.ToByteArray-method uses the little-endian-format). Therefore, it is not necessary to reverse the byte-order (using the Reverse-method).

    • With these modifications, the signature is:

        r = 0x1781ff4997b48d389f518df75001c4b6564082956228d74dd0321656
        s = 0x0aadc68cf78dc75d44fb300f200465e72a70826ec2d5577d49b62e59
      

      which, however, still differs from the signature shown in the instructions.

      In the C#-code, the ECDsaSigner-instance is created with a HMacDsaKCalculator-instance, generating a deterministic signature based on RFC6979. When creating a signature with ECDSA, the k-parameter is chosen randomly for the non-deterministic ECDSA, whereas in the deterministic variant it is created from the message and the private key according to a specific algorithm (described in RFC6979), see here. Thus, the deterministic variant generates the same signature for the same message and private key, while the non-deterministic variant generates different signatures.

      Probably the difference between the signatures is caused by the use of the non-deterministic variant by CoinFLEX. Unfortunately, the instructions do not go into detail about the ECDSA-procedure used.

    Update:

    Both variants, deterministic and non-deterministic, provide valid ECDSA-signatures! Before the deterministic variant (RFC6979 is from August 2013) there was only the non-deterministic variant, see here.

    I installed and tested the sign_secp224k1-tool on a Linux (Debian) machine. As suspected, the tool generates different signatures for the same private key and the same message, obviously using the non-deterministic variant. This can also be easily verified from the source-code: The signature is calculated using the ecp_sign-method, which randomly determines the k-value using /dev/urandom.

    Thus it is clear that the signature generated by the C#-code using the deterministic variant generally cannot match a signature generated by the sign_secp224k1-tool using the non-deterministic variant.