Java: ECDSA Signature Verification returns inconsistent result

Viewed 174

Note: I have tried searching the side with "ECDSA Verification" but the questions being posted was the question that using the service either from package or from framework, not from pure implementation, that's why I made this question

So, basically, I want to sign my message every time I send them... the code of how I send and sign it is below,

Send Message

send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String messageContent = typeMessage.getText().toString();
                String Sign = new String();
             
                final BigInteger dA = BigInteger.probablePrime(192,new Random());
                ecdsa.setdA(dA);
                Point QA = new Point();
                QA = ecdsa.getQA();


                Sign = ecdsa.SignMessage(messageContent);
                if((!messageContent.equals("")) && (ecdsa.MessageVerify(messageContent,Sign))){
                    Map<String,String> messageTextBody = new HashMap<String,String>();
                    messageTextBody.put("Message",messageContent);
                    messageTextBody.put("User", Username);
                    messageTextBody.put("Signature", Sign);
                    reference1.push().setValue(messageTextBody);
                    reference2.push().setValue(messageTextBody);
                    typeMessage.setText("");
                }else{
                    Toast.makeText(getApplicationContext(),"You are not the sender",Toast.LENGTH_SHORT).show();
                }
            }
        });

The problem was, every time the signature being verified in the code above, it can return true or false randomly, even though the user who send the message is the same user... so the questions are:

  1. Why did the result always random even though it sent by the same user?
  2. How can I make it agree to always return true because the user who sent it is essentially the same user?

Below I have posted the code about the Signature Generation and the Signature Verification for additional info

Signature Generation

//Signing Message
    public String SignMessage(String message){
        Point sign = SignGeneration(message);
        String signString = sign.toHexString();
        return signString;
    }


//Generate Signature
    private Point SignGeneration(String message){
        BigInteger k = BigInteger.ZERO;
        BigInteger hash = BigInteger.ZERO;
        BigInteger r = BigInteger.ZERO;
        BigInteger s = BigInteger.ZERO;
        Random random = new Random(); //variabel for random k
        Point xy = new Point(); //variabel for kG
        String H = generateSha1String(message);
        hash = new BigInteger(H,16); //Compute hash
        //Proses
        do{
            k = randomBigInt(n.subtract(BigInteger.ONE)); //Generate random bigint with max range n-1
            xy = G.multiplication(k); //do k*G
            r = xy.getX().mod(n); //Compute r by get x_1 fro xy and mod it with n
            if(!(r.compareTo(BigInteger.ZERO) == 0) ){
                if(k.gcd(n).compareTo(BigInteger.ONE) == 0){
                    BigInteger temp = k.modInverse(n);
                    s = (temp.multiply((dA.multiply(r))).add(hash)).mod(n); //Compute s
                }
            }
        }while((r.compareTo(BigInteger.ZERO) == 0) || (s.compareTo(BigInteger.ZERO) == 0));
       
        Point sign = new Point();

        sign.setX(r); //Set x value as r
        sign.setY(s); //Set y value as s

        return sign;
    }

Signature verification

//Verify Message Signature
    public boolean MessageVerify(String message, String sign){
        int length = sign.length();
        Point Signature = new Point();
        Signature.setX(new BigInteger(sign.substring(0, length/2), 16));
        Signature.setY(new BigInteger(sign.substring(length/2), 16));
        return SignVerification(message, Signature);
    }


 //Signature Verification
    private boolean SignVerification(String message, Point sign){
        //Verify r and s random integer in range max n-1
        BigInteger r = sign.getX();
        BigInteger s = sign.getY();
        BigInteger w,u1,u2 = BigInteger.ZERO;
        if(((r.compareTo(BigInteger.ONE) >= 0) && (r.compareTo(n.subtract(BigInteger.ONE)) <= 0)) && ((s.compareTo(BigInteger.ONE) >= 0) && (s.compareTo(n.subtract(BigInteger.ONE)) <= 0))){
            String H = generateSha1String(message);
            BigInteger e = new BigInteger(H,16); //Compute e
            w = s.modInverse(n); //Compute w
            u1 = (e.multiply(w)).mod(n); //Compute u1
            u2 = (r.multiply(w)).mod(n); //Compute u2
            Point X = new Point();
            X = (G.multiplication(u1)).addition(QA.multiplication(u2));//Compute X
            Log.d("n", n.toString());
            if((X.getX().mod(n)).compareTo(r.mod(n)) == 0){
                return true;
            }else{
                return false;
            }

        }else{
            return false;
        }
    }

Progress

Since the problem occurs in the verification process, I tried to log the value in the MessageVerify

