1 /*
   2  * Copyright (c) 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 javax.crypto.Cipher;
  29 import javax.crypto.NoSuchPaddingException;
  30 import javax.crypto.SecretKey;
  31 import javax.crypto.spec.SecretKeySpec;
  32 import java.io.IOException;
  33 import java.security.*;
  34 import java.util.Arrays;
  35 import java.util.Locale;
  36 
  37 public class CtrDrbg extends AbstractDrbg {
  38 
  39     private static final long serialVersionUID = 9L;
  40     static final int aesLimit;
  41 
  42     static {
  43         try {
  44             aesLimit = Cipher.getMaxAllowedKeyLength("AES");
  45         } catch (Exception e) {
  46             // should not happen
  47             throw new AssertionError("Cannot detect AES");
  48         }
  49     }
  50 
  51     private transient Cipher cipher;
  52 
  53     private String cipherAlg;
  54     protected String keyAlg;
  55 
  56     private int ctrLen;
  57     private int blockLen;
  58     private int keyLen;
  59     private int seedLen;
  60 
  61     private byte[] v;
  62     private byte[] k;
  63 
  64     public CtrDrbg(SecureRandomParameters params) {
  65         mechName = "CTR_DRBG";
  66         configureInternal(params);
  67     }
  68 
  69     private static int alg2strength(String algorithm) {
  70         switch (algorithm.toUpperCase(Locale.ROOT)) {
  71             case "TDEA":
  72             case "3KEYTDEA":
  73             case "3 KEY TDEA":
  74             case "DESEDE":
  75                 return 112;
  76             case "AES-128":
  77                 return 128;
  78             case "AES-192":
  79                 return 192;
  80             case "AES-256":
  81                 return 256;
  82             default:
  83                 throw new IllegalArgumentException(algorithm +
  84                         " not supported in CTR_DBRG");
  85         }
  86     }
  87 
  88     protected void chooseAlgorithmAndStrength() {
  89         if (requestedAlgorithm != null) {
  90             algorithm = requestedAlgorithm.toUpperCase();
  91             int supportedStrength = alg2strength(algorithm);
  92             if (requestedStrength >= 0) {
  93                 int tryStrength = getStandardStrength(requestedStrength);
  94                 if (tryStrength > supportedStrength) {
  95                     throw new IllegalArgumentException(
  96                             algorithm + " does not support strength " + requestedStrength);
  97                 }
  98                 this.strength = tryStrength;
  99             } else {
 100                 this.strength = defaultStrength > supportedStrength?
 101                         supportedStrength: defaultStrength;
 102             }
 103         } else {
 104             int tryStrength =
 105                     requestedStrength<0? defaultStrength: requestedStrength;
 106             tryStrength = getStandardStrength(tryStrength);
 107             // Default algorithm, use AES-128 if AES-256 is not available
 108             // Also described in comments of the "drbg" security property.
 109             if (tryStrength <= 128 && aesLimit < 256) {
 110                 algorithm = "AES-128";
 111             } else if (aesLimit >= 256) {
 112                 algorithm = "AES-256";
 113             } else {
 114                 throw new IllegalArgumentException(
 115                         "unsupported strength " + requestedStrength);
 116             }
 117             this.strength = tryStrength;
 118         }
 119         switch (algorithm.toUpperCase(Locale.ROOT)) {
 120             case "TDEA":
 121             case "3KEYTDEA":
 122             case "3 KEY TDEA":
 123             case "DESEDE":
 124                 algorithm = "DESede";
 125                 this.keyAlg = "DESede";
 126                 this.cipherAlg = "DESede/ECB/NoPadding";
 127                 this.blockLen = 64/8;
 128                 this.keyLen = 168/8;
 129                 break;
 130             case "AES-128":
 131             case "AES-192":
 132             case "AES-256":
 133                 this.keyAlg = "AES";
 134                 this.cipherAlg = "AES/ECB/NoPadding";
 135                 switch (algorithm) {
 136                     case "AES-128":
 137                         this.keyLen = 128/8;
 138                         break;
 139                     case "AES-192":
 140                         this.keyLen = 192/8;
 141                         if (aesLimit < 192) {
 142                             throw new IllegalArgumentException(algorithm +
 143                                     " not supported in CTR_DBRG");
 144                         }
 145                         break;
 146                     case "AES-256":
 147                         this.keyLen = 256/8;
 148                         if (aesLimit < 256) {
 149                             throw new IllegalArgumentException(algorithm +
 150                                     " not supported in CTR_DBRG");
 151                         }
 152                         break;
 153                     default:
 154                         throw new IllegalArgumentException(algorithm +
 155                                 " not supported in CTR_DBRG");
 156                 }
 157                 this.blockLen = 128/8;
 158                 break;
 159             default:
 160                 throw new IllegalArgumentException(algorithm +
 161                         " not supported in CTR_DBRG");
 162         }
 163         this.seedLen = this.blockLen + this.keyLen;
 164         this.ctrLen = this.blockLen;    // TODO
 165         if (usedf) {
 166             this.minLength = this.strength / 8;
 167         } else {
 168             this.minLength = this.maxLength =
 169                     this.maxPsLength = this.maxAiLength = seedLen;
 170         }
 171     }
 172 
 173     /**
 174      * This call, used by the constructors, instantiates the digest.
 175      */
 176     @Override
 177     protected void initEngine() {
 178         if (cipherAlg == null) {
 179             return;
 180         }
 181         try {
 182             /*
 183              * Use the local SUN implementation to avoid native
 184              * performance overhead.
 185              */
 186             cipher = Cipher.getInstance(cipherAlg, "SunJCE");
 187         } catch (NoSuchProviderException | NoSuchAlgorithmException
 188                 | NoSuchPaddingException e) {
 189             // Fallback to any available.
 190             try {
 191                 cipher = Cipher.getInstance(cipherAlg);
 192             } catch (NoSuchAlgorithmException | NoSuchPaddingException exc) {
 193                 throw new InternalError(
 194                     "internal error: " + cipherAlg + " not available.", exc);
 195             }
 196         }
 197     }
 198 
 199     private void status() {
 200         if (debug != null) {
 201             debug.println("Key = " + hex(k));
 202             debug.println("V   = " + hex(v));
 203             debug.println("reseed counter = " + reseedCounter);
 204         }
 205     }
 206 
 207     private void update(byte[] input) {
 208         if (input.length != seedLen) {
 209             // Should not happen
 210             throw new IllegalArgumentException("input must be of seedlen bytes");
 211         }
 212         try {
 213             int m = (seedLen + blockLen - 1) / blockLen;
 214             byte[] temp = new byte[m * blockLen];
 215             for (int i = 0; i < m; i++) {
 216                 if (ctrLen < blockLen) {
 217                     byte[] inc = addBytes(ctrLen, v, new byte[]{1});
 218                     System.arraycopy(inc, 0, v, blockLen - ctrLen, ctrLen);
 219                 } else {
 220                     v = addBytes(blockLen, v, new byte[]{1});
 221                 }
 222                 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
 223                 cipher.doFinal(v, 0, blockLen, temp, i * blockLen);
 224             }
 225             temp = Arrays.copyOf(temp, seedLen);
 226             for (int i=0; i<seedLen; i++) {
 227                 temp[i] ^= input[i];
 228             }
 229             k = Arrays.copyOf(temp, keyLen);
 230             v = Arrays.copyOfRange(temp, seedLen-blockLen, seedLen);
 231         } catch (GeneralSecurityException e) {
 232             throw new InternalError(e);
 233         }
 234     }
 235 
 236     @Override
 237     protected void instantiateAlgorithm(byte[] ei) {
 238         byte[] more;
 239         if (usedf) {
 240             if (ps == null) {
 241                 more = nonce;
 242             } else {
 243                 more = Arrays.copyOf(nonce, nonce.length + ps.length);
 244                 System.arraycopy(ps, 0, more, nonce.length, ps.length);
 245             }
 246         } else {
 247             more = ps;
 248         }
 249         reseedAlgorithm(ei, more);
 250     }
 251 
 252     private byte[] df(byte[] input) {
 253         int l = input.length;
 254         int n = seedLen;
 255         int slen = 4 + 4 + l + 1;
 256         byte[] s = new byte[(slen + blockLen - 1) / blockLen * blockLen];
 257         s[0] = (byte)(l >> 24);
 258         s[1] = (byte)(l >> 16);
 259         s[2] = (byte)(l >> 8);
 260         s[3] = (byte)(l);
 261         s[4] = (byte)(n >> 24);
 262         s[5] = (byte)(n >> 16);
 263         s[6] = (byte)(n >> 8);
 264         s[7] = (byte)(n);
 265         System.arraycopy(input, 0, s, 8, l);
 266         s[8+l] = (byte)0x80;
 267 
 268         byte[] k = new byte[keyLen];
 269         for (int i=0; i<k.length; i++) {
 270             k[i] = (byte)i;
 271         }
 272 
 273         byte[] temp = new byte[seedLen];
 274 
 275         for (int i=0; i*blockLen < temp.length; i++) {
 276             byte[] iv = new byte[blockLen + s.length];
 277             iv[0] = (byte)(i >> 24);
 278             iv[1] = (byte)(i >> 16);
 279             iv[2] = (byte)(i >> 8);
 280             iv[3] = (byte)(i);
 281             System.arraycopy(s, 0, iv, blockLen, s.length);
 282             int tailLen = temp.length - blockLen*i;
 283             if (tailLen > blockLen) tailLen = blockLen;
 284             System.arraycopy(bcc(k, iv), 0, temp, blockLen*i, tailLen);
 285         }
 286 
 287         k = Arrays.copyOf(temp, keyLen);
 288         byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length);
 289 
 290         for (int i=0; i*blockLen < seedLen; i++) {
 291             try {
 292                 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
 293                 int tailLen = temp.length - blockLen*i;
 294                 if (tailLen > blockLen) tailLen = blockLen;
 295                 x = cipher.doFinal(x);
 296                 System.arraycopy(x, 0, temp, blockLen * i, tailLen);
 297             } catch (GeneralSecurityException e) {
 298                 throw new InternalError(e);
 299             }
 300         }
 301         return temp;
 302     }
 303 
 304     private byte[] bcc(byte[] k, byte[] data) {
 305         byte[] chain = new byte[blockLen];
 306         int n = data.length / blockLen;
 307         for (int i=0; i<n; i++) {
 308             byte[] inputBlock = Arrays.copyOfRange(
 309                     data, i*blockLen, i*blockLen+blockLen);
 310             for (int j=0; j<blockLen; j++) {
 311                 inputBlock[j] ^= chain[j];
 312             }
 313             try {
 314                 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
 315                 chain = cipher.doFinal(inputBlock);
 316             } catch (GeneralSecurityException e) {
 317                 throw new InternalError(e);
 318             }
 319         }
 320         return chain;
 321     }
 322 
 323     @Override
 324     protected void reseedAlgorithm(
 325             byte[] ei,
 326             byte[] additionalInput) {
 327         if (usedf) {
 328             if (additionalInput != null) {
 329                 byte[] temp = Arrays.copyOf(
 330                         ei, ei.length + additionalInput.length);
 331                 System.arraycopy(additionalInput, 0, temp, ei.length, additionalInput.length);
 332                 ei = temp;
 333             }
 334             ei = df(ei);
 335         } else {
 336             if (additionalInput != null) {
 337                 for (int i = 0; i < seedLen && i < additionalInput.length; i++) {
 338                     ei[i] ^= additionalInput[i];
 339                 }
 340             }
 341         }
 342 
 343         // Also called by instantiateAlgorithm.
 344         if (v == null) {
 345             k = new byte[keyLen];
 346             v = new byte[blockLen];
 347         }
 348         status();
 349         update(ei);
 350         reseedCounter = 1;
 351         status();
 352     }
 353 
 354     private static byte[] addBytes(int len, byte[]... data) {
 355         byte[] out = new byte[len];
 356         for (byte[] d: data) {
 357             int dlen = d.length;
 358             int carry = 0;
 359             for (int i=0; i<len; i++) {
 360                 int sum = (out[len-i-1] & 0xff) + carry;
 361                 if (i < dlen) {
 362                     sum += (d[dlen - i - 1] & 0xff);
 363                 }
 364                 out[len - i - 1] = (byte) sum;
 365                 carry = sum >> 8;
 366                 if (i >= dlen && carry == 0) break;
 367             }
 368         }
 369         return out;
 370     }
 371 
 372     @Override
 373     public synchronized void generateAlgorithm(
 374             byte[] result, byte[] additionalInput) {
 375 
 376         if (additionalInput != null) {
 377             if (usedf) {
 378                 additionalInput = df(additionalInput);
 379             } else {
 380                 additionalInput = Arrays.copyOf(additionalInput, seedLen);
 381             }
 382             update(additionalInput);
 383         }
 384 
 385         // Step 3.
 386         int pos = 0;
 387 
 388         // Step 4.
 389         while (pos < result.length) {
 390             int tailLen = result.length - pos;
 391             // Step 4.1.
 392             if (ctrLen < blockLen) {
 393                 byte[] inc = addBytes(ctrLen, v, new byte[]{1});
 394                 System.arraycopy(inc, 0, v, blockLen - ctrLen, ctrLen);
 395             } else {
 396                 v = addBytes(blockLen, v, new byte[]{1});
 397             }
 398             try {
 399                 // Step 4.2.
 400                 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
 401                 byte[] out = cipher.doFinal(v);
 402 
 403                 // Step 4.3 and 5.
 404                 System.arraycopy(out, 0, result, pos,
 405                         tailLen > blockLen ? blockLen: tailLen);
 406             } catch (GeneralSecurityException e) {
 407                 throw new InternalError(e);
 408             }
 409             pos += blockLen;
 410         }
 411 
 412         // Step 6.
 413         update(additionalInput != null? additionalInput: new byte[seedLen]);
 414 
 415         // Step 7.
 416         reseedCounter++;
 417 
 418         status();
 419     }
 420 
 421     private static void des7to8(byte[] key56, int off56, byte[] key64, int off64) {
 422         key64[off64+0] = (byte) (key56[off56+0] & 0xFE); // << 0
 423         key64[off64+1] = (byte) ((key56[off56+0] << 7) | ((key56[off56+1] & 0xFF) >>> 1));
 424         key64[off64+2] = (byte) ((key56[off56+1] << 6) | ((key56[off56+2] & 0xFF) >>> 2));
 425         key64[off64+3] = (byte) ((key56[off56+2] << 5) | ((key56[off56+3] & 0xFF) >>> 3));
 426         key64[off64+4] = (byte) ((key56[off56+3] << 4) | ((key56[off56+4] & 0xFF) >>> 4));
 427         key64[off64+5] = (byte) ((key56[off56+4] << 3) | ((key56[off56+5] & 0xFF) >>> 5));
 428         key64[off64+6] = (byte) ((key56[off56+5] << 2) | ((key56[off56+6] & 0xFF) >>> 6));
 429         key64[off64+7] = (byte) (key56[off56+6] << 1);
 430 
 431         for (int i = 0; i < 8; i++) {
 432             // if even # bits, make uneven, take last bit of count so XOR with 1
 433             // for uneven # bits, make even, take last bit of count so XOR with 0
 434             key64[off64+i] ^= Integer.bitCount(key64[off64+i] ^ 1) & 1;
 435         }
 436     }
 437 
 438     private static SecretKey getKey(String keyAlg, byte[] k) {
 439         if (keyAlg.equals("DESede")) {
 440             byte[] k2 = new byte[24];
 441             des7to8(k, 0, k2, 0);
 442             des7to8(k, 7, k2, 8);
 443             des7to8(k, 14, k2, 16);
 444             k = k2;
 445         }
 446         return new SecretKeySpec(k, keyAlg);
 447     }
 448 
 449     private void readObject(java.io.ObjectInputStream s)
 450             throws IOException, ClassNotFoundException {
 451         s.defaultReadObject ();
 452         initEngine();
 453     }
 454 
 455     @Override
 456     public String toString() {
 457         return super.toString() + "/"
 458                 + (usedf?"use_df":"no_df");
 459     }
 460 }