diff options
author | Sarah Chin <sarahchin@google.com> | 2023-06-05 21:24:50 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-06-05 21:24:50 +0000 |
commit | bd7328abe348bb2f0a0d0a6657e6b1e60088fbd6 (patch) | |
tree | 687bf51143dcc38e5229a1f96b73d274d80ac763 | |
parent | 68c8f109db8092e936ad68f1b6f12f5fa8ab4f2d (diff) | |
parent | 6256e66510fb7066d0741e51798c8f64d7bdb11b (diff) | |
download | gsma_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>
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(); + } } } |