Home > OS >  How to apply P1363 format on deterministic ECDSA - Bouncy Castle?
How to apply P1363 format on deterministic ECDSA - Bouncy Castle?

Time:05-29

I don't find any reference for deterministic with P1363 format. the r and s always different in deterministic mode.

Non-Deterministic with P1363 -> WithPlain-ECDSA OK

        ISigner sign = SignerUtilities.GetSigner("SHA256WithPlain-ECDSA");            
        sign.Init(true, privateKey);
        sign.BlockUpdate(message, 0, message.Length);
        byte[] signedBytes = sign.GenerateSignature();   // Signature can divded by 2. OK

Deterministic way: (How to apply WithPlain-ECDSA?)

        var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));
        signer.Init(true, privateKey);
        var toto = signer.GenerateSignature(message);
        var r = toto[0].ToByteArray();   // sometimes r are equqal s
        var s = toto[1].ToByteArray();   // sometimes s larger than s byte one.

I can't return r.Concat(s).ToArray(). There's no way to convert r and s to byte[] untill it converted to P1363? which doesn't exist in deterministic version above...

  1. How to concatenate r and s in byte[] in P1363 format?
  2. How to make sure I can convert it back through take half of array for r and other half for s.
  3. Sometimes r equal s, or s larger than r, How to make sure exactly both are equal when generated?

I don't find any reference for P1363. But I found only ASN1 DER format:

    using (MemoryStream ms = new MemoryStream())
        using (Asn1OutputStream asn1stream = new Asn1OutputStream(ms))
        {
            DerSequenceGenerator seq = new DerSequenceGenerator(asn1stream);
            seq.AddObject(new DerInteger(s));
            seq.AddObject(new DerInteger(s));
            seq.Close();
            var arrr =  ms.ToArray();
        }

Am also tried this trick But I looped 1000 and check if arrays of r.Length == s.Length, No equality in all loop.. As he use BigInteger.ToByteArrayUnsigned().

Not sure exactly 100% if above guy's trick work, But the guy try to generate it in fixed way:

   Array.Copy(sig1, 0, sig, 0   (32 - sig1.Length), sig1.Length);
    Array.Copy(sig2, 0, sig, 32   (32 - sig2.Length), sig2.Length);

Short Answer:

According to @Topaco answer, its fast manual approach. Many thanks to him. There's also two classes we can use after investigation StandardDsaEncoding and PlainDsaEncoding classes

  1. StandardDsaEncoding used to encode/decode ASN1.Der format.
  2. PlainDsaEncoding used to encode/decode P1363 format.

PlainDsaEncoding.Instance.Encode() // This convert to P1363 format. PlainDsaEncoding.Instance.Decode() // Decode to signature BigInteger[] again. (r, s)

Note: ECDsaSigner can wrapped inside DsaDigestSigner to allow hash of message automatically. or compute hash(message) manually then use ECDsaSigner exactly like @Topaco other post answer.

CodePudding user response:

For P1363 it's irrelevant whether the deterministic or non-deterministic ECDSA algorithm is used to determine the signature.

In P1363:

  • r and s are contained as unsigned, big endian arrays
  • r and s are padded to their maximum size (length of the order of the generator point) with leading 0x00 values

In your code, r (toto[0]) and s (toto[1]) are both of type Org.BouncyCastle.Math.BigInteger. The conversion to P1363 is then simply possible with (for simplicity, the maximum size is not calculated, but passed):

using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities.Encoders;
using System;

// r and s as BigIntegers
BigInteger r = new BigInteger("184277181267172538606383136781492460232995304963063764346199145255201368533");
BigInteger s = new BigInteger("59980738789003505646930326203273200546254161299399868639304126982806381806190");

// Convert to unsigned, big endian arrays
var rBytes = r.ToByteArrayUnsigned();
var sBytes = s.ToByteArrayUnsigned(); 

// Pad to maximum size 
int maxSize = 32;
byte[] rsBytes = new byte[2 * maxSize];
Buffer.BlockCopy(rBytes, 0, rsBytes, maxSize - rBytes.Length, rBytes.Length);     
Buffer.BlockCopy(sBytes, 0, rsBytes, 2 * maxSize - sBytes.Length, sBytes.Length); 
Console.WriteLine(Hex.ToHexString(rsBytes)); // 00684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

The output is 2 * maxSize bytes large and for this example:

00684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

The first maxSize bytes are r, the last maxSize bytes are s.

For comparison: The same signature in ASN.1/DER format is:

3044021f684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5022100849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

Edit:

The OP pointed out that BouncyCastle already has implementations for this conversion. This is indeed the case:

using Org.BouncyCastle.Crypto.Signers;

// Applied curve: secp256r1 aka P-256 aka prime256v1   
BigInteger n = new BigInteger(1, Convert.FromHexString("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")); // n: order of generator point
                                                        
byte[] p1363 = PlainDsaEncoding.Instance.Encode(n, r, s);
Console.WriteLine(Hex.ToHexString(p1363)); // 00684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

byte[] asn1Der = StandardDsaEncoding.Instance.Encode(n, r, s);
Console.WriteLine(Hex.ToHexString(asn1Der)); // 3044021f684c148ab8582b55fa0929de7853503a05f57119d80cb8b0172103eca369d5022100849be52463b4911ab97c4cbb17322afc0be9a973269d9d263e23bdd43f18426e

The Encode() methods determine the maximum size from the order n of the generator point.

  • Related