aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorevitayan <evitayan@google.com>2020-04-22 14:24:30 -0700
committerevitayan <evitayan@google.com>2020-04-24 19:13:30 -0700
commitffb24e8ed3ef96fb335d33ed16d6ed3213ad7974 (patch)
tree3de956a92781b9928700c0ea21b012837c30214c
parentfe1329b1637308e570acc9656388425334722460 (diff)
downloadike-ffb24e8ed3ef96fb335d33ed16d6ed3213ad7974.tar.gz
Create deterministic secure random to support IKE test mode
This CL also adds another IkeKePayload constructor for testing that the deterministic secure random works for DH exchange Bug: 148689509 Test: atest FrameworksIkeTests(new tests added) Change-Id: Ic6ed919d13416dcd1015602634ea11c302eaa745
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java32
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandom.java114
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandomTest.java83
3 files changed, 225 insertions, 4 deletions
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java
index f06f1512..4692dfa2 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeKePayload.java
@@ -140,16 +140,40 @@ public final class IkeKePayload extends IkePayload {
/**
* Construct an instance of IkeKePayload for building an outbound packet.
*
- * <p>Generate a DH key pair. Cache the private key and and send out the public key as
+ * <p>Generate a DH key pair. Cache the private key and send out the public key as
* keyExchangeData.
*
* <p>Critical bit in this payload must not be set as instructed in RFC 7296.
*
+ * <p>TODO(b/148689509): Remove this constructor in the following CL that injects
+ * DeterministicSecureRandom to the process of IKE Session creation.
+ *
* @param dh DH group for this KE payload
* @see <a href="https://tools.ietf.org/html/rfc7296#page-76">RFC 7296, Internet Key Exchange
* Protocol Version 2 (IKEv2), Critical.
*/
public IkeKePayload(@SaProposal.DhGroup int dh) {
+ this(dh, null /* secureRandom */);
+ }
+
+ /**
+ * Construct an instance of IkeKePayload for building an outbound packet.
+ *
+ * <p>Generate a DH key pair. Cache the private key and send out the public key as
+ * keyExchangeData.
+ *
+ * <p>Critical bit in this payload must not be set as instructed in RFC 7296.
+ *
+ * <p>TODO(b/148689509): Pass in a SecureRandom factory in the following CL that injects
+ * DeterministicSecureRandom to the process of IKE Session creation.
+ *
+ * @param dh DH group for this KE payload
+ * @param secureRandom the source of secure randomness, if null, a SecureRandom will be
+ * constructed internally
+ * @see <a href="https://tools.ietf.org/html/rfc7296#page-76">RFC 7296, Internet Key Exchange
+ * Protocol Version 2 (IKEv2), Critical.
+ */
+ public IkeKePayload(@SaProposal.DhGroup int dh, SecureRandom secureRandom) {
super(PAYLOAD_TYPE_KE, false);
dhGroup = dh;
@@ -191,9 +215,9 @@ public final class IkeKePayload extends IkePayload {
DHParameterSpec dhParams = new DHParameterSpec(prime, baseGen);
KeyPairGenerator dhKeyPairGen = KeyPairGenerator.getInstance(KEY_EXCHANGE_ALGORITHM);
- // By default SecureRandom uses AndroidOpenSSL provided SHA1PRNG Algorithm, which takes
- // /dev/urandom as seed source.
- dhKeyPairGen.initialize(dhParams, new SecureRandom());
+
+ SecureRandom random = secureRandom == null ? new SecureRandom() : secureRandom;
+ dhKeyPairGen.initialize(dhParams, random);
KeyPair keyPair = dhKeyPairGen.generateKeyPair();
diff --git a/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandom.java b/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandom.java
new file mode 100644
index 00000000..e09fa0be
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandom.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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.ipsec.ike.testmode;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.crypto.KeyGenerationUtils;
+import com.android.internal.util.HexDump;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Deterministic SecureRandom to support test mode IKE library.
+ *
+ * <p>Deterministic SecureRandom MUST only be used in test mode. It allows IKE library to do IKE
+ * Session negotiation with pre captured responder packets as well as always generates the same set
+ * of crypto keys.
+ *
+ * <p>This deterministic SecureRandom is deterministic in the way that if caller constructs multiple
+ * instances and call the same methods from these instances, all the outputs will be the same. This
+ * class is random in the way that, from the same intance, caller will get different outputs by
+ * calling the same method multiple times.
+ */
+@VisibleForTesting
+public class DeterministicSecureRandom extends SecureRandom
+ implements KeyGenerationUtils.ByteSigner {
+ private static final String TAG = DeterministicSecureRandom.class.getSimpleName();
+
+ private static final String MAC_SHA256_NAME = "HmacSHA256";
+ private static final String MAC_SHA256_KEY_HEX =
+ "5D00F680E84F96374FC1BF8A4FC5F711467CBC62DF81A3B6169812531DF13E6C";
+ private static final String INITIAL_BYTE_TO_SIGN_HEX =
+ "2514A9C2B797BDDC50A1975A00866C3CC87190C29DCEBB228A4D8730AF8881BC";
+
+ private final Mac mByteSignerMac;
+
+ private byte[] mBytesToSign;
+
+ @VisibleForTesting
+ public DeterministicSecureRandom() {
+ super(null /*secureRandomSpi*/, null /*provider*/);
+
+ try {
+ mByteSignerMac = Mac.getInstance(MAC_SHA256_NAME);
+ byte[] byteSignerKey = HexDump.hexStringToByteArray(MAC_SHA256_KEY_HEX);
+ mByteSignerMac.init(new SecretKeySpec(byteSignerKey, MAC_SHA256_NAME));
+ } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+ // Should never happen
+ throw new IllegalArgumentException("Failed to construct DeterministicSecureRandom", e);
+ }
+
+ mBytesToSign = HexDump.hexStringToByteArray(INITIAL_BYTE_TO_SIGN_HEX);
+ }
+
+ @Override
+ public byte[] signBytes(byte[] keyBytes, byte[] dataToSign) {
+ mByteSignerMac.update(dataToSign);
+ return mByteSignerMac.doFinal();
+ }
+
+ private byte[] generateBytes(int numBytes) {
+ mBytesToSign =
+ KeyGenerationUtils.prfPlus(
+ this, null /* keyBytes; unused */, mBytesToSign, numBytes);
+ return mBytesToSign;
+ }
+
+ @Override
+ public byte[] generateSeed(int numBytes) {
+ return generateBytes(numBytes);
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return TAG;
+ }
+
+ // This method serves to provide a source of random bits to all of the method inherited from
+ // {@link Random} (for example, {@link nextInt}, {@link nextLong}).
+ @Override
+ public void nextBytes(byte[] bytes) {
+ ByteBuffer buffer = ByteBuffer.wrap(generateBytes(bytes.length));
+ buffer.rewind();
+ buffer.get(bytes);
+ }
+
+ @Override
+ public void setSeed(byte[] seed) {
+ // Do nothing
+ }
+
+ @Override
+ public void setSeed(long seed) {
+ // Do nothing
+ }
+}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandomTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandomTest.java
new file mode 100644
index 00000000..4ff1c12c
--- /dev/null
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/testmode/DeterministicSecureRandomTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.ipsec.ike.testmode;
+
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.internal.net.ipsec.ike.message.IkeKePayload;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import javax.crypto.spec.DHPrivateKeySpec;
+
+public final class DeterministicSecureRandomTest {
+ private static final int BYTE_ARRAY_LEN = 20;
+
+ @Test
+ public void testDeterministicSecureRandomNonRepeating() throws Exception {
+ DeterministicSecureRandom secureRandom = new DeterministicSecureRandom();
+
+ assertFalse(
+ Arrays.equals(
+ secureRandom.generateSeed(BYTE_ARRAY_LEN),
+ secureRandom.generateSeed(BYTE_ARRAY_LEN)));
+ assertNotEquals(secureRandom.nextLong(), secureRandom.nextLong());
+
+ byte[] randomBytesOne = new byte[BYTE_ARRAY_LEN];
+ byte[] randomBytesTwo = new byte[BYTE_ARRAY_LEN];
+ secureRandom.nextBytes(randomBytesOne);
+ secureRandom.nextBytes(randomBytesTwo);
+ assertFalse(Arrays.equals(randomBytesOne, randomBytesTwo));
+ }
+
+ @Test
+ public void testTwoDeterministicSecureRandomsAreDeterministic() throws Exception {
+ DeterministicSecureRandom srOne = new DeterministicSecureRandom();
+ DeterministicSecureRandom srTwo = new DeterministicSecureRandom();
+
+ assertArrayEquals(srOne.generateSeed(BYTE_ARRAY_LEN), srTwo.generateSeed(BYTE_ARRAY_LEN));
+ assertEquals(srOne.nextLong(), srTwo.nextLong());
+
+ byte[] randomBytesOne = new byte[BYTE_ARRAY_LEN];
+ byte[] randomBytesTwo = new byte[BYTE_ARRAY_LEN];
+ srOne.nextBytes(randomBytesOne);
+ srTwo.nextBytes(randomBytesTwo);
+ assertArrayEquals(randomBytesOne, randomBytesTwo);
+ }
+
+ @Test
+ public void testDeterministicSecureRandomInKePayload() throws Exception {
+ DeterministicSecureRandom srOne = new DeterministicSecureRandom();
+ DeterministicSecureRandom srTwo = new DeterministicSecureRandom();
+
+ IkeKePayload kePayloadOne = new IkeKePayload(DH_GROUP_2048_BIT_MODP, srOne);
+ IkeKePayload kePayloadTwo = new IkeKePayload(DH_GROUP_2048_BIT_MODP, srTwo);
+ assertArrayEquals(kePayloadOne.keyExchangeData, kePayloadTwo.keyExchangeData);
+
+ DHPrivateKeySpec localPrivateKeyOne = kePayloadOne.localPrivateKey;
+ DHPrivateKeySpec localPrivateKeyTwo = kePayloadTwo.localPrivateKey;
+ assertEquals(localPrivateKeyOne.getG(), localPrivateKeyTwo.getG());
+ assertEquals(localPrivateKeyOne.getP(), localPrivateKeyTwo.getP());
+ assertEquals(localPrivateKeyOne.getX(), localPrivateKeyTwo.getX());
+ }
+}