• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.internal.net.eap.statemachine;
18 
19 import static android.net.eap.EapSessionConfig.EapMethodConfig.EAP_TYPE_AKA;
20 
21 import static com.android.internal.net.eap.EapAuthenticator.LOG;
22 import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS;
23 import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_AUTHENTICATION_REJECT;
24 import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE;
25 import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CLIENT_ERROR;
26 import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY;
27 import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_NOTIFICATION;
28 import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_SYNCHRONIZATION_FAILURE;
29 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ;
30 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN;
31 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_BIDDING;
32 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ENCR_DATA;
33 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_FULLAUTH_ID_REQ;
34 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IV;
35 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC;
36 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ;
37 import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND;
38 
39 import android.annotation.Nullable;
40 import android.content.Context;
41 import android.net.eap.EapSessionConfig.EapAkaConfig;
42 import android.net.eap.EapSessionConfig.EapMethodConfig.EapMethod;
43 import android.telephony.TelephonyManager;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.net.eap.EapResult;
47 import com.android.internal.net.eap.EapResult.EapError;
48 import com.android.internal.net.eap.EapResult.EapSuccess;
49 import com.android.internal.net.eap.crypto.Fips186_2Prf;
50 import com.android.internal.net.eap.exceptions.EapInvalidRequestException;
51 import com.android.internal.net.eap.exceptions.EapSilentException;
52 import com.android.internal.net.eap.exceptions.simaka.EapAkaInvalidAuthenticationResponse;
53 import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException;
54 import com.android.internal.net.eap.exceptions.simaka.EapSimAkaIdentityUnavailableException;
55 import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException;
56 import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidLengthException;
57 import com.android.internal.net.eap.message.EapMessage;
58 import com.android.internal.net.eap.message.simaka.EapAkaTypeData;
59 import com.android.internal.net.eap.message.simaka.EapAkaTypeData.EapAkaTypeDataDecoder;
60 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute;
61 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn;
62 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAuts;
63 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtBidding;
64 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode;
65 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity;
66 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka;
67 import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRes;
68 import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult;
69 
70 import java.nio.BufferUnderflowException;
71 import java.nio.ByteBuffer;
72 import java.nio.charset.StandardCharsets;
73 import java.security.GeneralSecurityException;
74 import java.security.MessageDigest;
75 import java.security.NoSuchAlgorithmException;
76 import java.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.List;
79 import java.util.Set;
80 
81 /**
82  * EapAkaMethodStateMachine represents the valid paths possible for the EAP-AKA protocol.
83  *
84  * <p>EAP-AKA sessions will always follow the path:
85  *
86  * Created --+--> Identity --+--> Challenge --> Final
87  *           |               |
88  *           +---------------+
89  *
90  * Note: If the EAP-Request/AKA-Challenge message contains an AUTN with an invalid sequence number,
91  * the peer will indicate a synchronization failure to the server and a new challenge will be
92  * attempted.
93  *
94  * Note: EAP-Request/Notification messages can be received at any point in the above state machine
95  * At most one EAP-AKA/Notification message is allowed per EAP-AKA session.
96  *
97  * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication
98  * Protocol for Authentication and Key Agreement (EAP-AKA)</a>
99  */
100 class EapAkaMethodStateMachine extends EapSimAkaMethodStateMachine {
101     private static final String TAG = EapAkaMethodStateMachine.class.getSimpleName();
102 
103     // EAP-AKA identity prefix (RFC 4187#4.1.1.6)
104     private static final String AKA_IDENTITY_PREFIX = "0";
105 
106     private final EapAkaTypeDataDecoder mEapAkaTypeDataDecoder;
107     private final boolean mSupportsEapAkaPrime;
108 
EapAkaMethodStateMachine( Context context, byte[] eapIdentity, EapAkaConfig eapAkaConfig)109     protected EapAkaMethodStateMachine(
110             Context context, byte[] eapIdentity, EapAkaConfig eapAkaConfig) {
111         this(context, eapIdentity, eapAkaConfig, false);
112     }
113 
EapAkaMethodStateMachine( Context context, byte[] eapIdentity, EapAkaConfig eapAkaConfig, boolean supportsEapAkaPrime)114     EapAkaMethodStateMachine(
115             Context context,
116             byte[] eapIdentity,
117             EapAkaConfig eapAkaConfig,
118             boolean supportsEapAkaPrime) {
119         this(
120                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
121                 eapIdentity,
122                 eapAkaConfig,
123                 EapAkaTypeData.getEapAkaTypeDataDecoder(),
124                 supportsEapAkaPrime);
125     }
126 
127     @VisibleForTesting
EapAkaMethodStateMachine( TelephonyManager telephonyManager, byte[] eapIdentity, EapAkaConfig eapAkaConfig, EapAkaTypeDataDecoder eapAkaTypeDataDecoder, boolean supportsEapAkaPrime)128     protected EapAkaMethodStateMachine(
129             TelephonyManager telephonyManager,
130             byte[] eapIdentity,
131             EapAkaConfig eapAkaConfig,
132             EapAkaTypeDataDecoder eapAkaTypeDataDecoder,
133             boolean supportsEapAkaPrime) {
134         super(
135                 telephonyManager.createForSubscriptionId(eapAkaConfig.getSubId()),
136                 eapIdentity,
137                 eapAkaConfig);
138         mEapAkaTypeDataDecoder = eapAkaTypeDataDecoder;
139         mSupportsEapAkaPrime = supportsEapAkaPrime;
140 
141         transitionTo(new CreatedState());
142     }
143 
144     @Override
145     @EapMethod
getEapMethod()146     int getEapMethod() {
147         return EAP_TYPE_AKA;
148     }
149 
decode(byte[] typeData)150     protected DecodeResult<EapAkaTypeData> decode(byte[] typeData) {
151         return mEapAkaTypeDataDecoder.decode(typeData);
152     }
153 
154     /**
155      * This exists so we can override the identity prefix in the EapAkaPrimeMethodStateMachine.
156      *
157      * @return the Identity prefix for this EAP method
158      */
getIdentityPrefix()159     protected String getIdentityPrefix() {
160         return AKA_IDENTITY_PREFIX;
161     }
162 
buildChallengeState()163     protected ChallengeState buildChallengeState() {
164         return new ChallengeState();
165     }
166 
buildChallengeState(byte[] identity)167     protected ChallengeState buildChallengeState(byte[] identity) {
168         return new ChallengeState(identity);
169     }
170 
171     protected class CreatedState extends EapMethodState {
172         private final String mTAG = CreatedState.class.getSimpleName();
173 
process(EapMessage message)174         public EapResult process(EapMessage message) {
175             EapResult result = handleEapSuccessFailureNotification(mTAG, message);
176             if (result != null) {
177                 return result;
178             }
179 
180             DecodeResult<? extends EapAkaTypeData> decodeResult =
181                     decode(message.eapData.eapTypeData);
182             if (!decodeResult.isSuccessfulDecode()) {
183                 return buildClientErrorResponse(
184                         message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode);
185             }
186 
187             EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData;
188             switch (eapAkaTypeData.eapSubtype) {
189                 case EAP_AKA_IDENTITY:
190                     return transitionAndProcess(new IdentityState(), message);
191                 case EAP_AKA_CHALLENGE:
192                     return transitionAndProcess(buildChallengeState(), message);
193                 case EAP_AKA_NOTIFICATION:
194                     return handleEapSimAkaNotification(
195                             mTAG,
196                             true, // isPreChallengeState
197                             message.eapIdentifier,
198                             eapAkaTypeData);
199                 default:
200                     return buildClientErrorResponse(
201                             message.eapIdentifier,
202                             getEapMethod(),
203                             AtClientErrorCode.UNABLE_TO_PROCESS);
204             }
205         }
206     }
207 
208     protected class IdentityState extends EapMethodState {
209         private final String mTAG = IdentityState.class.getSimpleName();
210 
211         private byte[] mIdentity;
212 
process(EapMessage message)213         public EapResult process(EapMessage message) {
214             EapResult result = handleEapSuccessFailureNotification(mTAG, message);
215             if (result != null) {
216                 return result;
217             }
218 
219             DecodeResult<? extends EapAkaTypeData> decodeResult =
220                     decode(message.eapData.eapTypeData);
221             if (!decodeResult.isSuccessfulDecode()) {
222                 return buildClientErrorResponse(
223                         message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode);
224             }
225 
226             EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData;
227             switch (eapAkaTypeData.eapSubtype) {
228                 case EAP_AKA_IDENTITY:
229                     break;
230                 case EAP_AKA_CHALLENGE:
231                     return transitionAndProcess(buildChallengeState(mIdentity), message);
232                 case EAP_AKA_NOTIFICATION:
233                     return handleEapSimAkaNotification(
234                             mTAG,
235                             true, // isPreChallengeState
236                             message.eapIdentifier,
237                             eapAkaTypeData);
238                 default:
239                     return buildClientErrorResponse(
240                             message.eapIdentifier,
241                             getEapMethod(),
242                             AtClientErrorCode.UNABLE_TO_PROCESS);
243             }
244 
245             if (!isValidIdentityAttributes(eapAkaTypeData)) {
246                 LOG.e(mTAG, "Invalid attributes: " + eapAkaTypeData.attributeMap.keySet());
247                 return buildClientErrorResponse(
248                         message.eapIdentifier,
249                         EAP_TYPE_AKA,
250                         AtClientErrorCode.UNABLE_TO_PROCESS);
251             }
252 
253             String imsi = mTelephonyManager.getSubscriberId();
254             if (imsi == null) {
255                 int subId = mEapUiccConfig.getSubId();
256                 LOG.e(mTAG, "Unable to get IMSI for subId=" + subId);
257                 return new EapError(
258                         new EapSimAkaIdentityUnavailableException(
259                                 "IMSI for subId (" + subId + ") not available"));
260             }
261             String identityString = getIdentityPrefix() + imsi;
262             mIdentity = identityString.getBytes(StandardCharsets.US_ASCII);
263             LOG.d(mTAG, "EAP-AKA/Identity=" + LOG.pii(identityString));
264 
265             AtIdentity atIdentity;
266             try {
267                 atIdentity = AtIdentity.getAtIdentity(mIdentity);
268             } catch (EapSimAkaInvalidAttributeException ex) {
269                 LOG.wtf(mTAG, "Exception thrown while making AtIdentity attribute", ex);
270                 return new EapError(ex);
271             }
272 
273             return buildResponseMessage(
274                     getEapMethod(),
275                     EAP_AKA_IDENTITY,
276                     message.eapIdentifier,
277                     Arrays.asList(atIdentity));
278         }
279 
isValidIdentityAttributes(EapAkaTypeData eapAkaTypeData)280         private boolean isValidIdentityAttributes(EapAkaTypeData eapAkaTypeData) {
281             Set<Integer> attrs = eapAkaTypeData.attributeMap.keySet();
282 
283             // exactly one ID request type required
284             int idRequests = 0;
285             idRequests += attrs.contains(EAP_AT_PERMANENT_ID_REQ) ? 1 : 0;
286             idRequests += attrs.contains(EAP_AT_ANY_ID_REQ) ? 1 : 0;
287             idRequests += attrs.contains(EAP_AT_FULLAUTH_ID_REQ) ? 1 : 0;
288 
289             if (idRequests != 1) {
290                 return false;
291             }
292 
293             // can't contain mac, iv, encr data
294             if (attrs.contains(EAP_AT_MAC)
295                     || attrs.contains(EAP_AT_IV)
296                     || attrs.contains(EAP_AT_ENCR_DATA)) {
297                 return false;
298             }
299             return true;
300         }
301     }
302 
303     protected class ChallengeState extends EapMethodState {
304         private final String mTAG = ChallengeState.class.getSimpleName();
305 
306         @VisibleForTesting boolean mHadSuccessfulChallenge = false;
307         @VisibleForTesting protected final byte[] mIdentity;
308 
309         // IK and CK lengths defined as 16B (RFC 4187#1)
310         private final int mIkLenBytes = 16;
311         private final int mCkLenBytes = 16;
312 
313         // Tags for Successful and Synchronization responses
314         private final byte mSuccess = (byte) 0xDB;
315         private final byte mSynchronization = (byte) 0xDC;
316 
ChallengeState()317         ChallengeState() {
318             // use the EAP-Identity for the default value (RFC 4187#7)
319             this(mEapIdentity);
320         }
321 
ChallengeState(byte[] identity)322         ChallengeState(byte[] identity) {
323             this.mIdentity = identity;
324         }
325 
process(EapMessage message)326         public EapResult process(EapMessage message) {
327             if (message.eapCode == EAP_CODE_SUCCESS) {
328                 if (!mHadSuccessfulChallenge) {
329                     LOG.e(mTAG, "Received unexpected EAP-Success");
330                     return new EapError(
331                             new EapInvalidRequestException(
332                                     "Received an EAP-Success in the ChallengeState"));
333                 }
334                 transitionTo(new FinalState());
335                 return new EapSuccess(mMsk, mEmsk);
336             }
337 
338             EapResult result = handleEapSuccessFailureNotification(mTAG, message);
339             if (result != null) {
340                 return result;
341             }
342 
343             DecodeResult<? extends EapAkaTypeData> decodeResult =
344                     decode(message.eapData.eapTypeData);
345             if (!decodeResult.isSuccessfulDecode()) {
346                 return buildClientErrorResponse(
347                         message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode);
348             }
349 
350             EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData;
351             switch (eapAkaTypeData.eapSubtype) {
352                 case EAP_AKA_CHALLENGE:
353                     break;
354                 case EAP_AKA_NOTIFICATION:
355                     return handleEapSimAkaNotification(
356                             mTAG,
357                             false, // isPreChallengeState
358                             message.eapIdentifier,
359                             eapAkaTypeData);
360                 default:
361                     return buildClientErrorResponse(
362                             message.eapIdentifier,
363                             getEapMethod(),
364                             AtClientErrorCode.UNABLE_TO_PROCESS);
365             }
366 
367             if (!isValidChallengeAttributes(eapAkaTypeData)) {
368                 LOG.e(mTAG, "Invalid attributes: " + eapAkaTypeData.attributeMap.keySet());
369                 return buildClientErrorResponse(
370                         message.eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS);
371             }
372 
373             return handleChallengeAuthentication(message, eapAkaTypeData);
374         }
375 
handleChallengeAuthentication( EapMessage message, EapAkaTypeData eapAkaTypeData)376         protected EapResult handleChallengeAuthentication(
377                 EapMessage message, EapAkaTypeData eapAkaTypeData) {
378             RandChallengeResult result;
379             try {
380                 result = getRandChallengeResult(eapAkaTypeData);
381             } catch (EapAkaInvalidAuthenticationResponse ex) {
382                 return new EapError(ex);
383             } catch (EapSimAkaInvalidLengthException | BufferUnderflowException ex) {
384                 LOG.e(mTAG, "Invalid response returned from SIM", ex);
385                 return buildClientErrorResponse(
386                         message.eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS);
387             } catch (EapSimAkaAuthenticationFailureException ex) {
388                 // Return EAP-Response/AKA-Authentication-Reject when the AUTN is rejected
389                 // (RFC 4187#6.3.1)
390                 return buildAuthenticationRejectMessage(message.eapIdentifier);
391             }
392 
393             if (!result.isSuccessfulResult()) {
394                 try {
395                     return buildResponseMessage(
396                             getEapMethod(),
397                             EAP_AKA_SYNCHRONIZATION_FAILURE,
398                             message.eapIdentifier,
399                             Arrays.asList(new AtAuts(result.auts)));
400                 } catch (EapSimAkaInvalidAttributeException ex) {
401                     LOG.wtf(mTAG, "Error creating an AtAuts attr", ex);
402                     return new EapError(ex);
403                 }
404             }
405 
406             EapResult eapResult =
407                     generateAndPersistEapAkaKeys(result, message.eapIdentifier, eapAkaTypeData);
408             if (eapResult != null) {
409                 return eapResult;
410             }
411 
412             try {
413                 if (!isValidMac(mTAG, message, eapAkaTypeData, new byte[0])) {
414                     return buildClientErrorResponse(
415                             message.eapIdentifier,
416                             getEapMethod(),
417                             AtClientErrorCode.UNABLE_TO_PROCESS);
418                 }
419             } catch (GeneralSecurityException
420                     | EapSilentException
421                     | EapSimAkaInvalidAttributeException ex) {
422                 // if the MAC can't be generated, we can't continue
423                 LOG.e(mTAG, "Error computing MAC for EapMessage", ex);
424                 return new EapError(ex);
425             }
426 
427             // before sending a response, check for bidding-down attacks (RFC 5448#4)
428             if (mSupportsEapAkaPrime) {
429                 AtBidding atBidding = (AtBidding) eapAkaTypeData.attributeMap.get(EAP_AT_BIDDING);
430                 if (atBidding != null && atBidding.doesServerSupportEapAkaPrime) {
431                     LOG.w(
432                             mTAG,
433                             "Potential bidding down attack. AT_BIDDING attr included and EAP-AKA'"
434                                 + " is supported");
435                     return buildAuthenticationRejectMessage(message.eapIdentifier);
436                 }
437             }
438 
439             // server has been authenticated, so we can send a response
440             try {
441                 mHadSuccessfulChallenge = true;
442                 return buildResponseMessageWithMac(
443                         message.eapIdentifier,
444                         EAP_AKA_CHALLENGE,
445                         new byte[0],
446                         Arrays.asList(AtRes.getAtRes(result.res)));
447             } catch (EapSimAkaInvalidAttributeException ex) {
448                 LOG.wtf(mTAG, "Error creating AtRes value", ex);
449                 return new EapError(ex);
450             }
451         }
452 
453         @VisibleForTesting
454         class RandChallengeResult {
455             public final byte[] res;
456             public final byte[] ik;
457             public final byte[] ck;
458             public final byte[] auts;
459 
RandChallengeResult(byte[] res, byte[] ik, byte[] ck)460             RandChallengeResult(byte[] res, byte[] ik, byte[] ck)
461                     throws EapSimAkaInvalidLengthException {
462                 if (!AtRes.isValidResLen(res.length)) {
463                     throw new EapSimAkaInvalidLengthException("Invalid RES length");
464                 } else if (ik.length != mIkLenBytes) {
465                     throw new EapSimAkaInvalidLengthException("Invalid IK length");
466                 } else if (ck.length != mCkLenBytes) {
467                     throw new EapSimAkaInvalidLengthException("Invalid CK length");
468                 }
469 
470                 this.res = res;
471                 this.ik = ik;
472                 this.ck = ck;
473                 this.auts = null;
474             }
475 
RandChallengeResult(byte[] auts)476             RandChallengeResult(byte[] auts) throws EapSimAkaInvalidLengthException {
477                 if (auts.length != AtAuts.AUTS_LENGTH) {
478                     throw new EapSimAkaInvalidLengthException("Invalid AUTS length");
479                 }
480 
481                 this.res = null;
482                 this.ik = null;
483                 this.ck = null;
484                 this.auts = auts;
485             }
486 
isSuccessfulResult()487             private boolean isSuccessfulResult() {
488                 return res != null && ik != null && ck != null;
489             }
490         }
491 
isValidChallengeAttributes(EapAkaTypeData eapAkaTypeData)492         private boolean isValidChallengeAttributes(EapAkaTypeData eapAkaTypeData) {
493             Set<Integer> attrs = eapAkaTypeData.attributeMap.keySet();
494 
495             // must contain: AT_RAND, AT_AUTN, AT_MAC
496             return attrs.contains(EAP_AT_RAND)
497                     && attrs.contains(EAP_AT_AUTN)
498                     && attrs.contains(EAP_AT_MAC);
499         }
500 
getRandChallengeResult(EapAkaTypeData eapAkaTypeData)501         private RandChallengeResult getRandChallengeResult(EapAkaTypeData eapAkaTypeData)
502                 throws EapSimAkaAuthenticationFailureException, EapSimAkaInvalidLengthException {
503             AtRandAka atRandAka = (AtRandAka) eapAkaTypeData.attributeMap.get(EAP_AT_RAND);
504             AtAutn atAutn = (AtAutn) eapAkaTypeData.attributeMap.get(EAP_AT_AUTN);
505 
506             // pre-Base64 formatting needs to be: [Length][RAND][Length][AUTN]
507             int randLen = atRandAka.rand.length;
508             int autnLen = atAutn.autn.length;
509             ByteBuffer formattedChallenge = ByteBuffer.allocate(1 + randLen + 1 + autnLen);
510             formattedChallenge.put((byte) randLen);
511             formattedChallenge.put(atRandAka.rand);
512             formattedChallenge.put((byte) autnLen);
513             formattedChallenge.put(atAutn.autn);
514 
515             byte[] challengeResponse =
516                     processUiccAuthentication(
517                             mTAG,
518                             TelephonyManager.AUTHTYPE_EAP_AKA,
519                             formattedChallenge.array());
520             ByteBuffer buffer = ByteBuffer.wrap(challengeResponse);
521             byte tag = buffer.get();
522 
523             switch (tag) {
524                 case mSuccess:
525                     // response format: [tag][RES length][RES][CK length][CK][IK length][IK]
526                     // (TS 131 102#7.1.2.1)
527                     break;
528                 case mSynchronization:
529                     // response format: [tag][AUTS length][AUTS]
530                     // (TS 131 102#7.1.2.1)
531                     byte[] auts = new byte[Byte.toUnsignedInt(buffer.get())];
532                     buffer.get(auts);
533 
534                     LOG.i(mTAG, "Synchronization Failure");
535                     LOG.d(
536                             mTAG,
537                             "RAND=" + LOG.pii(atRandAka.rand)
538                                     + " AUTN=" + LOG.pii(atAutn.autn)
539                                     + " AUTS=" + LOG.pii(auts));
540 
541                     return new RandChallengeResult(auts);
542                 default:
543                     throw new EapAkaInvalidAuthenticationResponse(
544                             "Invalid tag for UICC response: " + String.format("%02X", tag));
545             }
546 
547             byte[] res = new byte[Byte.toUnsignedInt(buffer.get())];
548             buffer.get(res);
549 
550             byte[] ck = new byte[Byte.toUnsignedInt(buffer.get())];
551             buffer.get(ck);
552 
553             byte[] ik = new byte[Byte.toUnsignedInt(buffer.get())];
554             buffer.get(ik);
555 
556             LOG.d(mTAG, "RAND=" + LOG.pii(atRandAka.rand));
557             LOG.d(mTAG, "AUTN=" + LOG.pii(atAutn.autn));
558             LOG.d(mTAG, "RES=" + LOG.pii(res));
559             LOG.d(mTAG, "IK=" + LOG.pii(ik));
560             LOG.d(mTAG, "CK=" + LOG.pii(ck));
561 
562             return new RandChallengeResult(res, ik, ck);
563         }
564 
buildAuthenticationRejectMessage(int eapIdentifier)565         protected EapResult buildAuthenticationRejectMessage(int eapIdentifier) {
566             mIsExpectingEapFailure = true;
567             return buildResponseMessage(
568                     getEapMethod(),
569                     EAP_AKA_AUTHENTICATION_REJECT,
570                     eapIdentifier,
571                     new ArrayList<>());
572         }
573 
574         @Nullable
generateAndPersistEapAkaKeys( RandChallengeResult result, int eapIdentifier, EapAkaTypeData eapAkaTypeData)575         protected EapResult generateAndPersistEapAkaKeys(
576                 RandChallengeResult result, int eapIdentifier, EapAkaTypeData eapAkaTypeData) {
577             try {
578                 MessageDigest sha1 = MessageDigest.getInstance(MASTER_KEY_GENERATION_ALG);
579                 byte[] mkInputData = getMkInputData(result);
580                 generateAndPersistKeys(mTAG, sha1, new Fips186_2Prf(), mkInputData);
581                 return null;
582             } catch (NoSuchAlgorithmException | BufferUnderflowException ex) {
583                 LOG.e(mTAG, "Error while creating keys", ex);
584                 return buildClientErrorResponse(
585                         eapIdentifier, EAP_TYPE_AKA, AtClientErrorCode.UNABLE_TO_PROCESS);
586             }
587         }
588 
getMkInputData(RandChallengeResult result)589         private byte[] getMkInputData(RandChallengeResult result) {
590             int numInputBytes = mIdentity.length + result.ik.length + result.ck.length;
591             ByteBuffer buffer = ByteBuffer.allocate(numInputBytes);
592             buffer.put(mIdentity);
593             buffer.put(result.ik);
594             buffer.put(result.ck);
595             return buffer.array();
596         }
597     }
598 
getEapSimAkaTypeData(AtClientErrorCode clientErrorCode)599     EapAkaTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode) {
600         return new EapAkaTypeData(EAP_AKA_CLIENT_ERROR, Arrays.asList(clientErrorCode));
601     }
602 
getEapSimAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes)603     EapAkaTypeData getEapSimAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) {
604         return new EapAkaTypeData(eapSubtype, attributes);
605     }
606 }
607