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