1 /*
   2  * Copyright (c) 1997, 2017, 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.ProviderException;
  38 import java.security.spec.AlgorithmParameterSpec;
  39 import java.security.spec.InvalidParameterSpecException;
  40 import java.security.spec.DSAParameterSpec;
  41 import java.security.spec.DSAGenParameterSpec;
  42 
  43 import static sun.security.util.SecurityProviderConstants.DEF_DSA_KEY_SIZE;
  44 import static sun.security.util.SecurityProviderConstants.getDefDSASubprimeSize;
  45 
  46 
  47 /**
  48  * This class generates parameters for the DSA algorithm.
  49  *
  50  * @author Jan Luehe
  51  *
  52  *
  53  * @see java.security.AlgorithmParameters
  54  * @see java.security.spec.AlgorithmParameterSpec
  55  * @see DSAParameters
  56  *
  57  * @since 1.2
  58  */
  59 
  60 public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi {
  61 
  62     // the length of prime P, subPrime Q, and seed in bits
  63     private int valueL = -1;
  64     private int valueN = -1;
  65     private int seedLen = -1;
  66 
  67     // the source of randomness
  68     private SecureRandom random;
  69 
  70     // useful constants
  71     private static final BigInteger ZERO = BigInteger.valueOf(0);
  72     private static final BigInteger ONE = BigInteger.valueOf(1);
  73     private static final BigInteger TWO = BigInteger.valueOf(2);
  74 
  75     public DSAParameterGenerator() {
  76     }
  77 
  78     /**
  79      * Initializes this parameter generator for a certain strength
  80      * and source of randomness.
  81      *
  82      * @param strength the strength (size of prime) in bits
  83      * @param random the source of randomness
  84      */
  85     @Override
  86     protected void engineInit(int strength, SecureRandom random) {
  87         if ((strength != 2048) &&
  88             ((strength < 512) || (strength > 1024) || (strength % 64 != 0))) {
  89             throw new InvalidParameterException(
  90                 "Unexpected strength (size of prime): " + strength +
  91                 ". Prime size should be 512-1024, or 2048");
  92         }
  93         this.valueL = strength;
  94         this.valueN = getDefDSASubprimeSize(strength);
  95         this.seedLen = valueN;
  96         this.random = random;
  97     }
  98 
  99     /**
 100      * Initializes this parameter generator with a set of
 101      * algorithm-specific parameter generation values.
 102      *
 103      * @param genParamSpec the set of algorithm-specific parameter
 104      *        generation values
 105      * @param random the source of randomness
 106      *
 107      * @exception InvalidAlgorithmParameterException if the given parameter
 108      * generation values are inappropriate for this parameter generator
 109      */
 110     @Override
 111     protected void engineInit(AlgorithmParameterSpec genParamSpec,
 112             SecureRandom random) throws InvalidAlgorithmParameterException {
 113         if (!(genParamSpec instanceof DSAGenParameterSpec)) {
 114             throw new InvalidAlgorithmParameterException("Invalid parameter");
 115         }
 116         DSAGenParameterSpec dsaGenParams = (DSAGenParameterSpec)genParamSpec;
 117         int primePLen = dsaGenParams.getPrimePLength();
 118         if (primePLen > 2048) {
 119             throw new InvalidParameterException
 120                 ("No support for prime size " + primePLen);
 121         }
 122         // directly initialize using the already validated values
 123         this.valueL = primePLen;
 124         this.valueN = dsaGenParams.getSubprimeQLength();
 125         this.seedLen = dsaGenParams.getSeedLength();
 126         this.random = random;
 127     }
 128 
 129     /**
 130      * Generates the parameters.
 131      *
 132      * @return the new AlgorithmParameters object
 133      */
 134     protected AlgorithmParameters engineGenerateParameters() {
 135         AlgorithmParameters algParams = null;
 136         try {
 137             if (this.random == null) {
 138                 this.random = new SecureRandom();
 139             }
 140             if (valueL == -1) {
 141                 engineInit(DEF_DSA_KEY_SIZE, this.random);
 142             }
 143             BigInteger[] pAndQ = generatePandQ(this.random, valueL,
 144                                                valueN, seedLen);
 145             BigInteger paramP = pAndQ[0];
 146             BigInteger paramQ = pAndQ[1];
 147             BigInteger paramG = generateG(paramP, paramQ);
 148 
 149             DSAParameterSpec dsaParamSpec =
 150                 new DSAParameterSpec(paramP, paramQ, paramG);
 151             algParams = AlgorithmParameters.getInstance("DSA", "SUN");
 152             algParams.init(dsaParamSpec);
 153         } catch (InvalidParameterSpecException e) {
 154             // this should never happen
 155             throw new RuntimeException(e.getMessage());
 156         } catch (NoSuchAlgorithmException e) {
 157             // this should never happen, because we provide it
 158             throw new RuntimeException(e.getMessage());
 159         } catch (NoSuchProviderException e) {
 160             // this should never happen, because we provide it
 161             throw new RuntimeException(e.getMessage());
 162         }
 163 
 164         return algParams;
 165     }
 166 
 167     /*
 168      * Generates the prime and subprime parameters for DSA,
 169      * using the provided source of randomness.
 170      * This method will generate new seeds until a suitable
 171      * seed has been found.
 172      *
 173      * @param random the source of randomness to generate the
 174      * seed
 175      * @param valueL the size of <code>p</code>, in bits.
 176      * @param valueN the size of <code>q</code>, in bits.
 177      * @param seedLen the length of <code>seed</code>, in bits.
 178      *
 179      * @return an array of BigInteger, with <code>p</code> at index 0 and
 180      * <code>q</code> at index 1, the seed at index 2, and the counter value
 181      * at index 3.
 182      */
 183     private static BigInteger[] generatePandQ(SecureRandom random, int valueL,
 184                                               int valueN, int seedLen) {
 185         String hashAlg = null;
 186         if (valueN == 160) {
 187             hashAlg = "SHA";
 188         } else if (valueN == 224) {
 189             hashAlg = "SHA-224";
 190         } else if (valueN == 256) {
 191             hashAlg = "SHA-256";
 192         }
 193         MessageDigest hashObj = null;
 194         try {
 195             hashObj = MessageDigest.getInstance(hashAlg);
 196         } catch (NoSuchAlgorithmException nsae) {
 197             // should never happen
 198             nsae.printStackTrace();
 199         }
 200 
 201         /* Step 3, 4: Useful variables */
 202         int outLen = hashObj.getDigestLength()*8;
 203         int n = (valueL - 1) / outLen;
 204         int b = (valueL - 1) % outLen;
 205         byte[] seedBytes = new byte[seedLen/8];
 206         BigInteger twoSl = TWO.pow(seedLen);
 207         int primeCertainty = -1;
 208         if (valueL <= 1024) {
 209             primeCertainty = 80;
 210         } else if (valueL == 2048) {
 211             primeCertainty = 112;
 212         }
 213 
 214         if (primeCertainty < 0) {
 215             throw new ProviderException("Invalid valueL: " + valueL);
 216         }
 217         BigInteger resultP, resultQ, seed = null;
 218         int counter;
 219         while (true) {
 220             do {
 221                 /* Step 5 */
 222                 random.nextBytes(seedBytes);
 223                 seed = new BigInteger(1, seedBytes);
 224 
 225                 /* Step 6 */
 226                 BigInteger U = new BigInteger(1, hashObj.digest(seedBytes)).
 227                     mod(TWO.pow(valueN - 1));
 228 
 229                 /* Step 7 */
 230                 resultQ = TWO.pow(valueN - 1).add(U).add(ONE). subtract(U.mod(TWO));
 231             } while (!resultQ.isProbablePrime(primeCertainty));
 232 
 233             /* Step 10 */
 234             BigInteger offset = ONE;
 235             /* Step 11 */
 236             for (counter = 0; counter < 4*valueL; counter++) {
 237                 BigInteger V[] = new BigInteger[n + 1];
 238                 /* Step 11.1 */
 239                 for (int j = 0; j <= n; j++) {
 240                     BigInteger J = BigInteger.valueOf(j);
 241                     BigInteger tmp = (seed.add(offset).add(J)).mod(twoSl);
 242                     byte[] vjBytes = hashObj.digest(toByteArray(tmp));
 243                     V[j] = new BigInteger(1, vjBytes);
 244                 }
 245                 /* Step 11.2 */
 246                 BigInteger W = V[0];
 247                 for (int i = 1; i < n; i++) {
 248                     W = W.add(V[i].multiply(TWO.pow(i * outLen)));
 249                 }
 250                 W = W.add((V[n].mod(TWO.pow(b))).multiply(TWO.pow(n * outLen)));
 251                 /* Step 11.3 */
 252                 BigInteger twoLm1 = TWO.pow(valueL - 1);
 253                 BigInteger X = W.add(twoLm1);
 254                 /* Step 11.4, 11.5 */
 255                 BigInteger c = X.mod(resultQ.multiply(TWO));
 256                 resultP = X.subtract(c.subtract(ONE));
 257                 /* Step 11.6, 11.7 */
 258                 if (resultP.compareTo(twoLm1) > -1
 259                     && resultP.isProbablePrime(primeCertainty)) {
 260                     /* Step 11.8 */
 261                     BigInteger[] result = {resultP, resultQ, seed,
 262                                            BigInteger.valueOf(counter)};
 263                     return result;
 264                 }
 265                 /* Step 11.9 */
 266                 offset = offset.add(BigInteger.valueOf(n)).add(ONE);
 267              }
 268         }
 269 
 270     }
 271 
 272     /*
 273      * Generates the <code>g</code> parameter for DSA.
 274      *
 275      * @param p the prime, <code>p</code>.
 276      * @param q the subprime, <code>q</code>.
 277      *
 278      * @param the <code>g</code>
 279      */
 280     private static BigInteger generateG(BigInteger p, BigInteger q) {
 281         BigInteger h = ONE;
 282         /* Step 1 */
 283         BigInteger pMinusOneOverQ = (p.subtract(ONE)).divide(q);
 284         BigInteger resultG = ONE;
 285         while (resultG.compareTo(TWO) < 0) {
 286             /* Step 3 */
 287             resultG = h.modPow(pMinusOneOverQ, p);
 288             h = h.add(ONE);
 289         }
 290         return resultG;
 291     }
 292 
 293     /*
 294      * Converts the result of a BigInteger.toByteArray call to an exact
 295      * signed magnitude representation for any positive number.
 296      */
 297     private static byte[] toByteArray(BigInteger bigInt) {
 298         byte[] result = bigInt.toByteArray();
 299         if (result[0] == 0) {
 300             byte[] tmp = new byte[result.length - 1];
 301             System.arraycopy(result, 1, tmp, 0, tmp.length);
 302             result = tmp;
 303         }
 304         return result;
 305     }
 306 }