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.spec.SecretKeySpec;
31 import java.security.*;
32 import java.util.Arrays;
33 import java.util.Locale;
34 
35 public class CtrDrbg extends AbstractDrbg {
36 
37     private static final int AES_LIMIT;
38 
39     static {
40         try {
41             AES_LIMIT = Cipher.getMaxAllowedKeyLength("AES");
42         } catch (Exception e) {
43             // should not happen
44             throw new AssertionError("Cannot detect AES", e);
45         }
46     }
47 
48     private Cipher cipher;
49 
50     private String cipherAlg;
51     private String keyAlg;
52 
53     private int ctrLen;
54     private int blockLen;
55     private int keyLen;
56     private int seedLen;
57 
58     private byte[] v;
59     private byte[] k;
60 
CtrDrbg(SecureRandomParameters params)61     public CtrDrbg(SecureRandomParameters params) {
62         mechName = "CTR_DRBG";
63         configure(params);
64     }
65 
alg2strength(String algorithm)66     private static int alg2strength(String algorithm) {
67         switch (algorithm.toUpperCase(Locale.ROOT)) {
68             case "AES-128":
69                 return 128;
70             case "AES-192":
71                 return 192;
72             case "AES-256":
73                 return 256;
74             default:
75                 throw new IllegalArgumentException(algorithm +
76                         " not supported in CTR_DBRG");
77         }
78     }
79 
80     @Override
chooseAlgorithmAndStrength()81     protected void chooseAlgorithmAndStrength() {
82         if (requestedAlgorithm != null) {
83             algorithm = requestedAlgorithm.toUpperCase(Locale.ROOT);
84             int supportedStrength = alg2strength(algorithm);
85             if (requestedInstantiationSecurityStrength >= 0) {
86                 int tryStrength = getStandardStrength(
87                         requestedInstantiationSecurityStrength);
88                 if (tryStrength > supportedStrength) {
89                     throw new IllegalArgumentException(algorithm +
90                             " does not support strength " +
91                             requestedInstantiationSecurityStrength);
92                 }
93                 this.securityStrength = tryStrength;
94             } else {
95                 this.securityStrength = (DEFAULT_STRENGTH > supportedStrength) ?
96                         supportedStrength : DEFAULT_STRENGTH;
97             }
98         } else {
99             int tryStrength = (requestedInstantiationSecurityStrength < 0) ?
100                     DEFAULT_STRENGTH : requestedInstantiationSecurityStrength;
101             tryStrength = getStandardStrength(tryStrength);
102             // Default algorithm, use AES-128 if AES-256 is not available.
103             // Remember to sync with "securerandom.drbg.config" in java.security
104             if (tryStrength <= 128 && AES_LIMIT < 256) {
105                 algorithm = "AES-128";
106             } else if (AES_LIMIT >= 256) {
107                 algorithm = "AES-256";
108             } else {
109                 throw new IllegalArgumentException("unsupported strength " +
110                         requestedInstantiationSecurityStrength);
111             }
112             this.securityStrength = tryStrength;
113         }
114         switch (algorithm.toUpperCase(Locale.ROOT)) {
115             case "AES-128":
116             case "AES-192":
117             case "AES-256":
118                 this.keyAlg = "AES";
119                 this.cipherAlg = "AES/ECB/NoPadding";
120                 switch (algorithm) {
121                     case "AES-128":
122                         this.keyLen = 128 / 8;
123                         break;
124                     case "AES-192":
125                         this.keyLen = 192 / 8;
126                         if (AES_LIMIT < 192) {
127                             throw new IllegalArgumentException(algorithm +
128                                 " not available (because policy) in CTR_DBRG");
129                         }
130                         break;
131                     case "AES-256":
132                         this.keyLen = 256 / 8;
133                         if (AES_LIMIT < 256) {
134                             throw new IllegalArgumentException(algorithm +
135                                 " not available (because policy) in CTR_DBRG");
136                         }
137                         break;
138                     default:
139                         throw new IllegalArgumentException(algorithm +
140                             " not supported in CTR_DBRG");
141                 }
142                 this.blockLen = 128 / 8;
143                 break;
144             default:
145                 throw new IllegalArgumentException(algorithm +
146                         " not supported in CTR_DBRG");
147         }
148         this.seedLen = this.blockLen + this.keyLen;
149         this.ctrLen = this.blockLen;    // TODO
150         if (usedf) {
151             this.minLength = this.securityStrength / 8;
152         } else {
153             this.minLength = this.maxLength =
154                     this.maxPersonalizationStringLength =
155                             this.maxAdditionalInputLength = seedLen;
156         }
157     }
158 
159     /**
160      * This call, used by the constructors, instantiates the digest.
161      */
162     @Override
initEngine()163     protected void initEngine() {
164         try {
165             /*
166              * Use the local SunJCE implementation to avoid native
167              * performance overhead.
168              */
169             cipher = Cipher.getInstance(cipherAlg, "SunJCE");
170         } catch (NoSuchProviderException | NoSuchAlgorithmException
171                 | NoSuchPaddingException e) {
172             // Fallback to any available.
173             try {
174                 cipher = Cipher.getInstance(cipherAlg);
175             } catch (NoSuchAlgorithmException | NoSuchPaddingException exc) {
176                 throw new InternalError(
177                     "internal error: " + cipherAlg + " not available.", exc);
178             }
179         }
180     }
181 
status()182     private void status() {
183         if (debug != null) {
184             debug.println(this, "Key = " + hex(k));
185             debug.println(this, "V   = " + hex(v));
186             debug.println(this, "reseed counter = " + reseedCounter);
187         }
188     }
189 
190     // 800-90Ar1 10.2.1.2. CTR_DRBG_Update
update(byte[] input)191     private void update(byte[] input) {
192         if (input.length != seedLen) {
193             // Should not happen
194             throw new IllegalArgumentException("input length not seedLen: "
195                     + input.length);
196         }
197         try {
198 
199             int m = (seedLen + blockLen - 1) / blockLen;
200             byte[] temp = new byte[m * blockLen];
201 
202             // Step 1. temp = Null.
203 
204             // Step 2. Loop
205             for (int i = 0; i < m; i++) {
206                 // Step 2.1. Increment
207                 addOne(v, ctrLen);
208                 // Step 2.2. Block_Encrypt
209                 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
210                 // Step 2.3. Encrypt into right position, no need to cat
211                 cipher.doFinal(v, 0, blockLen, temp, i * blockLen);
212             }
213 
214             // Step 3. Truncate
215             temp = Arrays.copyOf(temp, seedLen);
216 
217             // Step 4: Add
218             for (int i = 0; i < seedLen; i++) {
219                 temp[i] ^= input[i];
220             }
221 
222             // Step 5: leftmost
223             k = Arrays.copyOf(temp, keyLen);
224 
225             // Step 6: rightmost
226             v = Arrays.copyOfRange(temp, seedLen - blockLen, seedLen);
227 
228             // Step 7. Return
229         } catch (GeneralSecurityException e) {
230             throw new InternalError(e);
231         }
232     }
233 
234     @Override
instantiateAlgorithm(byte[] ei)235     protected void instantiateAlgorithm(byte[] ei) {
236         if (debug != null) {
237             debug.println(this, "instantiate");
238         }
239         byte[] more;
240         if (usedf) {
241             // 800-90Ar1 10.2.1.3.2 Step 1-2. cat bytes
242             if (personalizationString == null) {
243                 more = nonce;
244             } else {
245                 if (nonce.length + personalizationString.length < 0) {
246                     // Length must be represented as a 32 bit integer in df()
247                     throw new IllegalArgumentException(
248                             "nonce plus personalization string is too long");
249                 }
250                 more = Arrays.copyOf(
251                         nonce, nonce.length + personalizationString.length);
252                 System.arraycopy(personalizationString, 0, more, nonce.length,
253                         personalizationString.length);
254             }
255         } else {
256             // 800-90Ar1 10.2.1.3.1
257             // Step 1-2, no need to expand personalizationString, we only XOR
258             // with shorter length
259             more = personalizationString;
260         }
261         reseedAlgorithm(ei, more);
262     }
263 
264     /**
265      * Block_cipher_df in 10.3.2
266      *
267      * @param input the input string
268      * @return the output block (always of seedLen)
269      */
df(byte[] input)270     private byte[] df(byte[] input) {
271         // 800-90Ar1 10.3.2
272         // 2. L = len (input_string)/8
273         int l = input.length;
274         // 3. N = number_of_bits_to_return/8
275         int n = seedLen;
276         // 4. S = L || N || input_string || 0x80
277         byte[] ln = new byte[8];
278         ln[0] = (byte)(l >> 24);
279         ln[1] = (byte)(l >> 16);
280         ln[2] = (byte)(l >> 8);
281         ln[3] = (byte)(l);
282         ln[4] = (byte)(n >> 24);
283         ln[5] = (byte)(n >> 16);
284         ln[6] = (byte)(n >> 8);
285         ln[7] = (byte)(n);
286 
287         // 5. Zero padding of S
288         // Not necessary, see bcc
289 
290         // 8. K = leftmost (0x00010203...1D1E1F, keylen).
291         byte[] k = new byte[keyLen];
292         for (int i = 0; i < k.length; i++) {
293             k[i] = (byte)i;
294         }
295 
296         // 6. temp = the Null String
297         byte[] temp = new byte[seedLen];
298 
299         // 7. i = 0
300         for (int i = 0; i * blockLen < temp.length; i++) {
301             // 9.1 IV = i || 0^(outlen - len (i)). outLen is blockLen
302             byte[] iv = new byte[blockLen];
303             iv[0] = (byte)(i >> 24);
304             iv[1] = (byte)(i >> 16);
305             iv[2] = (byte)(i >> 8);
306             iv[3] = (byte)(i);
307 
308             int tailLen = temp.length - blockLen*i;
309             if (tailLen > blockLen) {
310                 tailLen = blockLen;
311             }
312             // 9.2 temp = temp || BCC (K, (IV || S)).
313             System.arraycopy(bcc(k, iv, ln, input, new byte[]{(byte)0x80}),
314                     0, temp, blockLen*i, tailLen);
315         }
316 
317         // 10. K = leftmost(temp, keylen)
318         k = Arrays.copyOf(temp, keyLen);
319 
320         // 11. x = select(temp, keylen+1, keylen+outlen)
321         byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length);
322 
323         // 12. temp = the Null string
324         // No need to clean up, temp will be overwritten
325 
326         for (int i = 0; i * blockLen < seedLen; i++) {
327             try {
328                 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
329                 int tailLen = temp.length - blockLen*i;
330                 // 14. requested_bits = leftmost(temp, nuumber_of_bits_to_return)
331                 if (tailLen > blockLen) {
332                     tailLen = blockLen;
333                 }
334                 x = cipher.doFinal(x);
335                 System.arraycopy(x, 0, temp, blockLen * i, tailLen);
336             } catch (GeneralSecurityException e) {
337                 throw new InternalError(e);
338             }
339         }
340 
341         // 15. Return
342         return temp;
343     }
344 
345     /**
346      * Block_Encrypt in 10.3.3
347      *
348      * @param k the key
349      * @param data after concatenated, the data to be operated upon. This is
350      *             a series of byte[], each with an arbitrary length. Note
351      *             that the full length is not necessarily a multiple of
352      *             outlen. XOR with zero is no-op.
353      * @return the result
354      */
bcc(byte[] k, byte[]... data)355     private byte[] bcc(byte[] k, byte[]... data) {
356         byte[] chain = new byte[blockLen];
357         int n1 = 0; // index in data
358         int n2 = 0; // index in data[n1]
359         // pack blockLen of bytes into chain from data[][], again and again
360         while (n1 < data.length) {
361             int j;
362             out: for (j = 0; j < blockLen; j++) {
363                 while (n2 >= data[n1].length) {
364                     n1++;
365                     if (n1 >= data.length) {
366                         break out;
367                     }
368                     n2 = 0;
369                 }
370                 chain[j] ^= data[n1][n2];
371                 n2++;
372             }
373             if (j == 0) { // all data happens to be consumed in the last loop
374                 break;
375             }
376             try {
377                 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
378                 chain = cipher.doFinal(chain);
379             } catch (GeneralSecurityException e) {
380                 throw new InternalError(e);
381             }
382         }
383         return chain;
384     }
385 
386     @Override
reseedAlgorithm( byte[] ei, byte[] additionalInput)387     protected synchronized void reseedAlgorithm(
388             byte[] ei,
389             byte[] additionalInput) {
390         if (usedf) {
391             // 800-90Ar1 10.2.1.3.2 Instantiate.
392             // 800-90Ar1 10.2.1.4.2 Reseed.
393 
394             // Step 1: cat bytes
395             if (additionalInput != null) {
396                 if (ei.length + additionalInput.length < 0) {
397                     // Length must be represented as a 32 bit integer in df()
398                     throw new IllegalArgumentException(
399                             "entropy plus additional input is too long");
400                 }
401                 byte[] temp = Arrays.copyOf(
402                         ei, ei.length + additionalInput.length);
403                 System.arraycopy(additionalInput, 0, temp, ei.length,
404                         additionalInput.length);
405                 ei = temp;
406             }
407             // Step 2. df (seed_material, seedlen).
408             ei = df(ei);
409         } else {
410             // 800-90Ar1 10.2.1.3.1 Instantiate
411             // 800-90Ar1 10.2.1.4.1 Reseed
412             // Step 1-2. Needless
413             // Step 3. seed_material = entropy_input XOR more
414             if (additionalInput != null) {
415                 // additionalInput.length <= seedLen
416                 for (int i = 0; i < additionalInput.length; i++) {
417                     ei[i] ^= additionalInput[i];
418                 }
419             }
420         }
421 
422         if (v == null) {
423             // 800-90Ar1 10.2.1.3.2 Instantiate. Step 3-4
424             // 800-90Ar1 10.2.1.3.1 Instantiate. Step 4-5
425             k = new byte[keyLen];
426             v = new byte[blockLen];
427         }
428         //status();
429 
430         // 800-90Ar1 10.2.1.3.1 Instantiate. Step 6
431         // 800-90Ar1 10.2.1.3.2 Instantiate. Step 5
432         // 800-90Ar1 10.2.1.4.1 Reseed. Step 4
433         // 800-90Ar1 10.2.1.4.2 Reseed. Step 3
434         update(ei);
435         // 800-90Ar1 10.2.1.3.1 Instantiate. Step 7
436         // 800-90Ar1 10.2.1.3.2 Instantiate. Step 6
437         // 800-90Ar1 10.2.1.4.1 Reseed. Step 5
438         // 800-90Ar1 10.2.1.4.2 Reseed. Step 4
439         reseedCounter = 1;
440         //status();
441 
442         // Whatever step. Return
443     }
444 
445     /**
446      * Add one to data, only touch the last len bytes.
447      */
addOne(byte[] data, int len)448     private static void addOne(byte[] data, int len) {
449         for (int i = 0; i < len; i++) {
450             data[data.length - 1 - i]++;
451             if (data[data.length - 1 - i] != 0) {
452                 break;
453             }
454         }
455     }
456 
457     @Override
generateAlgorithm( byte[] result, byte[] additionalInput)458     public synchronized void generateAlgorithm(
459             byte[] result, byte[] additionalInput) {
460 
461         if (debug != null) {
462             debug.println(this, "generateAlgorithm");
463         }
464 
465         // 800-90Ar1 10.2.1.5.1 Generate
466         // 800-90Ar1 10.2.1.5.2 Generate
467 
468         // Step 1: Check reseed_counter. Will not fail. Already checked in
469         // AbstractDrbg#engineNextBytes.
470 
471         if (additionalInput != null) {
472             if (usedf) {
473                 // 10.2.1.5.2 Step 2.1
474                 additionalInput = df(additionalInput);
475             } else {
476                 // 10.2.1.5.1 Step 2.1-2.2
477                 additionalInput = Arrays.copyOf(additionalInput, seedLen);
478             }
479             // 10.2.1.5.1 Step 2.3
480             // 10.2.1.5.2 Step 2.2
481             update(additionalInput);
482         } else {
483             // 10.2.1.5.1 Step 2 Else
484             // 10.2.1.5.2 Step 2 Else
485             additionalInput = new byte[seedLen];
486         }
487 
488         // Step 3. temp = Null
489         int pos = 0;
490         int len = result.length;
491 
492         // Step 4. Loop
493         while (len > 0) {
494             // Step 4.1. Increment
495             addOne(v, ctrLen);
496             try {
497                 // Step 4.2. Encrypt
498                 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
499                 byte[] out = cipher.doFinal(v);
500 
501                 // Step 4.3 and 5. Cat bytes and leftmost
502                 System.arraycopy(out, 0, result, pos,
503                         (len > blockLen) ? blockLen : len);
504             } catch (GeneralSecurityException e) {
505                 throw new InternalError(e);
506             }
507             len -= blockLen;
508             if (len <= 0) {
509                 // shortcut, so that pos needn't be updated
510                 break;
511             }
512             pos += blockLen;
513         }
514 
515         // Step 6. Update
516         update(additionalInput);
517 
518         // Step 7. reseed_counter++
519         reseedCounter++;
520 
521         //status();
522 
523         // Step 8. Return
524     }
525 
526     @Override
toString()527     public String toString() {
528         return super.toString() + ","
529                 + (usedf ? "use_df" : "no_df");
530     }
531 }
532