1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.libraries.entitlement; 18 19 import static com.android.libraries.entitlement.eapaka.EapAkaResponse.respondToEapAkaChallenge; 20 21 import android.content.Context; 22 import android.telephony.TelephonyManager; 23 import android.util.Log; 24 25 import androidx.annotation.Nullable; 26 27 import com.android.libraries.entitlement.eapaka.EapAkaApi; 28 import com.android.libraries.entitlement.eapaka.EapAkaChallenge; 29 30 /** 31 * Some utility methods used in EAP-AKA authentication in service entitlement, and could be 32 * helpful to other apps. 33 */ 34 public class EapAkaHelper { 35 private static final String TAG = "ServiceEntitlement"; 36 37 private final Context mContext; 38 private final int mSimSubscriptionId; 39 EapAkaHelper(Context context, int simSubscriptionId)40 EapAkaHelper(Context context, int simSubscriptionId) { 41 mContext = context; 42 mSimSubscriptionId = simSubscriptionId; 43 } 44 45 /** 46 * Factory method. 47 * 48 * @param context context of application 49 * @param simSubscriptionId the subscroption ID of the carrier's SIM on device. This indicates 50 * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA 51 * authentication with. See 52 * {@link android.telephony.SubscriptionManager} 53 * for how to get the subscroption ID. 54 */ getInstance(Context context, int simSubscriptionId)55 public static EapAkaHelper getInstance(Context context, int simSubscriptionId) { 56 return new EapAkaHelper(context, simSubscriptionId); 57 } 58 59 /** 60 * Returns the root NAI for EAP-AKA authentication as per 3GPP TS 23.003 19.3.2, or 61 * {@code null} if failed. The result will be in the form: 62 * 63 * <p>{@code 0<IMSI>@nai.epc.mnc<MNC>.mcc<MCC>.3gppnetwork.org} 64 */ 65 @Nullable getEapAkaRootNai()66 public String getEapAkaRootNai() { 67 TelephonyManager telephonyManager = 68 mContext.getSystemService(TelephonyManager.class) 69 .createForSubscriptionId(mSimSubscriptionId); 70 return EapAkaApi.getImsiEap( 71 telephonyManager.getSimOperator(), telephonyManager.getSubscriberId()); 72 } 73 74 /** 75 * Returns the EAP-AKA challenge response to the given EAP-AKA {@code challenge}, or 76 * {@code null} if failed. 77 * 78 * <p>Both the challange and response are base-64 encoded EAP-AKA message: refer to 79 * RFC 4187 Section 8.1 Message Format/RFC 3748 Session 4 EAP Packet Format. 80 * 81 * @deprecated use {@link getEapAkaResponse(String)} which additionally supports 82 * Synchronization-Failure case. 83 */ 84 @Deprecated 85 @Nullable getEapAkaChallengeResponse(String challenge)86 public String getEapAkaChallengeResponse(String challenge) { 87 EapAkaResponse eapAkaResponse = getEapAkaResponse(challenge); 88 return (eapAkaResponse == null) 89 ? null 90 : eapAkaResponse.response(); // Would be null on synchronization failure 91 } 92 93 /** 94 * Returns the {@link EapAkaResponse} to the given EAP-AKA {@code challenge}, or 95 * {@code null} if failed. 96 * 97 * <p>Both the challange and response are base-64 encoded EAP-AKA message: refer to 98 * RFC 4187 Section 8.1 Message Format/RFC 3748 Session 4 EAP Packet Format. 99 */ 100 @Nullable getEapAkaResponse(String challenge)101 public EapAkaResponse getEapAkaResponse(String challenge) { 102 try { 103 EapAkaChallenge eapAkaChallenge = EapAkaChallenge.parseEapAkaChallenge(challenge); 104 com.android.libraries.entitlement.eapaka.EapAkaResponse eapAkaResponse = 105 respondToEapAkaChallenge(mContext, mSimSubscriptionId, eapAkaChallenge); 106 return new EapAkaResponse( 107 eapAkaResponse.response(), eapAkaResponse.synchronizationFailureResponse()); 108 } catch (ServiceEntitlementException e) { 109 Log.i(TAG, "Failed to generate EAP-AKA response", e); 110 return null; 111 } 112 } 113 114 // Similar to .eapaka.EapAkaResponse but with simplfied API surface for external usage. 115 /** EAP-AKA response */ 116 public static class EapAkaResponse { 117 // RFC 4187 Section 9.4 EAP-Response/AKA-Challenge 118 @Nullable private final String mResponse; 119 // RFC 4187 Section 9.6 EAP-Response/AKA-Synchronization-Failure 120 @Nullable private final String mSynchronizationFailureResponse; 121 EapAkaResponse( @ullable String response, @Nullable String synchronizationFailureResponse)122 private EapAkaResponse( 123 @Nullable String response, @Nullable String synchronizationFailureResponse) { 124 mResponse = response; 125 mSynchronizationFailureResponse = synchronizationFailureResponse; 126 } 127 128 /** 129 * Returns EAP-Response/AKA-Challenge, if authentication success. 130 * Otherwise {@code null}. 131 */ 132 @Nullable response()133 public String response() { 134 return mResponse; 135 } 136 137 /** 138 * Returns EAP-Response/AKA-Synchronization-Failure, if synchronization failure detected. 139 * Otherwise {@code null}. 140 */ 141 @Nullable synchronizationFailureResponse()142 public String synchronizationFailureResponse() { 143 return mSynchronizationFailureResponse; 144 } 145 } 146 } 147