1package org.bouncycastle.crypto.signers;
2
3import org.bouncycastle.crypto.CipherParameters;
4import org.bouncycastle.crypto.DSA;
5import org.bouncycastle.crypto.params.DSAKeyParameters;
6import org.bouncycastle.crypto.params.DSAParameters;
7import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
8import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
9import org.bouncycastle.crypto.params.ParametersWithRandom;
10
11import java.math.BigInteger;
12import java.security.SecureRandom;
13
14/**
15 * The Digital Signature Algorithm - as described in "Handbook of Applied
16 * Cryptography", pages 452 - 453.
17 */
18public class DSASigner
19    implements DSA
20{
21    DSAKeyParameters key;
22
23    SecureRandom    random;
24
25    public void init(
26        boolean                 forSigning,
27        CipherParameters        param)
28    {
29        if (forSigning)
30        {
31            if (param instanceof ParametersWithRandom)
32            {
33                ParametersWithRandom    rParam = (ParametersWithRandom)param;
34
35                this.random = rParam.getRandom();
36                this.key = (DSAPrivateKeyParameters)rParam.getParameters();
37            }
38            else
39            {
40                this.random = new SecureRandom();
41                this.key = (DSAPrivateKeyParameters)param;
42            }
43        }
44        else
45        {
46            this.key = (DSAPublicKeyParameters)param;
47        }
48    }
49
50    /**
51     * generate a signature for the given message using the key we were
52     * initialised with. For conventional DSA the message should be a SHA-1
53     * hash of the message of interest.
54     *
55     * @param message the message that will be verified later.
56     */
57    public BigInteger[] generateSignature(
58        byte[] message)
59    {
60        DSAParameters   params = key.getParameters();
61        BigInteger      m = calculateE(params.getQ(), message);
62        BigInteger      k;
63        int                  qBitLength = params.getQ().bitLength();
64
65        do
66        {
67            k = new BigInteger(qBitLength, random);
68        }
69        while (k.compareTo(params.getQ()) >= 0);
70
71        BigInteger  r = params.getG().modPow(k, params.getP()).mod(params.getQ());
72
73        k = k.modInverse(params.getQ()).multiply(
74                    m.add(((DSAPrivateKeyParameters)key).getX().multiply(r)));
75
76        BigInteger  s = k.mod(params.getQ());
77
78        BigInteger[]  res = new BigInteger[2];
79
80        res[0] = r;
81        res[1] = s;
82
83        return res;
84    }
85
86    /**
87     * return true if the value r and s represent a DSA signature for
88     * the passed in message for standard DSA the message should be a
89     * SHA-1 hash of the real message to be verified.
90     */
91    public boolean verifySignature(
92        byte[]      message,
93        BigInteger  r,
94        BigInteger  s)
95    {
96        DSAParameters   params = key.getParameters();
97        BigInteger      m = calculateE(params.getQ(), message);
98        BigInteger      zero = BigInteger.valueOf(0);
99
100        if (zero.compareTo(r) >= 0 || params.getQ().compareTo(r) <= 0)
101        {
102            return false;
103        }
104
105        if (zero.compareTo(s) >= 0 || params.getQ().compareTo(s) <= 0)
106        {
107            return false;
108        }
109
110        BigInteger  w = s.modInverse(params.getQ());
111
112        BigInteger  u1 = m.multiply(w).mod(params.getQ());
113        BigInteger  u2 = r.multiply(w).mod(params.getQ());
114
115        u1 = params.getG().modPow(u1, params.getP());
116        u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, params.getP());
117
118        BigInteger  v = u1.multiply(u2).mod(params.getP()).mod(params.getQ());
119
120        return v.equals(r);
121    }
122
123    private BigInteger calculateE(BigInteger n, byte[] message)
124    {
125        if (n.bitLength() >= message.length * 8)
126        {
127            return new BigInteger(1, message);
128        }
129        else
130        {
131            byte[] trunc = new byte[n.bitLength() / 8];
132
133            System.arraycopy(message, 0, trunc, 0, trunc.length);
134
135            return new BigInteger(1, trunc);
136        }
137    }
138}
139