Implement MSK generation for EAP MSCHAPv2.

AwaitingEapSuccessState will return an EapSuccess object when an
EAP-Success message is received. However, this requires generating the
MSK for the current session first. Utils are defined for generating the
MSK as defined in RFC 3079. Only 128-bit (16B) keys are generated with
this implementation.

Bug: 141483998
Test: added tests for EapMsChapV2MethodStateMachineTest.
Test: atest FrameworksIkeTests
Change-Id: Ia472cc7260bb270ab5c5069c309b8cac89b2d719
diff --git a/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachine.java b/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachine.java
index 4b7e253..d5a27de 100644
--- a/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachine.java
+++ b/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachine.java
@@ -102,13 +102,17 @@
     private static final int Z_PASSWORD_HASH_LEN = 21;
     private static final int Z_PASSWORD_SECTION_LEN = 7;
     private static final int RESPONSE_SECTION_LEN = 8;
+    private static final int SHS_PAD_LEN = 40;
+    private static final int MASTER_KEY_LEN = 16;
+    private static final int SESSION_KEY_LEN = 16;
+    private static final int MASTER_SESSION_KEY_LEN = 2 * SESSION_KEY_LEN;
 
     // Reserved for future use and must be 0 (EAP MSCHAPv2#2.2)
     private static final int FLAGS = 0;
 
     // we all need a little magic in our lives
