1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.internal.net.eap.crypto;
17 
18 import static com.android.internal.net.utils.BigIntegerUtils.bigIntegerToUnsignedByteArray;
19 import static com.android.internal.net.utils.BigIntegerUtils.unsignedByteArrayToBigInteger;
20 
21 import org.bouncycastle.crypto.digests.SHA1Digest;
22 
23 import java.math.BigInteger;
24 import java.nio.ByteBuffer;
25 
26 /**
27  * This class implements the Pseudo-Random Number Generator as specified by FIPS 186-2, change
28  * notice 1, as required by EAP-SIM (RFC 4186) and EAP-AKA (RFC 4187).
29  *
30  * <p>Simplifying constraints allow for some parameters to be permanently fixed: The "b" parameter
31  * is specified to always be 160 by RFC 4186. Likewise, the seed must be 20 bytes and therefore can
32  * never exceed 2^b.
33  */
34 public class Fips186_2Prf {
35     private static final int SEED_LEN_BYTES = 20;
36     private static final int SHA_OUTPUT_LEN_BYTES = 40;
37 
38     /**
39      * Gets the next random based on the PRF described in FIPS 186-2, Change Notice 1.
40      *
41      * @param seed the seed value
42      * @param outputLenBytes the output byte count required. Will run multiple iterations as needed.
43      * @return the byte-array random result
44      */
getRandom(byte[] seed, int outputLenBytes)45     public byte[] getRandom(byte[] seed, int outputLenBytes) {
46         if (seed.length != SEED_LEN_BYTES) {
47             throw new IllegalArgumentException("Invalid seed length. Must be 160b (20B)");
48         }
49 
50         BigInteger xkey = unsignedByteArrayToBigInteger(seed);
51         BigInteger exp_b = new BigInteger("2").pow(SEED_LEN_BYTES * 8);
52 
53         ByteBuffer buffer = ByteBuffer.allocate(outputLenBytes);
54 
55         // RFC 4186, Appendix B, Step 3:
56         //     M is the number of generation runs, based on the desired output bytes (rounded up)
57         //     EAP SIM/AKA require at least 16 (K_ENCR) + 16 (K_AUTH) + 64 (MSK) + 64 (EMSK) bytes
58         //     (total 160 Bytes)
59         int numIterations = (outputLenBytes + SHA_OUTPUT_LEN_BYTES - 1) / SHA_OUTPUT_LEN_BYTES;
60         for (int j = 0; j < numIterations; j++) {
61             for (int i = 0; i < 2; i++) {
62                 // RFC 4186, Appendix B, Step 3.1 XSEED_j = 0
63                 //     (XSEED_j unused, omitted)
64 
65                 // RFC 4186, Appendix B, Step 3.2a: XVAL = (XKEY + XSEED_j) mod 2^b
66                 //     (XSEED_j unused, omitted)
67                 BigInteger xval = xkey.mod(exp_b);
68 
69                 // RFC 4186, Appendix B, Step 3.2b:
70                 //     w_i = G(t, XVAL)
71                 byte[] w_i = new byte[SEED_LEN_BYTES];
72                 Sha1_186_2_FunctionG digest = new Sha1_186_2_FunctionG();
73                 digest.update(
74                         bigIntegerToUnsignedByteArray(xval, SEED_LEN_BYTES), 0, SEED_LEN_BYTES);
75                 digest.doFinal(w_i, 0);
76 
77                 // RFC 4186, Appendix B, Step 3.2c:
78                 //     XKEY = (1 + XKEY + w_i) mod 2^b
79                 xkey = xkey.add(BigInteger.ONE).add(unsignedByteArrayToBigInteger(w_i));
80                 xkey = xkey.mod(exp_b);
81 
82                 // RFC 4186, Appendix B, Step 3.3:
83                 //     x_j = w_0|w_1
84                 buffer.put(w_i, 0, Math.min(buffer.remaining(), w_i.length));
85             }
86         }
87 
88         return buffer.array();
89     }
90 
91     /**
92      * This inner class represents the modifications to the SHA1 digest required for FIPS 186-2 PRFs
93      *
94      * <p>FIPS 186-2 requires the use of a hashing function with exactly the same transforms as
95      * SHA1, but with a slightly different padding (all 0s instead of appending additional metadata
96      * for entropy).
97      *
98      * <p>Specifically, this function extends BouncyCastle's SHA1Digest in order to override the
99      * finish method, preventing the additional padding bytes from being appended (leaving them all
100      * 0).
101      */
102     private static class Sha1_186_2_FunctionG extends SHA1Digest {
103         // IV (t) specified by SHA-1; Use default.
104 
105         @Override
finish()106         public void finish() {
107             // Don't do any other processing. We only care about the processBlock() functionality.
108             processBlock();
109         }
110     }
111 }
112