1package org.bouncycastle.crypto.signers;
2
3import java.math.BigInteger;
4import java.security.SecureRandom;
5
6import org.bouncycastle.crypto.CipherParameters;
7import org.bouncycastle.crypto.DSA;
8import org.bouncycastle.crypto.params.DSAKeyParameters;
9import org.bouncycastle.crypto.params.DSAParameters;
10import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
11import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
12import org.bouncycastle.crypto.params.ParametersWithRandom;
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    private final DSAKCalculator kCalculator;
22
23    private DSAKeyParameters key;
24    private SecureRandom    random;
25
26    /**
27     * Default configuration, random K values.
28     */
29    public DSASigner()
30    {
31        this.kCalculator = new RandomDSAKCalculator();
32    }
33
34    /**
35     * Configuration with an alternate, possibly deterministic calculator of K.
36     *
37     * @param kCalculator a K value calculator.
38     */
39    public DSASigner(DSAKCalculator kCalculator)
40    {
41        this.kCalculator = kCalculator;
42    }
43
44    public void init(
45        boolean                 forSigning,
46        CipherParameters        param)
47    {
48        SecureRandom providedRandom = null;
49
50        if (forSigning)
51        {
52            if (param instanceof ParametersWithRandom)
53            {
54                ParametersWithRandom rParam = (ParametersWithRandom)param;
55
56                this.key = (DSAPrivateKeyParameters)rParam.getParameters();
57                providedRandom = rParam.getRandom();
58            }
59            else
60            {
61                this.key = (DSAPrivateKeyParameters)param;
62            }
63        }
64        else
65        {
66            this.key = (DSAPublicKeyParameters)param;
67        }
68
69        this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
70    }
71
72    /**
73     * generate a signature for the given message using the key we were
74     * initialised with. For conventional DSA the message should be a SHA-1
75     * hash of the message of interest.
76     *
77     * @param message the message that will be verified later.
78     */
79    public BigInteger[] generateSignature(
80        byte[] message)
81    {
82        DSAParameters   params = key.getParameters();
83        BigInteger      q = params.getQ();
84        BigInteger      m = calculateE(q, message);
85        BigInteger      x = ((DSAPrivateKeyParameters)key).getX();
86
87        if (kCalculator.isDeterministic())
88        {
89            kCalculator.init(q, x, message);
90        }
91        else
92        {
93            kCalculator.init(q, random);
94        }
95
96        BigInteger  k = kCalculator.nextK();
97
98        // the randomizer is to conceal timing information related to k and x.
99        BigInteger  r = params.getG().modPow(k.add(getRandomizer(q, random)), params.getP()).mod(q);
100
101        k = k.modInverse(q).multiply(m.add(x.multiply(r)));
102
103        BigInteger  s = k.mod(q);
104
105        return new BigInteger[]{ r, s };
106    }
107
108    /**
109     * return true if the value r and s represent a DSA signature for
110     * the passed in message for standard DSA the message should be a
111     * SHA-1 hash of the real message to be verified.
112     */
113    public boolean verifySignature(
114        byte[]      message,
115        BigInteger  r,
116        BigInteger  s)
117    {
118        DSAParameters   params = key.getParameters();
119        BigInteger      q = params.getQ();
120        BigInteger      m = calculateE(q, message);
121        BigInteger      zero = BigInteger.valueOf(0);
122
123        if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
124        {
125            return false;
126        }
127
128        if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
129        {
130            return false;
131        }
132
133        BigInteger  w = s.modInverse(q);
134
135        BigInteger  u1 = m.multiply(w).mod(q);
136        BigInteger  u2 = r.multiply(w).mod(q);
137
138        BigInteger p = params.getP();
139        u1 = params.getG().modPow(u1, p);
140        u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, p);
141
142        BigInteger  v = u1.multiply(u2).mod(p).mod(q);
143
144        return v.equals(r);
145    }
146
147    private BigInteger calculateE(BigInteger n, byte[] message)
148    {
149        if (n.bitLength() >= message.length * 8)
150        {
151            return new BigInteger(1, message);
152        }
153        else
154        {
155            byte[] trunc = new byte[n.bitLength() / 8];
156
157            System.arraycopy(message, 0, trunc, 0, trunc.length);
158
159            return new BigInteger(1, trunc);
160        }
161    }
162
163    protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
164    {
165        return !needed ? null : (provided != null) ? provided : new SecureRandom();
166    }
167
168    private BigInteger getRandomizer(BigInteger q, SecureRandom provided)
169    {
170        // Calculate a random multiple of q to add to k. Note that g^q = 1 (mod p), so adding multiple of q to k does not change r.
171        int randomBits = 7;
172
173        return new BigInteger(randomBits, provided != null ? provided : new SecureRandom()).add(BigInteger.valueOf(128)).multiply(q);
174    }
175}
176