| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.net.eap.statemachine; |
| |
| import static com.android.internal.net.eap.EapAuthenticator.LOG; |
| import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; |
| import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; |
| import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; |
| import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; |
| import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_AUTHENTICATION_REJECT; |
| import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CHALLENGE; |
| import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_CLIENT_ERROR; |
| import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_IDENTITY; |
| import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_NOTIFICATION; |
| import static com.android.internal.net.eap.message.simaka.EapAkaTypeData.EAP_AKA_SYNCHRONIZATION_FAILURE; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ANY_ID_REQ; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_AUTN; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_BIDDING; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_ENCR_DATA; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_FULLAUTH_ID_REQ; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_IV; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_MAC; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_PERMANENT_ID_REQ; |
| import static com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.EAP_AT_RAND; |
| |
| import android.annotation.Nullable; |
| import android.content.Context; |
| import android.net.eap.EapSessionConfig.EapAkaConfig; |
| import android.telephony.TelephonyManager; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.net.eap.EapResult; |
| import com.android.internal.net.eap.EapResult.EapError; |
| import com.android.internal.net.eap.EapResult.EapFailure; |
| import com.android.internal.net.eap.EapResult.EapSuccess; |
| import com.android.internal.net.eap.crypto.Fips186_2Prf; |
| import com.android.internal.net.eap.exceptions.EapInvalidRequestException; |
| import com.android.internal.net.eap.exceptions.EapSilentException; |
| import com.android.internal.net.eap.exceptions.simaka.EapAkaInvalidAuthenticationResponse; |
| import com.android.internal.net.eap.exceptions.simaka.EapSimAkaAuthenticationFailureException; |
| import com.android.internal.net.eap.exceptions.simaka.EapSimAkaIdentityUnavailableException; |
| import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidAttributeException; |
| import com.android.internal.net.eap.exceptions.simaka.EapSimAkaInvalidLengthException; |
| import com.android.internal.net.eap.message.EapData.EapMethod; |
| import com.android.internal.net.eap.message.EapMessage; |
| import com.android.internal.net.eap.message.simaka.EapAkaTypeData; |
| import com.android.internal.net.eap.message.simaka.EapAkaTypeData.EapAkaTypeDataDecoder; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAutn; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtAuts; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtBidding; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtClientErrorCode; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtIdentity; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRandAka; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaAttribute.AtRes; |
| import com.android.internal.net.eap.message.simaka.EapSimAkaTypeData.DecodeResult; |
| |
| import java.nio.BufferUnderflowException; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.StandardCharsets; |
| import java.security.GeneralSecurityException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * EapAkaMethodStateMachine represents the valid paths possible for the EAP-AKA protocol. |
| * |
| * <p>EAP-AKA sessions will always follow the path: |
| * |
| * Created --+--> Identity --+--> Challenge --> Final |
| * | | |
| * +---------------+ |
| * |
| * Note: If the EAP-Request/AKA-Challenge message contains an AUTN with an invalid sequence number, |
| * the peer will indicate a synchronization failure to the server and a new challenge will be |
| * attempted. |
| * |
| * Note: EAP-Request/Notification messages can be received at any point in the above state machine |
| * At most one EAP-AKA/Notification message is allowed per EAP-AKA session. |
| * |
| * @see <a href="https://tools.ietf.org/html/rfc4187">RFC 4187, Extensible Authentication |
| * Protocol for Authentication and Key Agreement (EAP-AKA)</a> |
| */ |
| class EapAkaMethodStateMachine extends EapSimAkaMethodStateMachine { |
| private static final String TAG = EapAkaMethodStateMachine.class.getSimpleName(); |
| |
| // EAP-AKA identity prefix (RFC 4187#4.1.1.6) |
| private static final String AKA_IDENTITY_PREFIX = "0"; |
| |
| private final EapAkaTypeDataDecoder mEapAkaTypeDataDecoder; |
| private final boolean mSupportsEapAkaPrime; |
| |
| protected EapAkaMethodStateMachine( |
| Context context, byte[] eapIdentity, EapAkaConfig eapAkaConfig) { |
| this(context, eapIdentity, eapAkaConfig, false); |
| } |
| |
| EapAkaMethodStateMachine( |
| Context context, |
| byte[] eapIdentity, |
| EapAkaConfig eapAkaConfig, |
| boolean supportsEapAkaPrime) { |
| this( |
| (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), |
| eapIdentity, |
| eapAkaConfig, |
| EapAkaTypeData.getEapAkaTypeDataDecoder(), |
| supportsEapAkaPrime); |
| } |
| |
| @VisibleForTesting |
| protected EapAkaMethodStateMachine( |
| TelephonyManager telephonyManager, |
| byte[] eapIdentity, |
| EapAkaConfig eapAkaConfig, |
| EapAkaTypeDataDecoder eapAkaTypeDataDecoder, |
| boolean supportsEapAkaPrime) { |
| super( |
| telephonyManager.createForSubscriptionId(eapAkaConfig.subId), |
| eapIdentity, |
| eapAkaConfig); |
| mEapAkaTypeDataDecoder = eapAkaTypeDataDecoder; |
| mSupportsEapAkaPrime = supportsEapAkaPrime; |
| |
| transitionTo(new CreatedState()); |
| } |
| |
| @Override |
| @EapMethod |
| int getEapMethod() { |
| return EAP_TYPE_AKA; |
| } |
| |
| protected DecodeResult<EapAkaTypeData> decode(byte[] typeData) { |
| return mEapAkaTypeDataDecoder.decode(typeData); |
| } |
| |
| /** |
| * This exists so we can override the identity prefix in the EapAkaPrimeMethodStateMachine. |
| * |
| * @return the Identity prefix for this EAP method |
| */ |
| protected String getIdentityPrefix() { |
| return AKA_IDENTITY_PREFIX; |
| } |
| |
| protected ChallengeState buildChallengeState() { |
| return new ChallengeState(); |
| } |
| |
| protected ChallengeState buildChallengeState(byte[] identity) { |
| return new ChallengeState(identity); |
| } |
| |
| protected class CreatedState extends EapMethodState { |
| private final String mTAG = CreatedState.class.getSimpleName(); |
| |
| public EapResult process(EapMessage message) { |
| EapResult result = handleEapSuccessFailureNotification(mTAG, message); |
| if (result != null) { |
| return result; |
| } |
| |
| DecodeResult<? extends EapAkaTypeData> decodeResult = |
| decode(message.eapData.eapTypeData); |
| if (!decodeResult.isSuccessfulDecode()) { |
| return buildClientErrorResponse( |
| message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode); |
| } |
| |
| EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData; |
| switch (eapAkaTypeData.eapSubtype) { |
| case EAP_AKA_IDENTITY: |
| return transitionAndProcess(new IdentityState(), message); |
| case EAP_AKA_CHALLENGE: |
| return transitionAndProcess(buildChallengeState(), message); |
| case EAP_AKA_NOTIFICATION: |
| return handleEapSimAkaNotification( |
| mTAG, |
| true, // isPreChallengeState |
| message.eapIdentifier, |
| eapAkaTypeData); |
| default: |
| return buildClientErrorResponse( |
| message.eapIdentifier, |
| getEapMethod(), |
| AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| } |
| } |
| |
| protected class IdentityState extends EapMethodState { |
| private final String mTAG = IdentityState.class.getSimpleName(); |
| |
| private byte[] mIdentity; |
| |
| public EapResult process(EapMessage message) { |
| EapResult result = handleEapSuccessFailureNotification(mTAG, message); |
| if (result != null) { |
| return result; |
| } |
| |
| DecodeResult<? extends EapAkaTypeData> decodeResult = |
| decode(message.eapData.eapTypeData); |
| if (!decodeResult.isSuccessfulDecode()) { |
| return buildClientErrorResponse( |
| message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode); |
| } |
| |
| EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData; |
| switch (eapAkaTypeData.eapSubtype) { |
| case EAP_AKA_IDENTITY: |
| break; |
| case EAP_AKA_CHALLENGE: |
| return transitionAndProcess(buildChallengeState(mIdentity), message); |
| case EAP_AKA_NOTIFICATION: |
| return handleEapSimAkaNotification( |
| mTAG, |
| true, // isPreChallengeState |
| message.eapIdentifier, |
| eapAkaTypeData); |
| default: |
| return buildClientErrorResponse( |
| message.eapIdentifier, |
| getEapMethod(), |
| AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| |
| if (!isValidIdentityAttributes(eapAkaTypeData)) { |
| LOG.e(mTAG, "Invalid attributes: " + eapAkaTypeData.attributeMap.keySet()); |
| return buildClientErrorResponse( |
| message.eapIdentifier, |
| EAP_TYPE_AKA, |
| AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| |
| String imsi = mTelephonyManager.getSubscriberId(); |
| if (imsi == null) { |
| LOG.e(mTAG, "Unable to get IMSI for subId=" + mEapUiccConfig.subId); |
| return new EapError( |
| new EapSimAkaIdentityUnavailableException( |
| "IMSI for subId (" + mEapUiccConfig.subId + ") not available")); |
| } |
| String identityString = getIdentityPrefix() + imsi; |
| mIdentity = identityString.getBytes(StandardCharsets.US_ASCII); |
| LOG.d(mTAG, "EAP-AKA/Identity=" + LOG.pii(identityString)); |
| |
| AtIdentity atIdentity; |
| try { |
| atIdentity = AtIdentity.getAtIdentity(mIdentity); |
| } catch (EapSimAkaInvalidAttributeException ex) { |
| LOG.wtf(mTAG, "Exception thrown while making AtIdentity attribute", ex); |
| return new EapError(ex); |
| } |
| |
| return buildResponseMessage( |
| getEapMethod(), |
| EAP_AKA_IDENTITY, |
| message.eapIdentifier, |
| Arrays.asList(atIdentity)); |
| } |
| |
| private boolean isValidIdentityAttributes(EapAkaTypeData eapAkaTypeData) { |
| Set<Integer> attrs = eapAkaTypeData.attributeMap.keySet(); |
| |
| // exactly one ID request type required |
| int idRequests = 0; |
| idRequests += attrs.contains(EAP_AT_PERMANENT_ID_REQ) ? 1 : 0; |
| idRequests += attrs.contains(EAP_AT_ANY_ID_REQ) ? 1 : 0; |
| idRequests += attrs.contains(EAP_AT_FULLAUTH_ID_REQ) ? 1 : 0; |
| |
| if (idRequests != 1) { |
| return false; |
| } |
| |
| // can't contain mac, iv, encr data |
| if (attrs.contains(EAP_AT_MAC) |
| || attrs.contains(EAP_AT_IV) |
| || attrs.contains(EAP_AT_ENCR_DATA)) { |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| protected class ChallengeState extends EapMethodState { |
| private final String mTAG = ChallengeState.class.getSimpleName(); |
| |
| @VisibleForTesting boolean mHadSuccessfulChallenge = false; |
| @VisibleForTesting protected final byte[] mIdentity; |
| |
| // IK and CK lengths defined as 16B (RFC 4187#1) |
| private final int mIkLenBytes = 16; |
| private final int mCkLenBytes = 16; |
| |
| // Tags for Successful and Synchronization responses |
| private final byte mSuccess = (byte) 0xDB; |
| private final byte mSynchronization = (byte) 0xDC; |
| |
| ChallengeState() { |
| // use the EAP-Identity for the default value (RFC 4187#7) |
| this(mEapIdentity); |
| } |
| |
| ChallengeState(byte[] identity) { |
| this.mIdentity = identity; |
| } |
| |
| public EapResult process(EapMessage message) { |
| if (message.eapCode == EAP_CODE_SUCCESS) { |
| if (!mHadSuccessfulChallenge) { |
| LOG.e(mTAG, "Received unexpected EAP-Success"); |
| return new EapError( |
| new EapInvalidRequestException( |
| "Received an EAP-Success in the ChallengeState")); |
| } |
| transitionTo(new FinalState()); |
| return new EapSuccess(mMsk, mEmsk); |
| } else if (message.eapCode == EAP_CODE_FAILURE) { |
| transitionTo(new FinalState()); |
| return new EapFailure(); |
| } else if (message.eapData.eapType == EAP_NOTIFICATION) { |
| return handleEapNotification(mTAG, message); |
| } |
| |
| if (message.eapData.eapType != getEapMethod()) { |
| return new EapError(new EapInvalidRequestException( |
| "Expected EAP Type " + getEapMethod() |
| + ", received " + message.eapData.eapType)); |
| } |
| |
| DecodeResult<? extends EapAkaTypeData> decodeResult = |
| decode(message.eapData.eapTypeData); |
| if (!decodeResult.isSuccessfulDecode()) { |
| return buildClientErrorResponse( |
| message.eapIdentifier, getEapMethod(), decodeResult.atClientErrorCode); |
| } |
| |
| EapAkaTypeData eapAkaTypeData = decodeResult.eapTypeData; |
| switch (eapAkaTypeData.eapSubtype) { |
| case EAP_AKA_CHALLENGE: |
| break; |
| case EAP_AKA_NOTIFICATION: |
| return handleEapSimAkaNotification( |
| mTAG, |
| false, // isPreChallengeState |
| message.eapIdentifier, |
| eapAkaTypeData); |
| default: |
| return buildClientErrorResponse( |
| message.eapIdentifier, |
| getEapMethod(), |
| AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| |
| if (!isValidChallengeAttributes(eapAkaTypeData)) { |
| LOG.e(mTAG, "Invalid attributes: " + eapAkaTypeData.attributeMap.keySet()); |
| return buildClientErrorResponse( |
| message.eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| |
| return handleChallengeAuthentication(message, eapAkaTypeData); |
| } |
| |
| protected EapResult handleChallengeAuthentication( |
| EapMessage message, EapAkaTypeData eapAkaTypeData) { |
| RandChallengeResult result; |
| try { |
| result = getRandChallengeResult(eapAkaTypeData); |
| } catch (EapAkaInvalidAuthenticationResponse ex) { |
| return new EapError(ex); |
| } catch (EapSimAkaInvalidLengthException | BufferUnderflowException ex) { |
| LOG.e(mTAG, "Invalid response returned from SIM", ex); |
| return buildClientErrorResponse( |
| message.eapIdentifier, getEapMethod(), AtClientErrorCode.UNABLE_TO_PROCESS); |
| } catch (EapSimAkaAuthenticationFailureException ex) { |
| // Return EAP-Response/AKA-Authentication-Reject when the AUTN is rejected |
| // (RFC 4187#6.3.1) |
| return buildAuthenticationRejectMessage(message.eapIdentifier); |
| } |
| |
| if (!result.isSuccessfulResult()) { |
| try { |
| return buildResponseMessage( |
| getEapMethod(), |
| EAP_AKA_SYNCHRONIZATION_FAILURE, |
| message.eapIdentifier, |
| Arrays.asList(new AtAuts(result.auts))); |
| } catch (EapSimAkaInvalidAttributeException ex) { |
| LOG.wtf(mTAG, "Error creating an AtAuts attr", ex); |
| return new EapError(ex); |
| } |
| } |
| |
| EapResult eapResult = |
| generateAndPersistEapAkaKeys(result, message.eapIdentifier, eapAkaTypeData); |
| if (eapResult != null) { |
| return eapResult; |
| } |
| |
| try { |
| if (!isValidMac(mTAG, message, eapAkaTypeData, new byte[0])) { |
| return buildClientErrorResponse( |
| message.eapIdentifier, |
| getEapMethod(), |
| AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| } catch (GeneralSecurityException |
| | EapSilentException |
| | EapSimAkaInvalidAttributeException ex) { |
| // if the MAC can't be generated, we can't continue |
| LOG.e(mTAG, "Error computing MAC for EapMessage", ex); |
| return new EapError(ex); |
| } |
| |
| // before sending a response, check for bidding-down attacks (RFC 5448#4) |
| if (mSupportsEapAkaPrime) { |
| AtBidding atBidding = (AtBidding) eapAkaTypeData.attributeMap.get(EAP_AT_BIDDING); |
| if (atBidding != null && atBidding.doesServerSupportEapAkaPrime) { |
| LOG.w( |
| mTAG, |
| "Potential bidding down attack. AT_BIDDING attr included and EAP-AKA'" |
| + " is supported"); |
| return buildAuthenticationRejectMessage(message.eapIdentifier); |
| } |
| } |
| |
| // server has been authenticated, so we can send a response |
| try { |
| mHadSuccessfulChallenge = true; |
| return buildResponseMessageWithMac( |
| message.eapIdentifier, |
| EAP_AKA_CHALLENGE, |
| new byte[0], |
| Arrays.asList(AtRes.getAtRes(result.res))); |
| } catch (EapSimAkaInvalidAttributeException ex) { |
| LOG.wtf(mTAG, "Error creating AtRes value", ex); |
| return new EapError(ex); |
| } |
| } |
| |
| @VisibleForTesting |
| class RandChallengeResult { |
| public final byte[] res; |
| public final byte[] ik; |
| public final byte[] ck; |
| public final byte[] auts; |
| |
| RandChallengeResult(byte[] res, byte[] ik, byte[] ck) |
| throws EapSimAkaInvalidLengthException { |
| if (!AtRes.isValidResLen(res.length)) { |
| throw new EapSimAkaInvalidLengthException("Invalid RES length"); |
| } else if (ik.length != mIkLenBytes) { |
| throw new EapSimAkaInvalidLengthException("Invalid IK length"); |
| } else if (ck.length != mCkLenBytes) { |
| throw new EapSimAkaInvalidLengthException("Invalid CK length"); |
| } |
| |
| this.res = res; |
| this.ik = ik; |
| this.ck = ck; |
| this.auts = null; |
| } |
| |
| RandChallengeResult(byte[] auts) throws EapSimAkaInvalidLengthException { |
| if (auts.length != AtAuts.AUTS_LENGTH) { |
| throw new EapSimAkaInvalidLengthException("Invalid AUTS length"); |
| } |
| |
| this.res = null; |
| this.ik = null; |
| this.ck = null; |
| this.auts = auts; |
| } |
| |
| private boolean isSuccessfulResult() { |
| return res != null && ik != null && ck != null; |
| } |
| } |
| |
| private boolean isValidChallengeAttributes(EapAkaTypeData eapAkaTypeData) { |
| Set<Integer> attrs = eapAkaTypeData.attributeMap.keySet(); |
| |
| // must contain: AT_RAND, AT_AUTN, AT_MAC |
| return attrs.contains(EAP_AT_RAND) |
| && attrs.contains(EAP_AT_AUTN) |
| && attrs.contains(EAP_AT_MAC); |
| } |
| |
| private RandChallengeResult getRandChallengeResult(EapAkaTypeData eapAkaTypeData) |
| throws EapSimAkaAuthenticationFailureException, EapSimAkaInvalidLengthException { |
| AtRandAka atRandAka = (AtRandAka) eapAkaTypeData.attributeMap.get(EAP_AT_RAND); |
| AtAutn atAutn = (AtAutn) eapAkaTypeData.attributeMap.get(EAP_AT_AUTN); |
| |
| // pre-Base64 formatting needs to be: [Length][RAND][Length][AUTN] |
| int randLen = atRandAka.rand.length; |
| int autnLen = atAutn.autn.length; |
| ByteBuffer formattedChallenge = ByteBuffer.allocate(1 + randLen + 1 + autnLen); |
| formattedChallenge.put((byte) randLen); |
| formattedChallenge.put(atRandAka.rand); |
| formattedChallenge.put((byte) autnLen); |
| formattedChallenge.put(atAutn.autn); |
| |
| byte[] challengeResponse = |
| processUiccAuthentication( |
| mTAG, |
| TelephonyManager.AUTHTYPE_EAP_AKA, |
| formattedChallenge.array()); |
| ByteBuffer buffer = ByteBuffer.wrap(challengeResponse); |
| byte tag = buffer.get(); |
| |
| switch (tag) { |
| case mSuccess: |
| // response format: [tag][RES length][RES][IK length][IK][CK length][CK] |
| break; |
| case mSynchronization: |
| // response format: [tag][AUTS length][AUTS] |
| byte[] auts = new byte[Byte.toUnsignedInt(buffer.get())]; |
| buffer.get(auts); |
| |
| LOG.i(mTAG, "Synchronization Failure"); |
| LOG.d( |
| mTAG, |
| "RAND=" + LOG.pii(atRandAka.rand) |
| + " AUTN=" + LOG.pii(atAutn.autn) |
| + " AUTS=" + LOG.pii(auts)); |
| |
| return new RandChallengeResult(auts); |
| default: |
| throw new EapAkaInvalidAuthenticationResponse( |
| "Invalid tag for UICC response: " + String.format("%02X", tag)); |
| } |
| |
| byte[] res = new byte[Byte.toUnsignedInt(buffer.get())]; |
| buffer.get(res); |
| |
| byte[] ik = new byte[Byte.toUnsignedInt(buffer.get())]; |
| buffer.get(ik); |
| |
| byte[] ck = new byte[Byte.toUnsignedInt(buffer.get())]; |
| buffer.get(ck); |
| |
| LOG.d( |
| mTAG, |
| "RAND=" + LOG.pii(atRandAka.rand) |
| + " AUTN=" + LOG.pii(atAutn.autn) |
| + " RES=" + LOG.pii(res) |
| + " IK=" + LOG.pii(ik) |
| + " CK=" + LOG.pii(ck)); |
| |
| return new RandChallengeResult(res, ik, ck); |
| } |
| |
| protected EapResult buildAuthenticationRejectMessage(int eapIdentifier) { |
| return buildResponseMessage( |
| getEapMethod(), |
| EAP_AKA_AUTHENTICATION_REJECT, |
| eapIdentifier, |
| new ArrayList<>()); |
| } |
| |
| @Nullable |
| protected EapResult generateAndPersistEapAkaKeys( |
| RandChallengeResult result, int eapIdentifier, EapAkaTypeData eapAkaTypeData) { |
| try { |
| MessageDigest sha1 = MessageDigest.getInstance(MASTER_KEY_GENERATION_ALG); |
| byte[] mkInputData = getMkInputData(result); |
| generateAndPersistKeys(mTAG, sha1, new Fips186_2Prf(), mkInputData); |
| return null; |
| } catch (NoSuchAlgorithmException | BufferUnderflowException ex) { |
| LOG.e(mTAG, "Error while creating keys", ex); |
| return buildClientErrorResponse( |
| eapIdentifier, EAP_TYPE_AKA, AtClientErrorCode.UNABLE_TO_PROCESS); |
| } |
| } |
| |
| private byte[] getMkInputData(RandChallengeResult result) { |
| int numInputBytes = mIdentity.length + result.ik.length + result.ck.length; |
| ByteBuffer buffer = ByteBuffer.allocate(numInputBytes); |
| buffer.put(mIdentity); |
| buffer.put(result.ik); |
| buffer.put(result.ck); |
| return buffer.array(); |
| } |
| } |
| |
| EapAkaTypeData getEapSimAkaTypeData(AtClientErrorCode clientErrorCode) { |
| return new EapAkaTypeData(EAP_AKA_CLIENT_ERROR, Arrays.asList(clientErrorCode)); |
| } |
| |
| EapAkaTypeData getEapSimAkaTypeData(int eapSubtype, List<EapSimAkaAttribute> attributes) { |
| return new EapAkaTypeData(eapSubtype, attributes); |
| } |
| } |