• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.bouncycastle.crypto.signers;
2 
3 import java.math.BigInteger;
4 import java.security.SecureRandom;
5 
6 import org.bouncycastle.crypto.CipherParameters;
7 import org.bouncycastle.crypto.DSA;
8 import org.bouncycastle.crypto.params.ECDomainParameters;
9 import org.bouncycastle.crypto.params.ECKeyParameters;
10 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
11 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
12 import org.bouncycastle.crypto.params.ParametersWithRandom;
13 import org.bouncycastle.math.ec.ECAlgorithms;
14 import org.bouncycastle.math.ec.ECConstants;
15 import org.bouncycastle.math.ec.ECCurve;
16 import org.bouncycastle.math.ec.ECFieldElement;
17 import org.bouncycastle.math.ec.ECMultiplier;
18 import org.bouncycastle.math.ec.ECPoint;
19 import org.bouncycastle.math.ec.FixedPointCombMultiplier;
20 
21 /**
22  * EC-DSA as described in X9.62
23  */
24 public class ECDSASigner
25     implements ECConstants, DSA
26 {
27     private final DSAKCalculator kCalculator;
28 
29     private ECKeyParameters key;
30     private SecureRandom    random;
31 
32     /**
33      * Default configuration, random K values.
34      */
ECDSASigner()35     public ECDSASigner()
36     {
37         this.kCalculator = new RandomDSAKCalculator();
38     }
39 
40     /**
41      * Configuration with an alternate, possibly deterministic calculator of K.
42      *
43      * @param kCalculator a K value calculator.
44      */
ECDSASigner(DSAKCalculator kCalculator)45     public ECDSASigner(DSAKCalculator kCalculator)
46     {
47         this.kCalculator = kCalculator;
48     }
49 
init( boolean forSigning, CipherParameters param)50     public void init(
51         boolean                 forSigning,
52         CipherParameters        param)
53     {
54         SecureRandom providedRandom = null;
55 
56         if (forSigning)
57         {
58             if (param instanceof ParametersWithRandom)
59             {
60                 ParametersWithRandom rParam = (ParametersWithRandom)param;
61 
62                 this.key = (ECPrivateKeyParameters)rParam.getParameters();
63                 providedRandom = rParam.getRandom();
64             }
65             else
66             {
67                 this.key = (ECPrivateKeyParameters)param;
68             }
69         }
70         else
71         {
72             this.key = (ECPublicKeyParameters)param;
73         }
74 
75         this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom);
76     }
77 
78     // 5.3 pg 28
79     /**
80      * generate a signature for the given message using the key we were
81      * initialised with. For conventional DSA the message should be a SHA-1
82      * hash of the message of interest.
83      *
84      * @param message the message that will be verified later.
85      */
generateSignature( byte[] message)86     public BigInteger[] generateSignature(
87         byte[] message)
88     {
89         ECDomainParameters ec = key.getParameters();
90         BigInteger n = ec.getN();
91         BigInteger e = calculateE(n, message);
92         BigInteger d = ((ECPrivateKeyParameters)key).getD();
93 
94         if (kCalculator.isDeterministic())
95         {
96             kCalculator.init(n, d, message);
97         }
98         else
99         {
100             kCalculator.init(n, random);
101         }
102 
103         BigInteger r, s;
104 
105         ECMultiplier basePointMultiplier = createBasePointMultiplier();
106 
107         // 5.3.2
108         do // generate s
109         {
110             BigInteger k;
111             do // generate r
112             {
113                 k = kCalculator.nextK();
114 
115                 ECPoint p = basePointMultiplier.multiply(ec.getG(), k).normalize();
116 
117                 // 5.3.3
118                 r = p.getAffineXCoord().toBigInteger().mod(n);
119             }
120             while (r.equals(ZERO));
121 
122             s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
123         }
124         while (s.equals(ZERO));
125 
126         return new BigInteger[]{ r, s };
127     }
128 
129     // 5.4 pg 29
130     /**
131      * return true if the value r and s represent a DSA signature for
132      * the passed in message (for standard DSA the message should be
133      * a SHA-1 hash of the real message to be verified).
134      */
verifySignature( byte[] message, BigInteger r, BigInteger s)135     public boolean verifySignature(
136         byte[]      message,
137         BigInteger  r,
138         BigInteger  s)
139     {
140         ECDomainParameters ec = key.getParameters();
141         BigInteger n = ec.getN();
142         BigInteger e = calculateE(n, message);
143 
144         // r in the range [1,n-1]
145         if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0)
146         {
147             return false;
148         }
149 
150         // s in the range [1,n-1]
151         if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0)
152         {
153             return false;
154         }
155 
156         BigInteger c = s.modInverse(n);
157 
158         BigInteger u1 = e.multiply(c).mod(n);
159         BigInteger u2 = r.multiply(c).mod(n);
160 
161         ECPoint G = ec.getG();
162         ECPoint Q = ((ECPublicKeyParameters)key).getQ();
163 
164         ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2);
165 
166         // components must be bogus.
167         if (point.isInfinity())
168         {
169             return false;
170         }
171 
172         /*
173          * If possible, avoid normalizing the point (to save a modular inversion in the curve field).
174          *
175          * There are ~cofactor elements of the curve field that reduce (modulo the group order) to 'r'.
176          * If the cofactor is known and small, we generate those possible field values and project each
177          * of them to the same "denominator" (depending on the particular projective coordinates in use)
178          * as the calculated point.X. If any of the projected values matches point.X, then we have:
179          *     (point.X / Denominator mod p) mod n == r
180          * as required, and verification succeeds.
181          *
182          * Based on an original idea by Gregory Maxwell (https://github.com/gmaxwell), as implemented in
183          * the libsecp256k1 project (https://github.com/bitcoin/secp256k1).
184          */
185         ECCurve curve = point.getCurve();
186         if (curve != null)
187         {
188             BigInteger cofactor = curve.getCofactor();
189             if (cofactor != null && cofactor.compareTo(EIGHT) <= 0)
190             {
191                 ECFieldElement D = getDenominator(curve.getCoordinateSystem(), point);
192                 if (D != null && !D.isZero())
193                 {
194                     ECFieldElement X = point.getXCoord();
195                     while (curve.isValidFieldElement(r))
196                     {
197                         ECFieldElement R = curve.fromBigInteger(r).multiply(D);
198                         if (R.equals(X))
199                         {
200                             return true;
201                         }
202                         r = r.add(n);
203                     }
204                     return false;
205                 }
206             }
207         }
208 
209         BigInteger v = point.normalize().getAffineXCoord().toBigInteger().mod(n);
210         return v.equals(r);
211     }
212 
calculateE(BigInteger n, byte[] message)213     protected BigInteger calculateE(BigInteger n, byte[] message)
214     {
215         int log2n = n.bitLength();
216         int messageBitLength = message.length * 8;
217 
218         BigInteger e = new BigInteger(1, message);
219         if (log2n < messageBitLength)
220         {
221             e = e.shiftRight(messageBitLength - log2n);
222         }
223         return e;
224     }
225 
createBasePointMultiplier()226     protected ECMultiplier createBasePointMultiplier()
227     {
228         return new FixedPointCombMultiplier();
229     }
230 
getDenominator(int coordinateSystem, ECPoint p)231     protected ECFieldElement getDenominator(int coordinateSystem, ECPoint p)
232     {
233         switch (coordinateSystem)
234         {
235         case ECCurve.COORD_HOMOGENEOUS:
236         case ECCurve.COORD_LAMBDA_PROJECTIVE:
237         case ECCurve.COORD_SKEWED:
238             return p.getZCoord(0);
239         case ECCurve.COORD_JACOBIAN:
240         case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
241         case ECCurve.COORD_JACOBIAN_MODIFIED:
242             return p.getZCoord(0).square();
243         default:
244             return null;
245         }
246     }
247 
initSecureRandom(boolean needed, SecureRandom provided)248     protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided)
249     {
250         return !needed ? null : (provided != null) ? provided : new SecureRandom();
251     }
252 }
253