3

If one of you is familiar with the DSA algorithm, could you check if I understood correctly the algorithm ? Or at least answer the 2 questions that follow ? I based my work on the Wikipedia DSA article.

I tried to make the code as clear as I could for reading.

public static class KeyGenerator {
    private final BigInteger TWO = new BigInteger("2");
    private final BigInteger P, Q, G;

    public KeyGenerator(int pLength) {
        this.Q = generateQ();
        this.P = generateP(Q,pLength);
        this.G = generateG(P,Q,TWO);
    }

    private BigInteger generateQ() {
        return BigInteger.probablePrime(160, new Random());
    }

    private BigInteger generateP(BigInteger Q, int l) {
        //l should be multiple of 64
        if(l%64!=0) throw new InvalidParameterException("64 should divide l");

        //P is prime and P mod Q == 1 ( i.e. P-1 = Q*Z )
        BigInteger P = BigInteger.probablePrime(512, new Random());//We generate a potential random P
        BigInteger remainder = P.mod(Q);//We calculate the remainder
        P = P.subtract(remainder).add(BigInteger.ONE);//We make P being P = Q*Z + 1 by removing the remainder
        while(!P.isProbablePrime(100)) {P = P.add(Q);}//Z++ until P = Q*Z + 1 is primal

        //P has to be l bits (l = 512 or 1024 generally)
        if(P.bitCount()>l) {return generateP(Q,l);}
        return P;
    }

    private BigInteger generateG(BigInteger P, BigInteger Q, BigInteger H) {
        //G is H^((P-1)/Q) mod P with H being any value 1<H<Q
        BigInteger G = H.modPow(P.subtract(BigInteger.ONE).divide(Q),P);
        if(G.compareTo(BigInteger.ONE)<=0) return generateG(P, Q, H.add(BigInteger.ONE));//G must be >1
        return G;
    }

    public KeyPair generateKeyPair() {
        BigInteger privateKey = new BigInteger(159, new Random());//private key should be <Q, but Q is 160 bits.
        //public key is G^X mod P where X is the private key
        BigInteger publicKey = G.modPow(privateKey,P);
        return new KeyPair(privateKey, publicKey);
    }

}

public static class SignatureMaker {

    ...

    public Signature sign(String message) {
        BigInteger K = new BigInteger(159,new Random());//Random K < Q
        BigInteger R = G.modPow(K, P).mod(Q);//R = G^K mod P mod Q
        if(R.compareTo(BigInteger.ZERO)==0) {return sign(message);}//R should be >0
        BigInteger M = new BigInteger(DigestUtils.sha1(message));//M = sha1(message) (i.e. M = H(m))
        BigInteger S = K.modInverse(Q).multiply(M.add(privateKey.multiply(R))).mod(Q);//S = K^-1*(H(m)+X*R) mod Q
        if(S.compareTo(BigInteger.ZERO)==0) {return sign(message);}//S should be >0
        return new Signature(R, S);
    }
}

public static class SignatureChecker {

    ...

    public boolean check(String message, Signature signature) {
        BigInteger R = signature.R;
        BigInteger S = signature.S;
        if(R.signum()<1 || R.compareTo(Q)>=0 || S.signum()<1 || S.compareTo(Q)>=0) return false;//0<R<Q and 0<S<Q
        BigInteger W = S.modInverse(Q);//W = S^-1 mod Q
        BigInteger M = new BigInteger(DigestUtils.sha1(message));//M = sha1(message) i.e. M = H(m)
        BigInteger U1 = M.multiply(W).mod(Q);//U1 = (H(M)*W) mod Q
        BigInteger U2 = R.multiply(W).mod(Q);//U2 = (R*W) mod Q
        BigInteger V = G.modPow(U1, P).multiply(publicKey.modPow(U2, P)).mod(P).mod(Q);//V = (G^U1 mod P)*(Y^U2 mod P) mod P mod Q
        return V.compareTo(R)==0;//Valid only if V==R
    }
}

I ran some tests and it seems to work, but it doesn't mean I didn't make any mistake.

  • I'm especially unsure of the calculation of S during the sign, and V during the check because I couldn't make the exact formula that was written in Wikipedia.

  • [Solved in comments] I also totally don't get why the algorithm in Wikipedia asks for mod P mod Q during the verification. Normally, mod P mod Q should return the same result as mod Q...

If you see possible improvement, I'll gladly take it too.

Thanks a lot !

Sharcoux
  • 5,546
  • 7
  • 45
  • 78
  • I think better suited for [code review](http://codereview.stackexchange.com) – Sanjeev Jun 15 '16 at 09:59
  • 3
    @Sanjeev *possibly*. The golden rule of Code Review is that the OP must reasonably believe that their code already works as intended. In this case, it sounds more like the OP wants somebody else to confirm that their code works, which would not be an appropriate CR question. – Kaz Jun 15 '16 at 10:02
  • The code works. I tested. It's just that with the nature of the algorithm, I can't think of a test that would make sure that I didn't reduce the security by making a mistake. Do you think I should make it a CR ? Or I can just erase the general question and be more focused on the 2 ones at the bottom. – Sharcoux Jun 15 '16 at 10:29
  • 1
    "Normally, mod P mod Q should return the same result as mod Q": no, this would only hold if Q divides P. – Henry Jun 15 '16 at 10:40
  • Oh, yeah, you're right. Q divides P-1, not P. – Sharcoux Jun 15 '16 at 12:19

0 Answers0