I logged the message, the sign, setX, and the SetY value by using Log.d, like below:

Log.d("Message", message);
Log.d("sign", sign);
Log.d("setX", new BigInteger(sign.substring(0, length/2), 16).toString());
Log.d("setY", new BigInteger(sign.substring(length/2), 16).toString());

The result for the successful verification is:

Message:test
Signature:188da80eb03090f67cbf20eb43a18800f4ff0afd82ff10127aa1eed4767a2a901f168332709baf22fc29f202527a2059
SetX:602046282375688656758213480587526111916698976636884684818
SetY:3006941389036669779186569967069188912278324616389591703641

The result for failed verification is:

Message:test
Signature: 188da80eb03090f67cbf20eb43a18800f4ff0afd82ff10126ed05452fa30cf258551fde4bc9453f7ed4fe08aa38e78d6
SetX:602046282375688656758213480587526111916698976636884684818
SetY:2717146143357900544226079474350104288116509888386597550294

Even though I still don't know where the problem come from, the test above suggest the problem may be from the Signature Generation process... but this is still uncertain

Update 2 I'm testing other people Source code from here Which their Sign Generation source are like this

 private Point signatureGeneration(String m){ 
        BigInteger e, k, r, s = BigInteger.ZERO;
        // e = HASH(m)
        e = new BigInteger("7e16b5527c77ea58bac36dddda6f5b444f32e81b", 16);
        Point x1y1 = new Point();
        Random rand = new Random();
        do{
            k = randomBigInteger(n.subtract(BigInteger.ONE));
            x1y1 = G.multiplication(k);
            r = x1y1.getX().mod(n);
            if (! (r.compareTo(BigInteger.ZERO) == 0)){
                if (k.gcd(n).compareTo(BigInteger.ONE) == 0){
                    BigInteger temp = k.modInverse(n);
                    s = (temp.multiply((dA.multiply(r)).add(e))).mod(n);
                }
            }
        } while ((r.compareTo(BigInteger.ZERO) == 0) || (s.compareTo(BigInteger.ZERO) == 0));
        Point signature = new Point();
        signature.setX(r);
        signature.setY(s);
        return signature;
    }

and their verification process are like this

private boolean signatureVerification(String m, Point signature){
        BigInteger r = signature.getX();
        BigInteger s = signature.getY();
        BigInteger e, w, u1, u2;
        if ((r.compareTo(BigInteger.ONE) >= 0) && 
            (r.compareTo(n.subtract(BigInteger.ONE)) <= 0) && 
            (s.compareTo(BigInteger.ONE) >= 0) && 
            (s.compareTo(n.subtract(BigInteger.ONE)) <= 0)){
            // e = HASH(m)
            e = new BigInteger("7e16b5527c77ea58bac36dddda6f5b444f32e81b", 16);
            w = s.modInverse(n);
            u1 = (e.multiply(w)).mod(n);
            u2 = (r.multiply(w)).mod(n);
            Point x1y1 = new Point();
            x1y1 = (G.multiplication(u1)).addition(QA.multiplication(u2));
            if ((x1y1.getX().mod(n)).compareTo(r.mod(n)) == 0){
                return true;
            } else {
                System.out.println("x1 = " + x1y1.getX().mod(n) + " | " + "r(mod n) = " + r.mod(n));
                return false;
            }
        } else {
            return false;
        }
    }

Unluckily, the result are still the same... inconsistent

Update 3

Someone suggested to test vectors online and compare the results I'm getting with the actual result.. Also the guy said to check which dA value that returns fail verification, will update after testing the vectors...

I forgot to mention the standard I used was P-192, but the current standard is using P-256 as from here

Update 4

I tested the dA (private keys) that returns true and false

Several dA that returns true

4932586797028446313791604162219331331880743384320484031523
4995058672683066518736860284447908987220060049995439736509
5862956776220859815493689331273390248765373547354939967957
6201324543883668844361639115428609811595727287245749002217
5222838399154712950243036378095036772903676114969495577797

Several dA that returns false

5425881164364981298041514040021735320995877440649227863027
5470323348832879969905327607374162405372818407344053932703
4946291572348807919810717095782002153268269929161598490177
4769474209716033018356422804849914975531275486757124133339
5134885695167195771629094047829365443294217726343663308709

Update 5

Alright, so I suspect that there's something wrong with my Curve Operation which you can access here

I tested the Point Operation code from here to my app, and it worked pretty well.. almost no inconsistencies, so the cause of the problem lies in my Points operation code which is I linked above...

So my update question, in which part in my Point Operation Code that is wrong?

0 Answers
Related