1 /*
   2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider;
  27 
  28 import java.math.BigInteger;
  29 import java.security.AlgorithmParameterGeneratorSpi;
  30 import java.security.AlgorithmParameters;
  31 import java.security.InvalidAlgorithmParameterException;
  32 import java.security.NoSuchAlgorithmException;
  33 import java.security.NoSuchProviderException;
  34 import java.security.InvalidParameterException;
  35 import java.security.MessageDigest;
  36 import java.security.SecureRandom;
  37 import java.security.spec.AlgorithmParameterSpec;
  38 import java.security.spec.InvalidParameterSpecException;
  39 import java.security.spec.DSAParameterSpec;
  40 import java.security.spec.DSAGenParameterSpec;
  41 
  42 /**
  43  * This class generates parameters for the DSA algorithm. It uses a default
  44  * prime modulus size of 1024 bits, which can be overwritten during
  45  * initialization.
  46  *
  47  * @author Jan Luehe
  48  *
  49  *
  50  * @see java.security.AlgorithmParameters
  51  * @see java.security.spec.AlgorithmParameterSpec
  52  * @see DSAParameters
  53  *
  54  * @since 1.2
  55  */
  56 
  57 public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi {
  58 
  59     // the default parameters
  60     private static final DSAGenParameterSpec DEFAULTS =
  61         new DSAGenParameterSpec(1024, 160, 160);
  62 
  63     // the length of prime P, subPrime Q, and seed in bits
  64     private int valueL = -1;
  65     private int valueN = -1;
  66     private int seedLen = -1;
  67 
  68     // the source of randomness
  69     private SecureRandom random;
  70 
  71     public DSAParameterGenerator() {
  72     }
  73 
  74     /**
  75      * Initializes this parameter generator for a certain strength
  76      * and source of randomness.
  77      *
  78      * @param strength the strength (size of prime) in bits
  79      * @param random the source of randomness
  80      */
  81     @Override
  82     protected void engineInit(int strength, SecureRandom random) {
  83         if ((strength >= 512) && (strength <= 1024) && (strength % 64 == 0)) {
  84             this.valueN = 160;
  85         } else if (strength == 2048) {
  86             this.valueN = 224;
  87         } else if (strength == 3072) {
  88             this.valueN = 256;
  89         } else {
  90             throw new InvalidParameterException
  91                 ("Prime size should be 512 - 1024, or 2048, 3072");
  92         }
  93         this.valueL = strength;
  94         this.seedLen = valueN;
  95         this.random = random;
  96     }
  97 
  98     /**
  99      * Initializes this parameter generator with a set of
 100      * algorithm-specific parameter generation values.
 101      *
 102      * @param genParamSpec the set of algorithm-specific parameter
 103      *        generation values
 104      * @param random the source of randomness
 105      *
 106      * @exception InvalidAlgorithmParameterException if the given parameter
 107      * generation values are inappropriate for this parameter generator
 108      */
 109     @Override
 110     protected void engineInit(AlgorithmParameterSpec genParamSpec,
 111             SecureRandom random) throws InvalidAlgorithmParameterException {
 112 
 113         if (!(genParamSpec instanceof DSAGenParameterSpec)) {
 114             throw new InvalidAlgorithmParameterException("Invalid parameter");
 115         }
 116         DSAGenParameterSpec dsaGenParams = (DSAGenParameterSpec)genParamSpec;
 117 
 118         // directly initialize using the already validated values
 119         this.valueL = dsaGenParams.getPrimePLength();
 120         this.valueN = dsaGenParams.getSubprimeQLength();
 121         this.seedLen = dsaGenParams.getSeedLength();
 122         this.random = random;
 123     }
 124 
 125     /**
 126      * Generates the parameters.
 127      *
 128      * @return the new AlgorithmParameters object
 129      */
 130     @Override
 131     protected AlgorithmParameters engineGenerateParameters() {
 132         AlgorithmParameters algParams = null;
 133         try {
 134             if (this.random == null) {
 135                 this.random = new SecureRandom();
 136             }
 137             if (valueL == -1) {
 138                 try {
 139                     engineInit(DEFAULTS, this.random);
 140                 } catch (InvalidAlgorithmParameterException iape) {
 141                     // should never happen
 142                 }
 143             }
 144             BigInteger[] pAndQ = generatePandQ(this.random, valueL,
 145                                                valueN, seedLen);
 146             BigInteger paramP = pAndQ[0];
 147             BigInteger paramQ = pAndQ[1];
 148             BigInteger paramG = generateG(paramP, paramQ);
 149 
 150             DSAParameterSpec dsaParamSpec =
 151                 new DSAParameterSpec(paramP, paramQ, paramG);
 152             algParams = AlgorithmParameters.getInstance("DSA", "SUN");
 153             algParams.init(dsaParamSpec);
 154         } catch (InvalidParameterSpecException e) {
 155             // this should never happen
 156             throw new RuntimeException(e.getMessage());
 157         } catch (NoSuchAlgorithmException e) {
 158             // this should never happen, because we provide it
 159             throw new RuntimeException(e.getMessage());
 160         } catch (NoSuchProviderException e) {
 161             // this should never happen, because we provide it
 162             throw new RuntimeException(e.getMessage());
 163         }
 164 
 165         return algParams;
 166     }
 167 
 168     /*
 169      * Generates the prime and subprime parameters for DSA,
 170      * using the provided source of randomness.
 171      * This method will generate new seeds until a suitable
 172      * seed has been found.
 173      *
 174      * @param random the source of randomness to generate the
 175      * seed
 176      * @param valueL the size of <code>p</code>, in bits.
 177      * @param valueN the size of <code>q</code>, in bits.
 178      * @param seedLen the length of <code>seed</code>, in bits.
 179      *
 180      * @return an array of BigInteger, with <code>p</code> at index 0 and
 181      * <code>q</code> at index 1, the seed at index 2, and the counter value
 182      * at index 3.
 183      */
 184     private static BigInteger[] generatePandQ(SecureRandom random, int valueL,
 185                                               int valueN, int seedLen) {
 186         String hashAlg = null;
 187         if (valueN == 160) {
 188             hashAlg = "SHA";
 189         } else if (valueN == 224) {
 190             hashAlg = "SHA-224";
 191         } else if (valueN == 256) {
 192             hashAlg = "SHA-256";
 193         }
 194         MessageDigest hashObj = null;
 195         try {
 196             hashObj = MessageDigest.getInstance(hashAlg);
 197         } catch (NoSuchAlgorithmException nsae) {
 198             // should never happen
 199             nsae.printStackTrace();
 200         }
 201 
 202         /* Step 3, 4: Useful variables */
 203         int outLen = hashObj.getDigestLength()*8;
 204         int n = (valueL - 1) / outLen;
 205         int b = (valueL - 1) % outLen;
 206         byte[] seedBytes = new byte[seedLen/8];
 207         BigInteger twoSl = BigInteger.TWO.pow(seedLen);
 208         int primeCertainty = 80; // for 1024-bit prime P
 209         if (valueL == 2048) {
 210             primeCertainty = 112;
 211         } else if (valueL == 3072) {
 212             // primeCertainty = 256;
 213             primeCertainty = 128;
 214         }
 215 
 216         BigInteger resultP, resultQ, seed = null;
 217         int counter;
 218         while (true) {
 219             do {
 220                 /* Step 5 */
 221                 random.nextBytes(seedBytes);
 222                 seed = new BigInteger(1, seedBytes);
 223 
 224                 /* Step 6 */
 225                 BigInteger U = new BigInteger(1, hashObj.digest(seedBytes)).
 226                     mod(BigInteger.TWO.pow(valueN - 1));
 227 
 228                 /* Step 7 */
 229                 resultQ = BigInteger.TWO.pow(valueN - 1)
 230                             .add(U)
 231                             .add(BigInteger.ONE)
 232                             .subtract(U.mod(BigInteger.TWO));
 233             } while (!resultQ.isProbablePrime(primeCertainty));
 234 
 235             /* Step 10 */
 236             BigInteger offset = BigInteger.ONE;
 237             /* Step 11 */
 238             for (counter = 0; counter < 4*valueL; counter++) {
 239                 BigInteger[] V = new BigInteger[n + 1];
 240                 /* Step 11.1 */
 241                 for (int j = 0; j <= n; j++) {
 242                     BigInteger J = BigInteger.valueOf(j);
 243                     BigInteger tmp = (seed.add(offset).add(J)).mod(twoSl);
 244                     byte[] vjBytes = hashObj.digest(toByteArray(tmp));
 245                     V[j] = new BigInteger(1, vjBytes);
 246                 }
 247                 /* Step 11.2 */
 248                 BigInteger W = V[0];
 249                 for (int i = 1; i < n; i++) {
 250                     W = W.add(V[i].multiply(BigInteger.TWO.pow(i * outLen)));
 251                 }
 252                 W = W.add((V[n].mod(BigInteger.TWO.pow(b)))
 253                                .multiply(BigInteger.TWO.pow(n * outLen)));
 254                 /* Step 11.3 */
 255                 BigInteger twoLm1 = BigInteger.TWO.pow(valueL - 1);
 256                 BigInteger X = W.add(twoLm1);
 257                 /* Step 11.4, 11.5 */
 258                 BigInteger c = X.mod(resultQ.multiply(BigInteger.TWO));
 259                 resultP = X.subtract(c.subtract(BigInteger.ONE));
 260                 /* Step 11.6, 11.7 */
 261                 if (resultP.compareTo(twoLm1) > -1
 262                     && resultP.isProbablePrime(primeCertainty)) {
 263                     /* Step 11.8 */
 264                     BigInteger[] result = {resultP, resultQ, seed,
 265                                            BigInteger.valueOf(counter)};
 266                     return result;
 267                 }
 268                 /* Step 11.9 */
 269                 offset = offset.add(BigInteger.valueOf(n)).add(BigInteger.ONE);
 270              }
 271         }
 272 
 273     }
 274 
 275     /*
 276      * Generates the <code>g</code> parameter for DSA.
 277      *
 278      * @param p the prime, <code>p</code>.
 279      * @param q the subprime, <code>q</code>.
 280      *
 281      * @param the <code>g</code>
 282      */
 283     private static BigInteger generateG(BigInteger p, BigInteger q) {
 284         BigInteger h = BigInteger.ONE;
 285         /* Step 1 */
 286         BigInteger pMinusOneOverQ = (p.subtract(BigInteger.ONE)).divide(q);
 287         BigInteger resultG = BigInteger.ONE;
 288         while (resultG.compareTo(BigInteger.TWO) < 0) {
 289             /* Step 3 */
 290             resultG = h.modPow(pMinusOneOverQ, p);
 291             h = h.add(BigInteger.ONE);
 292         }
 293         return resultG;
 294     }
 295 
 296     /*
 297      * Converts the result of a BigInteger.toByteArray call to an exact
 298      * signed magnitude representation for any positive number.
 299      */
 300     private static byte[] toByteArray(BigInteger bigInt) {
 301         byte[] result = bigInt.toByteArray();
 302         if (result[0] == 0) {
 303             byte[] tmp = new byte[result.length - 1];
 304             System.arraycopy(result, 1, tmp, 0, tmp.length);
 305             result = tmp;
 306         }
 307         return result;
 308     }
 309 }