jeudi 16 juin 2016

DSA algorithm in java : check and advice

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.

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 !

Aucun commentaire:

Enregistrer un commentaire