-    // Defined in RFC 2759#8.7. Constants used for response Success response generation.
-    private static final byte[] MAGIC_1 = {
+    // Defined in RFC 2759#8.7. Constants used for Success response generation.
+    private static final byte[] CHALLENGE_MAGIC_1 = {
         (byte) 0x4D, (byte) 0x61, (byte) 0x67, (byte) 0x69, (byte) 0x63, (byte) 0x20, (byte) 0x73,
         (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x74,
         (byte) 0x6F, (byte) 0x20, (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
@@ -116,7 +120,7 @@
         (byte) 0x6E, (byte) 0x67, (byte) 0x20, (byte) 0x63, (byte) 0x6F, (byte) 0x6E, (byte) 0x73,
         (byte) 0x74, (byte) 0x61, (byte) 0x6E, (byte) 0x74
     };
-    private static final byte[] MAGIC_2 = {
+    private static final byte[] CHALLENGE_MAGIC_2 = {
         (byte) 0x50, (byte) 0x61, (byte) 0x64, (byte) 0x20, (byte) 0x74, (byte) 0x6F, (byte) 0x20,
         (byte) 0x6D, (byte) 0x61, (byte) 0x6B, (byte) 0x65, (byte) 0x20, (byte) 0x69, (byte) 0x74,
         (byte) 0x20, (byte) 0x64, (byte) 0x6F, (byte) 0x20, (byte) 0x6D, (byte) 0x6F, (byte) 0x72,
@@ -125,6 +129,54 @@
         (byte) 0x72, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6F, (byte) 0x6E
     };
 
+    // Defined in RFC 3079#3.4. Constants used for Master Session Key (MSK) generation
+    private static final byte[] SHS_PAD_1 = new byte[SHS_PAD_LEN];
+    private static final byte[] SHS_PAD_2 = new byte[SHS_PAD_LEN];
+
+    static {
+        Arrays.fill(SHS_PAD_2, (byte) 0xF2);
+    }
+
+    private static final byte[] MSK_MAGIC_1 = {
+        (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69,
+        (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x20,
+        (byte) 0x4D, (byte) 0x50, (byte) 0x50, (byte) 0x45, (byte) 0x20, (byte) 0x4D,
+        (byte) 0x61, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x20,
+        (byte) 0x4B, (byte) 0x65, (byte) 0x79
+    };
+    private static final byte[] MSK_MAGIC_2 = {
+        (byte) 0x4F, (byte) 0x6E, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65,
+        (byte) 0x20, (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
+        (byte) 0x74, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x64, (byte) 0x65,
+        (byte) 0x2C, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x69, (byte) 0x73,
+        (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68,
+        (byte) 0x65, (byte) 0x20, (byte) 0x73, (byte) 0x65, (byte) 0x6E, (byte) 0x64,
+        (byte) 0x20, (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x3B, (byte) 0x20,
+        (byte) 0x6F, (byte) 0x6E, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65,
+        (byte) 0x20, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65,
+        (byte) 0x72, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x64, (byte) 0x65,
+        (byte) 0x2C, (byte) 0x20, (byte) 0x69, (byte) 0x74, (byte) 0x20, (byte) 0x69,
+        (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x20,
+        (byte) 0x72, (byte) 0x65, (byte) 0x63, (byte) 0x65, (byte) 0x69, (byte) 0x76,
+        (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x2E
+    };
+    private static final byte[] MSK_MAGIC_3 = {
+        (byte) 0x4F, (byte) 0x6E, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x65,
+        (byte) 0x20, (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
+        (byte) 0x74, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x64, (byte) 0x65,
+        (byte) 0x2C, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x69, (byte) 0x73,
+        (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x74, (byte) 0x68,
+        (byte) 0x65, (byte) 0x20, (byte) 0x72, (byte) 0x65, (byte) 0x63, (byte) 0x65,
+        (byte) 0x69, (byte) 0x76, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
+        (byte) 0x79, (byte) 0x3B, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x20,
+        (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x20, (byte) 0x73, (byte) 0x65,
+        (byte) 0x72, (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x73,
+        (byte) 0x69, (byte) 0x64, (byte) 0x65, (byte) 0x2C, (byte) 0x20, (byte) 0x69,
+        (byte) 0x74, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x74,
+        (byte) 0x68, (byte) 0x65, (byte) 0x20, (byte) 0x73, (byte) 0x65, (byte) 0x6E,
+        (byte) 0x64, (byte) 0x20, (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x2E
+    };
+
     private final EapMsChapV2Config mEapMsChapV2Config;
     private final SecureRandom mSecureRandom;
     private final EapMsChapV2TypeDataDecoder mTypeDataDecoder;
@@ -488,14 +540,14 @@
         MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG);
         sha1.update(passwordHashHash);
         sha1.update(ntResponse);
-        sha1.update(MAGIC_1); // add just a dash of magic
+        sha1.update(CHALLENGE_MAGIC_1); // add just a dash of magic
         byte[] digest = sha1.digest();
 
         byte[] challenge = challengeHash(peerChallenge, authenticatorChallenge, username);
 
         sha1.update(digest);
         sha1.update(challenge);
-        sha1.update(MAGIC_2);
+        sha1.update(CHALLENGE_MAGIC_2);
 
         return sha1.digest();
     }
@@ -515,4 +567,44 @@
                         password, ntResponse, peerChallenge, authenticatorChallenge, userName);
         return Arrays.equals(myResponse, receivedResponse);
     }
+
+    /* Implementation of RFC 3079#3.4: GetMasterKey() */
+    @VisibleForTesting
+    static byte[] getMasterKey(byte[] passwordHashHash, byte[] ntResponse)
+            throws GeneralSecurityException {
+        MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG);
+        sha1.update(passwordHashHash);
+        sha1.update(ntResponse);
+        sha1.update(MSK_MAGIC_1);
+        return Arrays.copyOf(sha1.digest(), MASTER_KEY_LEN);
+    }
+
+    /* Implementation of RFC 3079#3.4: GetAsymmetricStartKey() */
+    @VisibleForTesting
+    static byte[] getAsymmetricStartKey(byte[] masterKey, boolean isSend)
+            throws GeneralSecurityException {
+        // salt: referred to as 's' in RFC 3079#3.4 GetAsymmetricStartKey()
+        byte[] salt = isSend ? MSK_MAGIC_2 : MSK_MAGIC_3;
+        MessageDigest sha1 = MessageDigest.getInstance(SHA_ALG);
+        sha1.update(masterKey);
+        sha1.update(SHS_PAD_1);
+        sha1.update(salt);
+        sha1.update(SHS_PAD_2);
+        return Arrays.copyOf(sha1.digest(), SESSION_KEY_LEN);
+    }
+
+    @VisibleForTesting
+    static byte[] generateMsk(String password, byte[] ntResponse)
+            throws GeneralSecurityException, UnsupportedEncodingException {
+        byte[] passwordHash = ntPasswordHash(password);
+        byte[] passwordHashHash = hashNtPasswordHash(passwordHash);
+        byte[] masterKey = getMasterKey(passwordHashHash, ntResponse);
+
+        // MSK: SendKey + ReceiveKey
+        ByteBuffer msk = ByteBuffer.allocate(MASTER_SESSION_KEY_LEN);
+        msk.put(getAsymmetricStartKey(masterKey, true /* isSend */));
+        msk.put(getAsymmetricStartKey(masterKey, false /* isSend */));
+
+        return msk.array();
+    }
 }
diff --git a/tests/iketests/src/java/com/android/ike/eap/message/EapTestMessageDefinitions.java b/tests/iketests/src/java/com/android/ike/eap/message/EapTestMessageDefinitions.java
index a2a811a..50e88a8 100644
--- a/tests/iketests/src/java/com/android/ike/eap/message/EapTestMessageDefinitions.java
+++ b/tests/iketests/src/java/com/android/ike/eap/message/EapTestMessageDefinitions.java
@@ -236,7 +236,7 @@
     public static final byte[] EAP_REQUEST_MSCHAP_V2 =
             hexStringToByteArray("01" + ID + "00061A01");
 
-    // MSCHAPv2 Test vectors taken from RFC 2759#9.2
+    // MSCHAPv2 Test vectors taken from RFC 2759#9.2 and RFC 3079#3.5.3
     public static final String MSCHAP_V2_USERNAME = "User";
     public static final String MSCHAP_V2_USERNAME_HEX = "55736572";
     public static final byte[] MSCHAP_V2_USERNAME_ASCII_BYTES =
@@ -262,6 +262,20 @@
             hexStringToByteArray(MSCHAP_V2_NT_RESPONSE_STRING);
     public static final byte[] MSCHAP_V2_AUTHENTICATOR_RESPONSE =
             hexStringToByteArray("407A5589115FD0D6209F510FE9C04566932CDA56");
+    public static final byte[] MSCHAP_V2_MASTER_KEY =
+            hexStringToByteArray("FDECE3717A8C838CB388E527AE3CDD31");
+
+    // generated based on RFC 3079#3.5.3 params
+    public static final String SEND_KEY = "D5F0E9521E3EA9589645E86051C82226";
+    public static final byte[] MSCHAP_V2_SEND_START_KEY = hexStringToByteArray(SEND_KEY);
+
+    // This value is labeled 'send key' in RFC 3079#3.5.3. However, it's used as 'receive key' here,
+    // because send and receive keys are swapped for peers relative to authenticators.
+    public static final String RECEIVE_KEY = "8B7CDC149B993A1BA118CB153F56DCCB";
+    public static final byte[] MSCHAP_V2_RECEIVE_START_KEY = hexStringToByteArray(RECEIVE_KEY);
+
+    // MSK: MSCHAP_V2_SEND_START_KEY + MSCHAP_V2_RECEIVE_START_KEY
+    public static final byte[] MSCHAP_V2_MSK = hexStringToByteArray(SEND_KEY + RECEIVE_KEY);
 
     public static final String MSCHAP_V2_ID = "42";
     public static final int MSCHAP_V2_ID_INT = Integer.parseInt(MSCHAP_V2_ID, 16 /* radix */);
diff --git a/tests/iketests/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachineTest.java b/tests/iketests/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachineTest.java
index f671a42..4d57e43 100644
--- a/tests/iketests/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/ike/eap/statemachine/EapMsChapV2MethodStateMachineTest.java
@@ -20,12 +20,16 @@
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_CHALLENGE;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_RESPONSE;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_CHALLENGE;
+import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_MASTER_KEY;
+import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_MSK;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_NT_RESPONSE;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_HASH;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_HASH_HASH;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_UTF_BYTES;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_PEER_CHALLENGE;
+import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_RECEIVE_START_KEY;
+import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_SEND_START_KEY;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME;
 import static com.android.ike.eap.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME_ASCII_BYTES;
 
@@ -35,6 +39,7 @@
 
 import com.android.ike.eap.EapSessionConfig.EapMsChapV2Config;
 import com.android.ike.eap.statemachine.EapMsChapV2MethodStateMachine.CreatedState;
+import com.android.ike.utils.Log;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -143,4 +148,34 @@
                         MSCHAP_V2_USERNAME,
                         MSCHAP_V2_AUTHENTICATOR_RESPONSE));
     }
+
+    @Test
+    public void testGetMasterKey() throws Exception {
+        byte[] masterKey =
+                EapMsChapV2MethodStateMachine.getMasterKey(
+                        MSCHAP_V2_PASSWORD_HASH_HASH, MSCHAP_V2_NT_RESPONSE);
+        assertArrayEquals(MSCHAP_V2_MASTER_KEY, masterKey);
+    }
+
+    @Test
+    public void testGetAsymmetricStartKeySendKey() throws Exception {
+        byte[] startKey =
+                EapMsChapV2MethodStateMachine.getAsymmetricStartKey(MSCHAP_V2_MASTER_KEY, true);
+        assertArrayEquals(Log.byteArrayToHexString(startKey), MSCHAP_V2_SEND_START_KEY, startKey);
+    }
+
+    @Test
+    public void testGetAsymmetricStartKeyReceiveKey() throws Exception {
+        byte[] receiveKey =
+                EapMsChapV2MethodStateMachine.getAsymmetricStartKey(MSCHAP_V2_MASTER_KEY, false);
+        assertArrayEquals(MSCHAP_V2_RECEIVE_START_KEY, receiveKey);
+    }
+
+    @Test
+    public void testGenerateMsk() throws Exception {
+        byte[] msk =
+                EapMsChapV2MethodStateMachine.generateMsk(
+                        MSCHAP_V2_PASSWORD, MSCHAP_V2_NT_RESPONSE);
+        assertArrayEquals(MSCHAP_V2_MSK, msk);
+    }
 }