blob: a0eb98d7c341292fea675f1655c075b9aad83777 [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.test;
import static android.telephony.TelephonyManager.APPTYPE_USIM;
import static com.android.internal.net.TestUtils.hexStringToByteArray;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.EAP_REQUEST_SIM_START_PACKET;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.eap.test.EapSessionConfig;
import android.telephony.TelephonyManager;
import com.android.internal.net.eap.test.statemachine.EapStateMachine;
import org.junit.Before;
import org.junit.Test;
/**
* This test verifies that EAP-AKA is functional for an end-to-end implementation.
*
* <p>This test uses externally generated test vectors.
*/
public class EapAkaTest extends EapMethodEndToEndTest {
private static final long AUTHENTICATOR_TIMEOUT_MILLIS = 250L;
private static final int SUB_ID = 1;
private static final String UNFORMATTED_IDENTITY = "123456789012345"; // IMSI
// EAP_IDENTITY = hex("test@android.net")
private static final byte[] EAP_IDENTITY =
hexStringToByteArray("7465737440616E64726F69642E6E6574");
// TODO(b/140797965): find valid AUTN/RAND values for the CTS test sim
// CK: 070AC6E26957E00C83A4B577210A8AEC
// IK: 0B48923D40B48C476B0EE8A43F780356
// MK: B8F5AC1C7A5F5B8D5579A6F22FD18E4EEB0B55C3
// K_encr: 1C2B848ADA2B9485C52517D1A92BF4AB
// K_aut: C9500EC59DC62C7D7F5E9E445FA1A3C4
private static final String RAND_1 = "A789798E3E75560EA3EA5E41A043753A";
private static final String RAND_2 = "000102030405060708090A0B0C0D0E0F";
private static final String AUTN = "236C38DD8772000165A96F168F5C14E2";
private static final String RES = "2B1C2593F5AF288A3766CADA9CE23FB9";
private static final String AUTS = "0102030405060708090A0B0C0D0E";
private static final byte[] MSK =
hexStringToByteArray(
"40B9767F5D333645D3B72E9E57EFFA9D"
+ "C9D1E7EB598D907948DADB5AD968D520"
+ "48F0C56A0D68E37E9482F77BC8F68990"
+ "5EAB7114ECA9FC4AB99D0920D76CF1CF");
private static final byte[] EMSK =
hexStringToByteArray(
"F3463F6ADD56EE5360C81DEF017FD57D"
+ "CBABF12EA45D2CC815F4334AB7BE8523"
+ "4FEAF76FAA02EEF963B25C9DC95308AF"
+ "72A937FF04FF2C6D1F172FD9111411EE");
// CK: 070AC6E26957E00C83A4B577210A8AEC
// IK: 0B48923D40B48C476B0EE8A43F780356
// MK: B8F5AC1C7A5F5B8D5579A6F22FD18E4EEB0B55C3
// K_encr: 1F6AB828DD3CF1931E6E62038728C47C
// K_aut: F035D5CA7512389F4F96454EA6737B7A
private static final byte[] MSK_WITHOUT_IDENTITY_REQ =
hexStringToByteArray(
"111E1F3118B373F0658230785066F767"
+ "A238BF780BC77CA77DA3F7A3FE411BE8"
+ "0FCD763DE7FBCADB7EB9BDEF7F0E7B1D"
+ "2D5CA87C38AC0D3B8F99B882ECB4E840");
private static final byte[] EMSK_WITHOUT_IDENTITY_REQ =
hexStringToByteArray(
"CD1DD12100B99BC029B0C97777C42BD6"
+ "2FF24A32694D7EA209E3B3DBCC7B9CE9"
+ "EFD69A3A62CBDBDFA1CECD0F17B0E620"
+ "D648AA41D55AE0375BAD44C8F1A424BE");
// Base 64 of: [Length][RAND_1][Length][AUTN]
private static final String BASE64_CHALLENGE_1 =
"EKeJeY4+dVYOo+peQaBDdToQI2w43YdyAAFlqW8Wj1wU4g==";
// Base 64 of: ['DB'][Length][RES][Length][CK][Length][IK]
private static final String BASE_64_RESPONSE_SUCCESS =
"2xArHCWT9a8oijdmytqc4j+5EAcKxuJpV+AMg6S1dyEKiuwQC0iSPUC0jEdrDuikP3gDVg==";
// Base 64 of: [Length][RAND_2][Length][AUTN]
private static final String BASE64_CHALLENGE_2 =
"EAABAgMEBQYHCAkKCwwNDg8QI2w43YdyAAFlqW8Wj1wU4g==";
// Base 64 of: ['DC'][Length][AUTS]
private static final String BASE_64_RESPONSE_SYNC_FAIL = "3A4BAgMEBQYHCAkKCwwNDg==";
private static final String REQUEST_MAC = "8E733917F65C54EE820195EFB94246DC";
private static final String RESPONSE_MAC = "E5A74AE0840C1E75E0C471D5A915AB7F";
private static final String REQUEST_MAC_WITHOUT_IDENTITY_REQ =
"B66F674A14D96D358E8682230DF86253";
private static final String RESPONSE_MAC_WITHOUT_IDENTITY_REQ =
"631BEABB876F072B49D453B660FAE748";
private static final byte[] EAP_AKA_IDENTITY_REQUEST =
hexStringToByteArray(
"01CD000C" // EAP-Request | ID | length in bytes
+ "17050000" // EAP-AKA | Identity | 2B padding
+ "0D010000"); // AT_ANY_ID_REQ attribute
private static final byte[] EAP_AKA_IDENTITY_RESPONSE =
hexStringToByteArray(
"02CD001C" // EAP-Response | ID | length in bytes
+ "17050000" // EAP-AKA | Identity | 2B padding
+ "0E05001030313233343536373839303132333435"); // AT_IDENTITY attribute
private static final byte[] EAP_AKA_CHALLENGE_REQUEST =
hexStringToByteArray(
"01CE0044" // EAP-Request | ID | length in bytes
+ "17010000" // EAP-AKA | Challenge | 2B padding
+ "01050000" + RAND_1 // AT_RAND attribute
+ "02050000" + AUTN // AT_AUTN attribute
+ "0B050000" + REQUEST_MAC); // AT_MAC attribute
private static final byte[] EAP_AKA_CHALLENGE_RESPONSE =
hexStringToByteArray(
"02CE0030" // EAP-Response | ID | length in bytes
+ "17010000" // EAP-AKA | Challenge | 2B padding
+ "03050080" + RES // AT_RES attribute
+ "0B050000" + RESPONSE_MAC); // AT_MAC attribute
private static final byte[] EAP_AKA_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ =
hexStringToByteArray(
"019B0044" // EAP-Request | ID | length in bytes
+ "17010000" // EAP-AKA | Challenge | 2B padding
+ "01050000" + RAND_1 // AT_RAND attribute
+ "02050000" + AUTN // AT_AUTN attribute
+ "0B050000" + REQUEST_MAC_WITHOUT_IDENTITY_REQ); // AT_MAC attribute
private static final byte[] EAP_AKA_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQUEST =
hexStringToByteArray(
"029B0030" // EAP-Response | ID | length in bytes
+ "17010000" // EAP-AKA | Challenge | 2B padding
+ "03050080" + RES // AT_RES attribute
+ "0B050000" + RESPONSE_MAC_WITHOUT_IDENTITY_REQ); // AT_MAC attribute
private static final byte[] EAP_AKA_CHALLENGE_REQUEST_SYNC_FAIL =
hexStringToByteArray(
"01CE0044" // EAP-Request | ID | length in bytes
+ "17010000" // EAP-AKA | Challenge | 2B padding
+ "01050000" + RAND_2 // AT_RAND attribute
+ "02050000" + AUTN // AT_AUTN attribute
+ "0B050000" + REQUEST_MAC); // AT_MAC attribute
private static final byte[] EAP_AKA_SYNC_FAIL_RESPONSE =
hexStringToByteArray(
"02CE0018" // EAP-Response | ID | length in bytes
+ "17040000" // EAP-AKA | Synchronization-Failure | 2B padding
+ "0404" + AUTS); // AT_AUTS attribute
private static final byte[] EAP_AKA_AUTHENTICATION_REJECT =
hexStringToByteArray(
"02CE0008" // EAP-Response | ID | length in bytes
+ "17020000"); // EAP-AKA | Authentication-Reject | 2B padding
private static final byte[] EAP_RESPONSE_NAK_PACKET =
hexStringToByteArray("021000060317"); // NAK with EAP-AKA listed
private TelephonyManager mMockTelephonyManager;
@Before
@Override
public void setUp() {
super.setUp();
mMockTelephonyManager = mock(TelephonyManager.class);
mEapSessionConfig =
new EapSessionConfig.Builder()
.setEapIdentity(EAP_IDENTITY)
.setEapAkaConfig(SUB_ID, APPTYPE_USIM)
.build();
mEapAuthenticator =
new EapAuthenticator(
mTestLooper.getLooper(),
mMockCallback,
new EapStateMachine(mMockContext, mEapSessionConfig, mMockSecureRandom),
(runnable) -> runnable.run(),
AUTHENTICATOR_TIMEOUT_MILLIS);
TelephonyManager mockTelephonyManagerFromContext = mock(TelephonyManager.class);
doReturn(mockTelephonyManagerFromContext)
.when(mMockContext)
.getSystemService(Context.TELEPHONY_SERVICE);
doReturn(mMockTelephonyManager)
.when(mockTelephonyManagerFromContext)
.createForSubscriptionId(SUB_ID);
}
@Test
public void testEapAkaEndToEnd() {
verifyEapAkaIdentity();
verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE);
verifyEapSuccess(MSK, EMSK);
}
@Test
public void testEapAkaEndToEndWithoutIdentityRequest() {
verifyEapAkaChallengeWithoutIdentityReq();
verifyEapSuccess(MSK_WITHOUT_IDENTITY_REQ, EMSK_WITHOUT_IDENTITY_REQ);
}
@Test
public void testEapAkaWithEapNotifications() {
verifyEapNotification(1);
verifyEapAkaIdentity();
verifyEapNotification(2);
verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE);
verifyEapNotification(3);
verifyEapSuccess(MSK, EMSK);
}
@Test
public void testEapAkaUnsupportedType() {
verifyUnsupportedType(EAP_REQUEST_SIM_START_PACKET, EAP_RESPONSE_NAK_PACKET);
verifyEapAkaIdentity();
verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE);
verifyEapSuccess(MSK, EMSK);
}
@Test
public void testEapAkaSynchronizationFailure() {
verifyEapAkaIdentity();
verifyEapAkaSynchronizationFailure();
verifyEapAkaChallenge(BASE_64_RESPONSE_SUCCESS, EAP_AKA_CHALLENGE_RESPONSE);
verifyEapSuccess(MSK, EMSK);
}
@Test
public void testEapAkaAuthenticationReject() {
verifyEapAkaIdentity();
// return null from TelephonyManager to simluate rejection of AUTN
verifyEapAkaChallenge(null, EAP_AKA_AUTHENTICATION_REJECT);
verifyExpectsEapFailure(EAP_AKA_CHALLENGE_REQUEST);
verifyEapFailure();
}
private void verifyEapAkaIdentity() {
// EAP-AKA/Identity request
doReturn(UNFORMATTED_IDENTITY).when(mMockTelephonyManager).getSubscriberId();
mEapAuthenticator.processEapMessage(EAP_AKA_IDENTITY_REQUEST);
mTestLooper.dispatchAll();
// verify EAP-AKA/Identity response
verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE));
verify(mMockTelephonyManager).getSubscriberId();
verify(mMockCallback).onResponse(eq(EAP_AKA_IDENTITY_RESPONSE));
verifyNoMoreInteractions(
mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback);
}
private void verifyEapAkaChallenge(
String challengeBase64,
String responseBase64,
byte[] incomingEapPacket,
byte[] outgoingEapPacket) {
// EAP-AKA/Challenge request
when(mMockTelephonyManager.getIccAuthentication(
TelephonyManager.APPTYPE_USIM,
TelephonyManager.AUTHTYPE_EAP_AKA,
challengeBase64))
.thenReturn(responseBase64);
mEapAuthenticator.processEapMessage(incomingEapPacket);
mTestLooper.dispatchAll();
// verify EAP-AKA/Challenge response
verify(mMockTelephonyManager)
.getIccAuthentication(
TelephonyManager.APPTYPE_USIM,
TelephonyManager.AUTHTYPE_EAP_AKA,
challengeBase64);
verify(mMockCallback).onResponse(eq(outgoingEapPacket));
}
private void verifyEapAkaChallenge(String responseBase64, byte[] outgoingPacket) {
verifyEapAkaChallenge(
BASE64_CHALLENGE_1, responseBase64, EAP_AKA_CHALLENGE_REQUEST, outgoingPacket);
verifyNoMoreInteractions(
mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback);
}
private void verifyEapAkaChallengeWithoutIdentityReq() {
verifyEapAkaChallenge(
BASE64_CHALLENGE_1,
BASE_64_RESPONSE_SUCCESS,
EAP_AKA_CHALLENGE_REQUEST_WITHOUT_IDENTITY_REQ,
EAP_AKA_CHALLENGE_RESPONSE_WITHOUT_IDENTITY_REQUEST);
// also need to verify interactions with Context and TelephonyManager
verify(mMockContext).getSystemService(eq(Context.TELEPHONY_SERVICE));
verifyNoMoreInteractions(
mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback);
}
private void verifyEapAkaSynchronizationFailure() {
verifyEapAkaChallenge(
BASE64_CHALLENGE_2,
BASE_64_RESPONSE_SYNC_FAIL,
EAP_AKA_CHALLENGE_REQUEST_SYNC_FAIL,
EAP_AKA_SYNC_FAIL_RESPONSE);
verifyNoMoreInteractions(
mMockContext, mMockTelephonyManager, mMockSecureRandom, mMockCallback);
}
@Override
protected void verifyEapSuccess(byte[] msk, byte[] emsk) {
super.verifyEapSuccess(msk, emsk);
verifyNoMoreInteractions(mMockTelephonyManager);
}
}