summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSarah Chin <sarahchin@google.com>2023-06-05 21:24:50 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-06-05 21:24:50 +0000
commitbd7328abe348bb2f0a0d0a6657e6b1e60088fbd6 (patch)
tree687bf51143dcc38e5229a1f96b73d274d80ac763
parent68c8f109db8092e936ad68f1b6f12f5fa8ab4f2d (diff)
parent6256e66510fb7066d0741e51798c8f64d7bdb11b (diff)
downloadgsma_services-bd7328abe348bb2f0a0d0a6657e6b1e60088fbd6.tar.gz
TS.43 authentication library implementation am: 6256e66510
Original change: https://android-review.googlesource.com/c/platform/frameworks/libs/gsma_services/+/2616251 Change-Id: Idde6d4622710f8963879990318d7431e7ebecd7d Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationException.java83
-rw-r--r--ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java253
2 files changed, 315 insertions, 21 deletions
diff --git a/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationException.java b/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationException.java
index 805b793..bed5877 100644
--- a/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationException.java
+++ b/ts43authentication/src/com/android/libraries/ts43authentication/AuthenticationException.java
@@ -18,9 +18,12 @@ package com.android.libraries.ts43authentication;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.OutcomeReceiver;
import android.os.PersistableBundle;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URL;
@@ -47,7 +50,7 @@ public class AuthenticationException extends Exception {
* Clients should call {@link Ts43AuthenticationLibrary#requestOidcAuthenticationServer(
* PersistableBundle, String, String, int, URL, String, String, Executor, OutcomeReceiver)}
* and {@link Ts43AuthenticationLibrary#requestOidcAuthentication(
- * PersistableBundle, String, URL, Executor, OutcomeReceiver)} instead.
+ * PersistableBundle, String, URL, String, URL, Executor, OutcomeReceiver)} instead.
*/
public static final int ERROR_MUST_USE_OIDC = 2;
@@ -55,7 +58,7 @@ public class AuthenticationException extends Exception {
* Authentication request failed because one or more of the services required to complete the
* request (e.g. Telephony, SIM, TS.43 entitlement server) are not currently available.
*/
- public static final int ERROR_SERVICE_UNAVAILABLE = 3;
+ public static final int ERROR_SERVICE_NOT_AVAILABLE = 3;
/**
* Authentication request failed because the SIM is not returning a response to the EAP-AKA
@@ -92,7 +95,7 @@ public class AuthenticationException extends Exception {
ERROR_UNSPECIFIED,
ERROR_INVALID_APP_NAME,
ERROR_MUST_USE_OIDC,
- ERROR_SERVICE_UNAVAILABLE,
+ ERROR_SERVICE_NOT_AVAILABLE,
ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE,
ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE,
ERROR_MAXIMUM_EAP_AKA_ATTEMPTS,
@@ -112,12 +115,43 @@ public class AuthenticationException extends Exception {
*/
public static final String RETRY_AFTER_UNSPECIFIED = "";
+ @AuthenticationError private final int mError;
+ private final int mHttpStatusCode;
+ @NonNull private final String mRetryAfter;
+
+ private AuthenticationException(@AuthenticationError int error, int httpStatusCode,
+ @NonNull String retryAfter, @NonNull String message) {
+ super(message);
+ mError = error;
+ mHttpStatusCode = httpStatusCode;
+ mRetryAfter = retryAfter;
+ }
+
+ /**
+ * Create an AuthenticationException for the given {@link AuthenticationError}.
+ * @param error The authentication error.
+ * @param message The detail message with more information about the exception.
+ */
+ public AuthenticationException(@AuthenticationError int error, @NonNull String message) {
+ this(error, HTTP_STATUS_CODE_UNSPECIFIED, RETRY_AFTER_UNSPECIFIED, message);
+ }
+
+ /**
+ * Create an AuthenticationException from the given {@link ServiceEntitlementException}.
+ * @param exception The service entitlement exception from the TS.43 library.
+ */
+ public AuthenticationException(@NonNull ServiceEntitlementException exception) {
+ this(convertToAuthenticationError(exception.getErrorCode()),
+ convertToHttpStatusCode(exception.getHttpStatus()),
+ convertToRetryAfter(exception.getRetryAfter()), exception.getMessage());
+ }
+
/**
* The error code for why authentication failed, or {@link #ERROR_UNSPECIFIED} if it is
* unspecified.
*/
@AuthenticationError public int getError() {
- return ERROR_UNSPECIFIED;
+ return mError;
}
/**
@@ -125,7 +159,7 @@ public class AuthenticationException extends Exception {
* {@link #HTTP_STATUS_CODE_UNSPECIFIED} if it is unspecified.
*/
public int getHttpStatusCode() {
- return HTTP_STATUS_CODE_UNSPECIFIED;
+ return mHttpStatusCode;
}
/**
@@ -135,6 +169,43 @@ public class AuthenticationException extends Exception {
* <a href="https://tools.ietf.org/html/rfc7231#section-7.1.3">RFC 7231</a>
*/
@NonNull public String getRetryAfter() {
- return RETRY_AFTER_UNSPECIFIED;
+ return mRetryAfter;
+ }
+
+ @AuthenticationError private static int convertToAuthenticationError(int errorCode) {
+ switch (errorCode) {
+ case ServiceEntitlementException.ERROR_PHONE_NOT_AVAILABLE:
+ case ServiceEntitlementException.ERROR_SERVER_NOT_CONNECTABLE:
+ return ERROR_SERVICE_NOT_AVAILABLE;
+ case ServiceEntitlementException.ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE:
+ return ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE;
+ case ServiceEntitlementException.ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE:
+ return ERROR_EAP_AKA_SYNCHRONIZATION_FAILURE;
+ case ServiceEntitlementException.ERROR_EAP_AKA_FAILURE:
+ return ERROR_MAXIMUM_EAP_AKA_ATTEMPTS;
+ case ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS:
+ return ERROR_HTTP_RESPONSE_FAILED;
+ case ServiceEntitlementException.ERROR_MALFORMED_HTTP_RESPONSE:
+ case ServiceEntitlementException.ERROR_TOKEN_NOT_AVAILABLE:
+ return ERROR_INVALID_HTTP_RESPONSE;
+ case ServiceEntitlementException.ERROR_UNKNOWN:
+ default:
+ return ERROR_UNSPECIFIED;
+ }
+ }
+
+ private static int convertToHttpStatusCode(int httpStatusCode) {
+ if (httpStatusCode == ServiceEntitlementException.HTTP_STATUS_UNSPECIFIED) {
+ return HTTP_STATUS_CODE_UNSPECIFIED;
+ }
+ return httpStatusCode;
+ }
+
+ private static String convertToRetryAfter(@Nullable String retryAfter) {
+ if (retryAfter == null || retryAfter.isEmpty()
+ || retryAfter.equals(ServiceEntitlementException.RETRY_AFTER_UNSPECIFIED)) {
+ return RETRY_AFTER_UNSPECIFIED;
+ }
+ return retryAfter;
}
}
diff --git a/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java b/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java
index c05241e..d2c1593 100644
--- a/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java
+++ b/ts43authentication/src/com/android/libraries/ts43authentication/Ts43AuthenticationLibrary.java
@@ -20,23 +20,30 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.OutcomeReceiver;
import android.os.PersistableBundle;
import android.telephony.SubscriptionInfo;
-import com.android.libraries.entitlement.Ts43Authentication.Ts43AuthToken;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.Ts43Authentication;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URL;
import java.util.concurrent.Executor;
+import java.util.concurrent.locks.ReentrantLock;
/**
* TS.43 authentication library that directs EAP-AKA and OIDC authentication requests to the
- * entitlement server and returns an {@link Ts43AuthToken} on success or an
+ * entitlement server and returns a {@link Ts43Authentication.Ts43AuthToken} on success or an
* {@link AuthenticationException} on failure.
*/
-public class Ts43AuthenticationLibrary {
+public class Ts43AuthenticationLibrary extends Handler {
/**
* Configuration key for the list of {@code SHA256} signing certificates that are permitted
* to make authentication requests. This will be used to verify the {@code appName} that is
@@ -67,6 +74,98 @@ public class Ts43AuthenticationLibrary {
})
public @interface ConfigurationKey {}
+ private static final int EVENT_REQUEST_EAP_AKA_AUTHENTICATION = 0;
+ private static final int EVENT_REQUEST_OIDC_AUTHENTICATION_SERVER = 1;
+ private static final int EVENT_REQUEST_OIDC_AUTHENTICATION = 2;
+
+ @NonNull private final ReentrantLock mLock = new ReentrantLock();
+ @NonNull private final Context mContext;
+
+ /**
+ * Create an instance of the TS.43 Authentication Library.
+ * @param context The application context.
+ * @param looper The looper to run authentication requests on.
+ */
+ public Ts43AuthenticationLibrary(@NonNull Context context, @NonNull Looper looper) {
+ super(looper);
+ mContext = context;
+ }
+
+ private static class EapAkaAuthenticationRequest {
+ @NonNull private final String mAppName;
+ @Nullable private final String mAppVersion;
+ private final int mSlotIndex;
+ @NonNull private final URL mEntitlementServerAddress;
+ @Nullable private final String mEntitlementVersion;
+ @NonNull private final String mAppId;
+ @NonNull private final Executor mExecutor;
+ @NonNull private final OutcomeReceiver<
+ Ts43Authentication.Ts43AuthToken, AuthenticationException> mCallback;
+
+ private EapAkaAuthenticationRequest(@NonNull String appName, @Nullable String appVersion,
+ int slotIndex, @NonNull URL entitlementServerAddress,
+ @Nullable String entitlementVersion, @NonNull String appId,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<
+ Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
+ mAppName = appName;
+ mAppVersion = appVersion;
+ mSlotIndex = slotIndex;
+ mEntitlementServerAddress = entitlementServerAddress;
+ mEntitlementVersion = entitlementVersion;
+ mAppId = appId;
+ mExecutor = executor;
+ mCallback = callback;
+ }
+ }
+
+ private static class OidcAuthenticationServerRequest {
+ @NonNull private final String mAppName;
+ @Nullable private final String mAppVersion;
+ private final int mSlotIndex;
+ @NonNull private final URL mEntitlementServerAddress;
+ @Nullable private final String mEntitlementVersion;
+ @NonNull private final String mAppId;
+ @NonNull private final Executor mExecutor;
+ @NonNull private final OutcomeReceiver<URL, AuthenticationException> mCallback;
+
+ private OidcAuthenticationServerRequest(@NonNull String appName,
+ @Nullable String appVersion, int slotIndex, @NonNull URL entitlementServerAddress,
+ @Nullable String entitlementVersion, @NonNull String appId,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<URL, AuthenticationException> callback) {
+ mAppName = appName;
+ mAppVersion = appVersion;
+ mSlotIndex = slotIndex;
+ mEntitlementServerAddress = entitlementServerAddress;
+ mEntitlementVersion = entitlementVersion;
+ mAppId = appId;
+ mExecutor = executor;
+ mCallback = callback;
+ }
+ }
+
+ private static class OidcAuthenticationRequest {
+ @NonNull private final URL mEntitlementServerAddress;
+ @Nullable private final String mEntitlementVersion;
+ @NonNull private final URL mAesUrl;
+ @NonNull private final Executor mExecutor;
+ @NonNull private final OutcomeReceiver<
+ Ts43Authentication.Ts43AuthToken, AuthenticationException> mCallback;
+
+ private OidcAuthenticationRequest(@NonNull URL entitlementServerAddress,
+ @Nullable String entitlementVersion, @NonNull URL aesUrl,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<
+ Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
+ mEntitlementServerAddress = entitlementServerAddress;
+ mEntitlementVersion = entitlementVersion;
+ mAesUrl = aesUrl;
+ mExecutor = executor;
+ mCallback = callback;
+ }
+ }
+
/**
* Request authentication from the TS.43 server with EAP-AKA as described in
* TS.43 Service Entitlement Configuration section 2.8.1.
@@ -90,16 +189,26 @@ public class Ts43AuthenticationLibrary {
* @param executor The executor on which the callback will be called.
* @param callback The callback to receive the results of the authentication request.
* If authentication is successful, {@link OutcomeReceiver#onResult(Object)} will return
- * an {@link Ts43AuthToken} with the token and validity.
+ * a {@link Ts43Authentication.Ts43AuthToken} with the token and validity.
* If the authentication fails, {@link OutcomeReceiver#onError(Throwable)} will return an
* {@link AuthenticationException} with the failure details.
*/
- public void requestEapAkaAuthentication(@Nullable PersistableBundle configs,
+ public void requestEapAkaAuthentication(@NonNull PersistableBundle configs,
@NonNull String packageName, @Nullable String appVersion, int slotIndex,
@NonNull URL entitlementServerAddress, @Nullable String entitlementVersion,
@NonNull String appId, @NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<Ts43AuthToken, AuthenticationException> callback) {
- // TODO: implement
+ @NonNull OutcomeReceiver<
+ Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
+ if (isCallingPackageValid(configs, packageName)) {
+ obtainMessage(EVENT_REQUEST_EAP_AKA_AUTHENTICATION, new EapAkaAuthenticationRequest(
+ getAppName(configs, packageName), appVersion, slotIndex,
+ entitlementServerAddress, entitlementVersion, appId, executor, callback))
+ .sendToTarget();
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new AuthenticationException(AuthenticationException.ERROR_INVALID_APP_NAME,
+ "Failed to verify the identity of the calling application"))));
+ }
}
/**
@@ -107,8 +216,9 @@ public class Ts43AuthenticationLibrary {
* TS.43 Service Entitlement Configuration section 2.8.2.
* The client should present the content of the URL to the user to continue the authentication
* process. After receiving a response from the authentication server, the client can call
- * {@link #requestOidcAuthentication(PersistableBundle, String, URL, Executor, OutcomeReceiver)}
- * to get the authentication token.
+ * {@link #requestOidcAuthentication(
+ * PersistableBundle, String, URL, String, URL, Executor, OutcomeReceiver)} to get the
+ * authentication token.
*
* @param configs The configurations that should be applied to this authentication request.
* The keys of the bundle must be one of the {@link ConfigurationKey}s.
@@ -135,12 +245,21 @@ public class Ts43AuthenticationLibrary {
* If the authentication fails, {@link OutcomeReceiver#onError(Throwable)} will return an
* {@link AuthenticationException} with the failure details.
*/
- public void requestOidcAuthenticationServer(@Nullable PersistableBundle configs,
+ public void requestOidcAuthenticationServer(@NonNull PersistableBundle configs,
@NonNull String packageName, @Nullable String appVersion, int slotIndex,
@NonNull URL entitlementServerAddress, @Nullable String entitlementVersion,
@NonNull String appId, @NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<URL, AuthenticationException> callback) {
- // TODO: implement
+ if (isCallingPackageValid(configs, packageName)) {
+ obtainMessage(EVENT_REQUEST_OIDC_AUTHENTICATION_SERVER,
+ new OidcAuthenticationServerRequest(getAppName(configs, packageName),
+ appVersion, slotIndex, entitlementServerAddress, entitlementVersion,
+ appId, executor, callback)).sendToTarget();
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new AuthenticationException(AuthenticationException.ERROR_INVALID_APP_NAME,
+ "Failed to verify the identity of the calling application"))));
+ }
}
/**
@@ -151,19 +270,123 @@ public class Ts43AuthenticationLibrary {
* The keys of the bundle must be one of the {@link ConfigurationKey}s.
* @param packageName The package name for the calling application, used to validate the
* identity of the calling application.
+ * @param entitlementServerAddress The entitlement server address.
+ * @param entitlementVersion The TS.43 entitlement version to use. For example, {@code "9.0"}.
+ * If this is {@code null}, version {@code "2.0"} will be used by default.
* @param aesUrl The AES URL used to retrieve the authentication token. The parameters in the
* URL include the OIDC authentication code {@code code} and {@code state}.
* @param executor The executor on which the callback will be called.
* @param callback The callback to receive the results of the authentication request.
* If authentication is successful, {@link OutcomeReceiver#onResult(Object)} will return
- * an {@link Ts43AuthToken} with the token and validity.
+ * a {@link Ts43Authentication.Ts43AuthToken} with the token and validity.
* If the authentication fails, {@link OutcomeReceiver#onError(Throwable)} will return an
* {@link AuthenticationException} with the failure details.
*/
- public void requestOidcAuthentication(@Nullable PersistableBundle configs,
- @NonNull String packageName, @NonNull URL aesUrl,
+ public void requestOidcAuthentication(@NonNull PersistableBundle configs,
+ @NonNull String packageName, @NonNull URL entitlementServerAddress,
+ @Nullable String entitlementVersion, @NonNull URL aesUrl,
@NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<Ts43AuthToken, AuthenticationException> callback) {
+ @NonNull OutcomeReceiver<
+ Ts43Authentication.Ts43AuthToken, AuthenticationException> callback) {
+ if (isCallingPackageValid(configs, packageName)) {
+ obtainMessage(EVENT_REQUEST_OIDC_AUTHENTICATION, new OidcAuthenticationRequest(
+ entitlementServerAddress, entitlementVersion, aesUrl, executor, callback))
+ .sendToTarget();
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new AuthenticationException(AuthenticationException.ERROR_INVALID_APP_NAME,
+ "Failed to verify the identity of the calling application"))));
+ }
+ }
+
+ private static boolean isCallingPackageValid(@NonNull PersistableBundle configs,
+ @NonNull String packageName) {
+ // TODO: implement
+ return true;
+ }
+
+ @NonNull private static String getAppName(@NonNull PersistableBundle configs,
+ @NonNull String packageName) {
+ if (configs.getBoolean(KEY_APPEND_SHA_TO_APP_NAME_BOOL)) {
+ return getPackageSha(packageName) + "|" + packageName;
+ } else {
+ return packageName;
+ }
+ }
+
+ private static String getPackageSha(@NonNull String packageName) {
// TODO: implement
+ return "";
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_REQUEST_EAP_AKA_AUTHENTICATION:
+ onRequestEapAkaAuthentication((EapAkaAuthenticationRequest) msg.obj);
+ break;
+ case EVENT_REQUEST_OIDC_AUTHENTICATION_SERVER:
+ onRequestOidcAuthenticationServer((OidcAuthenticationServerRequest) msg.obj);
+ break;
+ case EVENT_REQUEST_OIDC_AUTHENTICATION:
+ onRequestOidcAuthentication((OidcAuthenticationRequest) msg.obj);
+ break;
+ default:
+ // unknown request
+ }
+ }
+
+ private void onRequestEapAkaAuthentication(@NonNull EapAkaAuthenticationRequest request) {
+ mLock.lock();
+ try {
+ Ts43Authentication authLibrary = new Ts43Authentication(mContext,
+ request.mEntitlementServerAddress, request.mEntitlementVersion);
+ Ts43Authentication.Ts43AuthToken authToken = authLibrary.getAuthToken(
+ request.mSlotIndex, request.mAppId, request.mAppName, request.mAppVersion);
+ request.mExecutor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> request.mCallback.onResult(authToken)));
+ } catch (ServiceEntitlementException exception) {
+ request.mExecutor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> request.mCallback.onError(new AuthenticationException(exception))));
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ private void onRequestOidcAuthenticationServer(
+ @NonNull OidcAuthenticationServerRequest request) {
+ mLock.lock();
+ try {
+ Ts43Authentication authLibrary = new Ts43Authentication(mContext,
+ request.mEntitlementServerAddress, request.mEntitlementVersion);
+ URL url = authLibrary.getOidcAuthServer(
+ mContext, request.mSlotIndex, request.mEntitlementServerAddress,
+ request.mEntitlementVersion, request.mAppId, request.mAppName,
+ request.mAppVersion);
+ request.mExecutor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> request.mCallback.onResult(url)));
+ } catch (ServiceEntitlementException exception) {
+ request.mExecutor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> request.mCallback.onError(new AuthenticationException(exception))));
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ private void onRequestOidcAuthentication(@NonNull OidcAuthenticationRequest request) {
+ mLock.lock();
+ try {
+ Ts43Authentication authLibrary = new Ts43Authentication(mContext,
+ request.mEntitlementServerAddress, request.mEntitlementVersion);
+ Ts43Authentication.Ts43AuthToken authToken = authLibrary.getAuthToken(
+ request.mAesUrl);
+ request.mExecutor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> request.mCallback.onResult(authToken)));
+ } catch (ServiceEntitlementException exception) {
+ request.mExecutor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> request.mCallback.onError(new AuthenticationException(exception))));
+ } finally {
+ mLock.unlock();
+ }
}
}