blob: fb1268c00423fd684777e0393565927ed4108e08 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.net.eap.crypto;
import static com.android.internal.net.utils.BigIntegerUtils.bigIntegerToUnsignedByteArray;
import static com.android.internal.net.utils.BigIntegerUtils.unsignedByteArrayToBigInteger;
import com.android.org.bouncycastle.crypto.digests.SHA1Digest;
import java.math.BigInteger;
import java.nio.ByteBuffer;
/**
* This class implements the Pseudo-Random Number Generator as specified by FIPS 186-2, change
* notice 1, as required by EAP-SIM (RFC 4186) and EAP-AKA (RFC 4187).
*
* <p>Simplifying constraints allow for some parameters to be permanently fixed: The "b" parameter
* is specified to always be 160 by RFC 4186. Likewise, the seed must be 20 bytes and therefore can
* never exceed 2^b.
*/
public class Fips186_2Prf {
private static final int SEED_LEN_BYTES = 20;
private static final int SHA_OUTPUT_LEN_BYTES = 40;
/**
* Gets the next random based on the PRF described in FIPS 186-2, Change Notice 1.
*
* @param seed the seed value
* @param outputLenBytes the output byte count required. Will run multiple iterations as needed.
* @return the byte-array random result
*/
public byte[] getRandom(byte[] seed, int outputLenBytes) {
if (seed.length != SEED_LEN_BYTES) {
throw new IllegalArgumentException("Invalid seed length. Must be 160b (20B)");
}
BigInteger xkey = unsignedByteArrayToBigInteger(seed);
BigInteger exp_b = new BigInteger("2").pow(SEED_LEN_BYTES * 8);
ByteBuffer buffer = ByteBuffer.allocate(outputLenBytes);
// RFC 4186, Appendix B, Step 3:
// M is the number of generation runs, based on the desired output bytes (rounded up)
// EAP SIM/AKA require at least 16 (K_ENCR) + 16 (K_AUTH) + 64 (MSK) + 64 (EMSK) bytes
// (total 160 Bytes)
int numIterations = (outputLenBytes + SHA_OUTPUT_LEN_BYTES - 1) / SHA_OUTPUT_LEN_BYTES;
for (int j = 0; j < numIterations; j++) {
for (int i = 0; i < 2; i++) {
// RFC 4186, Appendix B, Step 3.1 XSEED_j = 0
// (XSEED_j unused, omitted)
// RFC 4186, Appendix B, Step 3.2a: XVAL = (XKEY + XSEED_j) mod 2^b
// (XSEED_j unused, omitted)
BigInteger xval = xkey.mod(exp_b);
// RFC 4186, Appendix B, Step 3.2b:
// w_i = G(t, XVAL)
byte[] w_i = new byte[SEED_LEN_BYTES];
Sha1_186_2_FunctionG digest = new Sha1_186_2_FunctionG();
digest.update(
bigIntegerToUnsignedByteArray(xval, SEED_LEN_BYTES), 0, SEED_LEN_BYTES);
digest.doFinal(w_i, 0);
// RFC 4186, Appendix B, Step 3.2c:
// XKEY = (1 + XKEY + w_i) mod 2^b
xkey = xkey.add(BigInteger.ONE).add(unsignedByteArrayToBigInteger(w_i));
xkey = xkey.mod(exp_b);
// RFC 4186, Appendix B, Step 3.3:
// x_j = w_0|w_1
buffer.put(w_i, 0, Math.min(buffer.remaining(), w_i.length));
}
}
return buffer.array();
}
/**
* This inner class represents the modifications to the SHA1 digest required for FIPS 186-2 PRFs
*
* <p>FIPS 186-2 requires the use of a hashing function with exactly the same transforms as
* SHA1, but with a slightly different padding (all 0s instead of appending additional metadata
* for entropy).
*
* <p>Specifically, this function extends BouncyCastle's SHA1Digest in order to override the
* finish method, preventing the additional padding bytes from being appended (leaving them all
* 0).
*/
private static class Sha1_186_2_FunctionG extends SHA1Digest {
// IV (t) specified by SHA-1; Use default.
@Override
public void finish() {
// Don't do any other processing. We only care about the processBlock() functionality.
processBlock();
}
}
}