aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-01-20 02:10:09 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-01-20 02:10:09 +0000
commit9f160604a2fd31811a1dcce06b5402f4335ee8af (patch)
tree361cea2304fa6b365422eeac4061360a79c30a13
parentb5e74658f8b8668d58bba3762594c2131dab1c88 (diff)
parent9aec82c7937ac1ccf2365da970b7e8023591321f (diff)
downloadservice_entitlement-9f160604a2fd31811a1dcce06b5402f4335ee8af.tar.gz
Snap for 7090944 from 9aec82c7937ac1ccf2365da970b7e8023591321f to sc-d1-release
Change-Id: Idb6bb95c68ba14956cc44d965bd642ae2fbd9319
-rw-r--r--java/com/android/libraries/entitlement/ServiceEntitlement.java14
-rw-r--r--java/com/android/libraries/entitlement/eapaka/EapAkaApi.java15
-rw-r--r--java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java9
-rw-r--r--java/com/android/libraries/entitlement/eapaka/MasterKey.java2
-rw-r--r--java/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java176
-rw-r--r--tests/Android.bp1
-rw-r--r--tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java49
-rw-r--r--tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java140
-rw-r--r--tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java153
-rw-r--r--tests/src/com/android/libraries/entitlement/eapaka/MasterKeyTest.java82
-rw-r--r--tests/src/com/android/libraries/entitlement/http/HttpClientTest.java109
11 files changed, 740 insertions, 10 deletions
diff --git a/java/com/android/libraries/entitlement/ServiceEntitlement.java b/java/com/android/libraries/entitlement/ServiceEntitlement.java
index c196783..abf6ace 100644
--- a/java/com/android/libraries/entitlement/ServiceEntitlement.java
+++ b/java/com/android/libraries/entitlement/ServiceEntitlement.java
@@ -20,6 +20,8 @@ import android.content.Context;
import com.android.libraries.entitlement.eapaka.EapAkaApi;
+import com.google.common.annotations.VisibleForTesting;
+
import java.util.List;
import androidx.annotation.Nullable;
@@ -41,8 +43,8 @@ public class ServiceEntitlement {
public static final String APP_ODSA_PRIMARY = "ap2009";
private final Context context;
- private final int simSubscriptionId;
private final CarrierConfig carrierConfig;
+ private final EapAkaApi eapAkaApi;
/**
* Creates an instance for service entitlement configuration query and operation for the
@@ -58,8 +60,15 @@ public class ServiceEntitlement {
*/
public ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId) {
this.context = context;
- this.simSubscriptionId = simSubscriptionId;
this.carrierConfig = carrierConfig;
+ this.eapAkaApi = new EapAkaApi(context, simSubscriptionId);
+ }
+
+ @VisibleForTesting
+ ServiceEntitlement(Context context, CarrierConfig carrierConfig, EapAkaApi eapAkaApi) {
+ this.context = context;
+ this.carrierConfig = carrierConfig;
+ this.eapAkaApi = eapAkaApi;
}
/**
@@ -105,7 +114,6 @@ public class ServiceEntitlement {
@Nullable
public String queryEntitlementStatus(String appId, ServiceEntitlementRequest request)
throws ServiceEntitlementException {
- EapAkaApi eapAkaApi = new EapAkaApi(context, simSubscriptionId);
return eapAkaApi.queryEntitlementStatus(appId, carrierConfig.serverUrl(), request);
}
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
index 898b782..b9eaadd 100644
--- a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java
@@ -35,6 +35,7 @@ import org.json.JSONObject;
import androidx.annotation.Nullable;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.HttpHeaders;
import java.net.CookieHandler;
@@ -86,6 +87,13 @@ public class EapAkaApi {
this.httpClient = new HttpClient();
}
+ @VisibleForTesting
+ EapAkaApi(Context context, int simSubscriptionId, HttpClient httpClient) {
+ this.context = context;
+ this.simSubscriptionId = simSubscriptionId;
+ this.httpClient = httpClient;
+ }
+
/**
* Retrieves raw entitlement configuration doc though EAP-AKA authentication.
*
@@ -160,11 +168,12 @@ public class EapAkaApi {
return response.body();
}
- private String entitlementStatusUrl(
+ @VisibleForTesting
+ String entitlementStatusUrl(
String appId, String serverUrl, ServiceEntitlementRequest request) {
TelephonyManager telephonyManager =
- context.getSystemService(TelephonyManager.class).createForSubscriptionId(
- simSubscriptionId);
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager = telephonyManager.createForSubscriptionId(simSubscriptionId);
Uri.Builder urlBuilder = Uri.parse(serverUrl).buildUpon();
if (TextUtils.isEmpty(request.authenticationToken())) {
// EAP_ID required for initial AuthN
diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java b/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java
index 5021317..8f4f138 100644
--- a/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java
+++ b/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java
@@ -25,6 +25,8 @@ import android.util.Log;
import com.android.libraries.entitlement.ServiceEntitlementException;
import com.android.libraries.entitlement.eapaka.utils.BytesConverter;
+import com.google.common.annotations.VisibleForTesting;
+
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -73,6 +75,9 @@ class EapAkaResponse {
private boolean valid;
+ @VisibleForTesting
+ static String challengeResponseForTesting;
+
public EapAkaResponse(String eapAkaChallenge) {
try {
parseEapAkaChallengeRequest(eapAkaChallenge);
@@ -201,6 +206,10 @@ class EapAkaResponse {
*/
public String getEapAkaChallengeResponse(Context context, int simSubscriptionId)
throws ServiceEntitlementException {
+ if(challengeResponseForTesting != null) {
+ return challengeResponseForTesting;
+ }
+
if (!valid) {
throw new ServiceEntitlementException("EAP-AKA Challenge message not valid!");
}
diff --git a/java/com/android/libraries/entitlement/eapaka/MasterKey.java b/java/com/android/libraries/entitlement/eapaka/MasterKey.java
index 652fa65..57b8ab3 100644
--- a/java/com/android/libraries/entitlement/eapaka/MasterKey.java
+++ b/java/com/android/libraries/entitlement/eapaka/MasterKey.java
@@ -93,7 +93,7 @@ class MasterKey {
messageDigest.update(data);
masterKey = messageDigest.digest();
} catch (NoSuchAlgorithmException e) {
- Log.d(TAG, "process sHA-1 failed", e);
+ Log.d(TAG, "process SHA-1 failed", e);
}
// Generate TEKs
diff --git a/java/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java b/java/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java
new file mode 100644
index 0000000..0763747
--- /dev/null
+++ b/java/com/android/libraries/entitlement/testing/FakeURLStreamHandler.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.testing;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import javax.net.ssl.HttpsURLConnection;
+
+/**
+ * Fakes {@linkplain java.net.URLStreamHandler} which is used to set URLStreamHandlerFactory for URL
+ * as {@linkplain java.net.URL} is a final class and cannot be mocked using mockito.
+ */
+public class FakeURLStreamHandler extends URLStreamHandler implements URLStreamHandlerFactory {
+
+ private Map<String, FakeResponse> response;
+
+ private static final String ACCESS_TOKEN = "8dGozfI6%2FEaSsE7LaTfJKwdy";
+ private static final String LOCATION = "Location";
+ private static final String CONTENT_TYPE = "Content-Type";
+
+ @Override
+ public URLConnection openConnection(URL u) {
+ FakeHttpsURLConnection connection = new FakeHttpsURLConnection(u);
+ return connection;
+ }
+
+ @Override
+ public URLStreamHandler createURLStreamHandler(String protocol) {
+ return this;
+ }
+
+ public FakeURLStreamHandler stubResponse(Map<String, FakeResponse> response) {
+ this.response = response;
+ return this;
+ }
+
+ /** Fakes {@linkplain java.net.HttpURLConnection} to avoid making any network connection. */
+ public class FakeHttpsURLConnection extends HttpsURLConnection {
+
+ public ByteArrayOutputStream outputStream;
+
+ private final String urlString;
+
+ protected FakeHttpsURLConnection(URL url) {
+ super(url);
+ this.urlString = url.toString();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ InputStream inputStream = new ByteArrayInputStream(response.get(urlString).responseBody());
+ if (inputStream == null) {
+ throw new IOException();
+ }
+ return inputStream;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ outputStream = new ByteArrayOutputStream();
+ return outputStream;
+ }
+
+ @Override
+ public int getResponseCode() {
+ return response.get(urlString).responseCode();
+ }
+
+ @Override
+ public Map<String, List<String>> getHeaderFields() {
+ List<String> locationList = new ArrayList<>();
+ locationList.add("access_token=" + ACCESS_TOKEN);
+ return ImmutableMap.of("Location", locationList);
+ }
+
+ @Override
+ public String getHeaderField(String name) {
+ switch (name) {
+ case LOCATION:
+ return "Location: " + response.get(urlString).responseLocation();
+ case CONTENT_TYPE:
+ return response.get(urlString).contentType();
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void connect() {}
+
+ @Override
+ public void disconnect() {}
+
+ @Override
+ public boolean usingProxy() {
+ return false;
+ }
+
+ @Override
+ public String getCipherSuite() {
+ return null;
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ return null;
+ }
+
+ @Override
+ public Certificate[] getServerCertificates() {
+ return null;
+ }
+ }
+
+ @AutoValue
+ public abstract static class FakeResponse {
+ public abstract int responseCode();
+
+ @Nullable
+ public abstract String responseLocation();
+
+ @SuppressWarnings("mutable") // For test only
+ public abstract byte[] responseBody();
+
+ public abstract String contentType();
+
+ public static Builder builder() {
+ return new AutoValue_FakeURLStreamHandler_FakeResponse.Builder()
+ .setResponseBody(new byte[] {})
+ .setContentType("")
+ .setResponseCode(0)
+ .setResponseLocation("");
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder setResponseCode(int value);
+
+ public abstract Builder setResponseLocation(String value);
+
+ public abstract Builder setResponseBody(byte[] value);
+
+ public abstract Builder setContentType(String contentType);
+
+ public abstract FakeResponse build();
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/Android.bp b/tests/Android.bp
index 82b7b4d..10a044b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -24,6 +24,7 @@ android_test {
"androidx.test.rules",
"mockito-target-minus-junit4",
"platform-test-annotations",
+ "service-entitlement",
"testables",
"testng",
"truth-prebuilt",
diff --git a/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java b/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java
index 2619036..af18e1e 100644
--- a/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java
+++ b/tests/src/com/android/libraries/entitlement/ServiceEntitlementTest.java
@@ -18,16 +18,59 @@ package com.android.libraries.entitlement;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
import androidx.test.runner.AndroidJUnit4;
+import com.android.libraries.entitlement.eapaka.EapAkaApi;
+
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
-// TODO(b/173450048) add tests
@RunWith(AndroidJUnit4.class)
public class ServiceEntitlementTest {
+ private static final String QUERY_APP_VOLTE_RESULT = "QUERY_APP_VOLTE_RESULT";
+ private static final String QUERY_APP_VOWIFI_RESULT = "QUERY_APP_VOWIFI_RESULT";
+ private static final String TEST_URL = "https://test.url";
+
+ @Rule public final MockitoRule rule = MockitoJUnit.rule();
+ @Mock Context mockContext;
+ CarrierConfig carrierConfig;
+ @Mock EapAkaApi mockEapAkaApi;
+
+ private ServiceEntitlement serviceEntitlement;
+
+ @Before
+ public void setUp() {
+ carrierConfig = CarrierConfig.builder().setServerUrl(TEST_URL).build();
+ serviceEntitlement = new ServiceEntitlement(mockContext, carrierConfig, mockEapAkaApi);
+ }
+
+ @Test
+ public void queryEntitlementStatus_appVolte_returnResult() throws Exception {
+ ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build();
+ when(mockEapAkaApi.queryEntitlementStatus(ServiceEntitlement.APP_VOLTE, TEST_URL, request))
+ .thenReturn(QUERY_APP_VOLTE_RESULT);
+
+ assertThat(serviceEntitlement.queryEntitlementStatus(ServiceEntitlement.APP_VOLTE, request))
+ .isEqualTo(QUERY_APP_VOLTE_RESULT);
+ }
+
@Test
- public void test() {
- assertThat(1).isEqualTo(1);
+ public void queryEntitlementStatus_appVowifi_returnResult() throws Exception {
+ ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build();
+ when(mockEapAkaApi.queryEntitlementStatus(ServiceEntitlement.APP_VOWIFI, TEST_URL, request))
+ .thenReturn(QUERY_APP_VOWIFI_RESULT);
+
+ assertThat(
+ serviceEntitlement.queryEntitlementStatus(ServiceEntitlement.APP_VOWIFI, request))
+ .isEqualTo(QUERY_APP_VOWIFI_RESULT);
}
}
diff --git a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java
new file mode 100644
index 0000000..ea18be8
--- /dev/null
+++ b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaApiTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.when;
+
+import com.android.libraries.entitlement.ServiceEntitlement;
+import com.android.libraries.entitlement.ServiceEntitlementRequest;
+import com.android.libraries.entitlement.http.HttpClient;
+import com.android.libraries.entitlement.http.HttpResponse;
+import com.android.libraries.entitlement.http.HttpConstants.ContentType;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION;
+
+import android.telephony.TelephonyManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class EapAkaApiTest {
+ private static final String TEST_URL = "https://test.url/test-path";
+ private static final String EAP_AKA_CHALLENGE = "{\"eap-relay-packet\":\"EAP_AKA_CHALLENGE\"}";
+ private static final String EAP_AKA_CHALLENGE_RESPONSE = "EAP_AKA_CHALLENGE_RESPONSE";
+ private static final String RESPONSE_XML =
+ "<wap-provisioningdoc version=\"1.1\">\n"
+ + " <characteristic type=\"TOKEN\">\n"
+ + " <parm name=\"token\" value=\"kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX\"/>\n"
+ + " <parm name=\"validity\" value=\"3600\"/>\n"
+ + " </characteristic>\n"
+ + " <characteristic type=\"APPLICATION\">\n"
+ + " <parm name=\"EntitlementStatus\" value=\"0\"/>\n"
+ + " <parm name=\"AddrStatus\" value=\"0\"/>\n"
+ + " <parm name=\"TC_Status\" value=\"2\"/>\n"
+ + " <parm name=\"ProvStatus\" value=\"2\"/>\n"
+ + " <parm name=\"ServiceFlow_URL\""
+ + " value=\"http://vm-host:8180/self-prov-websheet/rcs\"/>\n"
+ + " <parm name=\"ServiceFlow_UserData\""
+ + " value=\"token=test_token\"/>\n"
+ + " </characteristic>\n"
+ + "</wap-provisioningdoc>\n";
+ private static final String TOKEN = "kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX";
+ private static final String IMSI = "TEST_IMSI";
+ private static final String IMEI = "TEST_IMEI";
+ private static final String MCCMNC = "10010";
+ private static final String ENTITLEMENT_URL_WITH_TOKEN =
+ TEST_URL
+ + "?IMSI="
+ + IMSI
+ + "&token="
+ + TOKEN
+ + "&terminal_id="
+ + IMEI
+ + "&terminal_vendor="
+ + Build.MANUFACTURER
+ + "&terminal_model="
+ + Build.MODEL
+ + "&terminal_sw_version="
+ + VERSION.BASE_OS
+ + "&app="
+ + ServiceEntitlement.APP_VOWIFI
+ + "&vers=0"
+ + "&entitlement_version=2.0";
+ private static final int SUB_ID = 0;
+
+ @Rule public final MockitoRule rule = MockitoJUnit.rule();
+ @Mock Context mockContext;
+ @Mock HttpClient mockHttpClient;
+ @Mock TelephonyManager mockTelephonyManager;
+ @Mock TelephonyManager mockTelephonyManagerForSubId;
+
+ private EapAkaApi eapAkaApi;
+
+ @Before
+ public void setUp() {
+ eapAkaApi = new EapAkaApi(mockContext, SUB_ID, mockHttpClient);
+ when(mockContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .thenReturn(mockTelephonyManager);
+ when(mockTelephonyManager.createForSubscriptionId(SUB_ID))
+ .thenReturn(mockTelephonyManagerForSubId);
+ }
+
+ @Test
+ public void queryEntitlementStatus_hasAuthenticationToken_fastAuthN() throws Exception {
+ HttpResponse response =
+ HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML).build();
+ when(mockHttpClient.request(any())).thenReturn(response);
+
+ ServiceEntitlementRequest request =
+ ServiceEntitlementRequest.builder().setAuthenticationToken(TOKEN).build();
+
+ assertThat(eapAkaApi.queryEntitlementStatus(
+ ServiceEntitlement.APP_VOWIFI, TEST_URL, request))
+ .isEqualTo(RESPONSE_XML);
+ }
+
+ @Test
+ public void queryEntitlementStatus_noAuthenticationToken_initialAuthN() throws Exception {
+ HttpResponse eapChallengeResponse =
+ HttpResponse
+ .builder().setContentType(ContentType.JSON).setBody(EAP_AKA_CHALLENGE).build();
+ HttpResponse xmlResponse =
+ HttpResponse.builder().setContentType(ContentType.XML).setBody(RESPONSE_XML).build();
+ when(mockHttpClient.request(any()))
+ .thenReturn(eapChallengeResponse).thenReturn(xmlResponse);
+ EapAkaResponse.challengeResponseForTesting = EAP_AKA_CHALLENGE_RESPONSE;
+
+ ServiceEntitlementRequest request = ServiceEntitlementRequest.builder().build();
+
+ assertThat(
+ eapAkaApi.queryEntitlementStatus(ServiceEntitlement.APP_VOWIFI, TEST_URL, request))
+ .isEqualTo(RESPONSE_XML);
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java
new file mode 100644
index 0000000..2ca7a6a
--- /dev/null
+++ b/tests/src/com/android/libraries/entitlement/eapaka/EapAkaSecurityContextTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.util.Base64;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+
+import com.google.common.io.BaseEncoding;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EapAkaSecurityContextTest {
+ // Base64 data : 2wjHnwKln8mjjxDzMKJvLBzMHtm0X9SNBsUWEAbEiAdD7xeqqZ7nsXzukRkIhd6SDZ4bj7s=
+ // RAW DATA : DB08C79F02A59FC9A38F10F330A26F2C1CCC1ED9B45FD48D06C5161006C4880743EF17AAA99EE7B17
+ // CEE91190885DE920D9E1B8FBB
+ // TAG : DB
+ // RES length : 08
+ // RES : C79F02A59FC9A38F
+ // CK Length : 10
+ // CK : F330A26F2C1CCC1ED9B45FD48D06C516
+ // IK Length : 10
+ // IK : 06C4880743EF17AAA99EE7B17CEE9119
+ // KC Length : 08
+ // KC : 85DE920D9E1B8FBB
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE =
+ "2wjHnwKln8mjjxDzMKJvLBzMHtm0X9SNBsUWEAbEiAdD7xeqqZ7nsXzukRkIhd6SDZ4bj7s=";
+ private static final String EXPECTED_IK = "06C4880743EF17AAA99EE7B17CEE9119";
+ private static final String EXPECTED_CK = "F330A26F2C1CCC1ED9B45FD48D06C516";
+ private static final String EXPECTED_RES = "C79F02A59FC9A38F";
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE_TAG_DC =
+ "DC08C79F02A59FC9A38F10F330A"
+ + "26F2C1CCC1ED9B45FD48D06C5161006C4880743EF17AAA99EE7B17CEE91190885DE920D9E1B8FBB";
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE_INVALID_RES_LENGTH =
+ "DC40C79F02A59FC"
+ + "9A38F10F330A26F2C1CCC1ED9B45FD48D06C5161006C4880743EF17AAA99EE7B17CEE91190885DE920D9"
+ + "E1B8FBB";
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE_INVALID_CK_LENGTH =
+ "DB08C79F02A59FC9"
+ + "A38F40F330A26F2C1CCC1ED9B45FD48D06C5161006C4880743EF17AAA99EE7B17CEE91190885DE920D9E"
+ + "1B8FBB";
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE_INVALID_IK_LENGTH =
+ "DB08C79F02A59FC9"
+ + "A38F10F330A26F2C1CCC1ED9B45FD48D06C5164006C4880743EF17AAA99EE7B17CEE91190885DE920D9E"
+ + "1B8FBB";
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE_INVALID_KC_LENGTH =
+ "DB08C79F02A59FC9"
+ + "A38F10F330A26F2C1CCC1ED9B45FD48D06C5161006C4880743EF17AAA99EE7B17CEE91191085DE920D9E"
+ + "1B8FBB";
+ private static final String GSM_SECURITY_CONTEXT_RESPONSE_NO_KC_KEY =
+ "DB08C79F02A59FC9A38F10F3"
+ + "30A26F2C1CCC1ED9B45FD48D06C5161006C4880743EF17AAA99EE7B17CEE9119";
+
+ @Test
+ public void parseResponseData_validResponse_pass() throws Exception {
+ EapAkaSecurityContext securityContext =
+ EapAkaSecurityContext.from(GSM_SECURITY_CONTEXT_RESPONSE);
+
+ assertThat(securityContext.isValid()).isTrue();
+ assertThat(securityContext.getIk()).isEqualTo(convertHexStringToBytes(EXPECTED_IK));
+ assertThat(securityContext.getCk()).isEqualTo(convertHexStringToBytes(EXPECTED_CK));
+ assertThat(securityContext.getRes()).isEqualTo(convertHexStringToBytes(EXPECTED_RES));
+ }
+
+ @Test
+ public void parseResponseData_invalidWithWrongTag_throwsException() {
+ byte[] data = convertHexStringToBytes(GSM_SECURITY_CONTEXT_RESPONSE_TAG_DC);
+ String encodedData = Base64.encodeToString(data, Base64.NO_WRAP).trim();
+
+ assertThrows(
+ ServiceEntitlementException.class, () -> EapAkaSecurityContext.from(encodedData));
+ }
+
+ @Test
+ public void parseResponseData_invalidWithWrongResLength_throwsException() {
+ byte[] data = convertHexStringToBytes(GSM_SECURITY_CONTEXT_RESPONSE_INVALID_RES_LENGTH);
+ String encodedData = Base64.encodeToString(data, Base64.NO_WRAP).trim();
+
+ assertThrows(
+ ServiceEntitlementException.class, () -> EapAkaSecurityContext.from(encodedData));
+ }
+
+ @Test
+ public void parseResponseData_invalidWithWrongCkLength_throwsException() {
+ byte[] data = convertHexStringToBytes(GSM_SECURITY_CONTEXT_RESPONSE_INVALID_CK_LENGTH);
+ String encodedData = Base64.encodeToString(data, Base64.NO_WRAP).trim();
+
+ assertThrows(
+ ServiceEntitlementException.class, () -> EapAkaSecurityContext.from(encodedData));
+ }
+
+ @Test
+ public void parseResponseData_invalidWithWrongIkLength_throwsException() {
+ byte[] data = convertHexStringToBytes(GSM_SECURITY_CONTEXT_RESPONSE_INVALID_IK_LENGTH);
+ String encodedData = Base64.encodeToString(data, Base64.NO_WRAP).trim();
+
+ assertThrows(
+ ServiceEntitlementException.class, () -> EapAkaSecurityContext.from(encodedData));
+ }
+
+ @Test
+ public void parseResponseData_validWithWrongKcLength() throws Exception {
+ // Because we don't parse KC, invalid KC length doesn't hurt
+ byte[] data = convertHexStringToBytes(GSM_SECURITY_CONTEXT_RESPONSE_INVALID_KC_LENGTH);
+ String encodedData = Base64.encodeToString(data, Base64.NO_WRAP).trim();
+
+ EapAkaSecurityContext securityContext = EapAkaSecurityContext.from(encodedData);
+
+ assertThat(securityContext.isValid()).isTrue();
+ assertThat(securityContext.getIk()).isEqualTo(convertHexStringToBytes(EXPECTED_IK));
+ assertThat(securityContext.getCk()).isEqualTo(convertHexStringToBytes(EXPECTED_CK));
+ assertThat(securityContext.getRes()).isEqualTo(convertHexStringToBytes(EXPECTED_RES));
+ }
+
+ @Test
+ public void parseResponseData_noKcKey() throws Exception {
+ byte[] data = convertHexStringToBytes(GSM_SECURITY_CONTEXT_RESPONSE_NO_KC_KEY);
+ String encodedData = Base64.encodeToString(data, Base64.NO_WRAP).trim();
+
+ EapAkaSecurityContext securityContext = EapAkaSecurityContext.from(encodedData);
+
+ assertThat(securityContext.isValid()).isTrue();
+ assertThat(securityContext.getIk()).isEqualTo(convertHexStringToBytes(EXPECTED_IK));
+ assertThat(securityContext.getCk()).isEqualTo(convertHexStringToBytes(EXPECTED_CK));
+ assertThat(securityContext.getRes()).isEqualTo(convertHexStringToBytes(EXPECTED_RES));
+ }
+
+ private byte[] convertHexStringToBytes(String input) {
+ return BaseEncoding.base16().decode(input);
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/libraries/entitlement/eapaka/MasterKeyTest.java b/tests/src/com/android/libraries/entitlement/eapaka/MasterKeyTest.java
new file mode 100644
index 0000000..7caa8fa
--- /dev/null
+++ b/tests/src/com/android/libraries/entitlement/eapaka/MasterKeyTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.eapaka;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.io.BaseEncoding;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MasterKeyTest {
+ private static final String EXPECTED_IK = "06C4880743EF17AAA99EE7B17CEE9119";
+ private static final String EXPECTED_CK = "F330A26F2C1CCC1ED9B45FD48D06C516";
+ private static final String IMSI_EAP = "0234107813240779@nai.epc.mnc010.mcc234.3gppnetwork.org";
+ private static final String EXPECTED_AUT = "4A2137E6E292679DD4C3FD8AB67F13DA";
+
+ @Test
+ public void generateTransientEapKeys_getAutPassed() throws Exception {
+ MasterKey masterKey =
+ MasterKey.create(
+ IMSI_EAP,
+ convertHexStringToBytes(EXPECTED_IK),
+ convertHexStringToBytes(EXPECTED_CK));
+
+ assertThat(masterKey.getAut()).isEqualTo(convertHexStringToBytes(EXPECTED_AUT));
+ }
+
+ @Test
+ public void generateTransientEapKeys_withoutImsiEap_getNull() throws Exception {
+ MasterKey masterKey =
+ MasterKey.create(
+ null,
+ convertHexStringToBytes(EXPECTED_IK),
+ convertHexStringToBytes(EXPECTED_CK));
+
+ assertThat(masterKey).isNull();
+ }
+
+ @Test
+ public void generateTransientEapKeys_withoutIk_getNull() throws Exception {
+ MasterKey masterKey =
+ MasterKey.create(
+ IMSI_EAP,
+ null,
+ convertHexStringToBytes(EXPECTED_CK));
+
+ assertThat(masterKey).isNull();
+ }
+
+ @Test
+ public void generateTransientEapKeys_withoutCk_getNull() throws Exception {
+ MasterKey masterKey =
+ MasterKey.create(
+ IMSI_EAP,
+ convertHexStringToBytes(EXPECTED_IK),
+ null);
+
+ assertThat(masterKey).isNull();
+ }
+
+ private byte[] convertHexStringToBytes(String input) {
+ return BaseEncoding.base16().decode(input);
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java b/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java
new file mode 100644
index 0000000..77461f3
--- /dev/null
+++ b/tests/src/com/android/libraries/entitlement/http/HttpClientTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 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.libraries.entitlement.http;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.android.libraries.entitlement.http.HttpClient;
+import com.android.libraries.entitlement.http.HttpConstants.ContentType;
+import com.android.libraries.entitlement.http.HttpConstants.RequestMethod;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION;
+
+import android.telephony.TelephonyManager;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.testing.FakeURLStreamHandler;
+import com.android.libraries.entitlement.testing.FakeURLStreamHandler.FakeResponse;
+
+import com.google.common.collect.ImmutableMap;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class HttpClientTest {
+ private static final String TEST_URL = "https://test.url";
+ private static final String TEST_RESPONSE_BODY = "TEST_RESPONSE_BODY";
+ private static final String CONTENT_TYPE_STRING_JSON = "application/json";
+
+ private static FakeURLStreamHandler fakeURLStreamHandler;
+
+ private final HttpClient httpClient = new HttpClient();
+
+ @BeforeClass
+ public static void setupURLStreamHandlerFactory() {
+ fakeURLStreamHandler = new FakeURLStreamHandler();
+ URL.setURLStreamHandlerFactory(fakeURLStreamHandler);
+ }
+
+ @Test
+ public void request_contentTypeXml_returnsXmlBody() throws Exception {
+ FakeResponse responseContent =
+ FakeResponse.builder()
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setResponseLocation(null)
+ .setResponseBody(TEST_RESPONSE_BODY.getBytes(UTF_8))
+ .setContentType(CONTENT_TYPE_STRING_JSON)
+ .build();
+ HttpRequest request =
+ HttpRequest.builder().setUrl(TEST_URL).setRequestMethod(RequestMethod.GET).build();
+ Map<String, FakeResponse> response = ImmutableMap.of(TEST_URL, responseContent);
+ fakeURLStreamHandler.stubResponse(response);
+
+ HttpResponse httpResponse = httpClient.request(request);
+
+ assertThat(httpResponse.contentType()).isEqualTo(ContentType.JSON);
+ assertThat(httpResponse.body()).isEqualTo(TEST_RESPONSE_BODY);
+ assertThat(httpResponse.responseCode()).isEqualTo(HttpURLConnection.HTTP_OK);
+ }
+
+ @Test
+ public void request_httpGetResponseBadRequest_throwsException() {
+ FakeResponse responseContent =
+ FakeResponse.builder()
+ .setResponseCode(HttpURLConnection.HTTP_BAD_REQUEST)
+ .setResponseLocation(null)
+ .setResponseBody(TEST_RESPONSE_BODY.getBytes(UTF_8))
+ .setContentType(CONTENT_TYPE_STRING_JSON)
+ .build();
+ HttpRequest request =
+ HttpRequest.builder().setUrl(TEST_URL).setRequestMethod(RequestMethod.GET).build();
+ Map<String, FakeResponse> response = ImmutableMap.of(TEST_URL, responseContent);
+ fakeURLStreamHandler.stubResponse(response);
+
+ assertThrows(ServiceEntitlementException.class, () -> httpClient.request(request));
+ }
+} \ No newline at end of file