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