blob: 463096ce787e08b8f5dfb0acfdcd7433a0f76584 [file] [log] [blame]
/*
* 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.ike.eap;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import com.android.ike.eap.EapResult.EapError;
import com.android.ike.eap.EapResult.EapResponse;
import com.android.ike.eap.EapResult.EapSuccess;
import com.android.ike.eap.statemachine.EapStateMachine;
import com.android.ike.utils.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;
/**
* EapAuthenticator represents an EAP peer implementation.
*
* @see <a href="https://tools.ietf.org/html/rfc3748#section-4">RFC 3748, Extensible Authentication
* Protocol (EAP)</a>
*/
public class EapAuthenticator extends Handler {
private static final String EAP_TAG = "EAP";
private static final boolean LOG_SENSITIVE = false;
public static final Log LOG = new Log(EAP_TAG, LOG_SENSITIVE);
private static final String TAG = EapAuthenticator.class.getSimpleName();
private static final long DEFAULT_TIMEOUT_MILLIS = 7000L;
private final Executor mWorkerPool;
private final EapStateMachine mStateMachine;
private final IEapCallback mCb;
private final long mTimeoutMillis;
private boolean mCallbackFired = false;
/**
* Constructor for EapAuthenticator
*
* @param looper Looper for running a message loop
* @param cb IEapCallback for callbacks to the client
* @param context Context for this EapAuthenticator
* @param eapSessionConfig Configuration for an EapAuthenticator
*/
public EapAuthenticator(
Looper looper,
IEapCallback cb,
Context context,
EapSessionConfig eapSessionConfig) {
this(
looper,
cb,
new EapStateMachine(context, eapSessionConfig, new SecureRandom()),
Executors.newSingleThreadExecutor(),
DEFAULT_TIMEOUT_MILLIS);
}
@VisibleForTesting
EapAuthenticator(
Looper looper,
IEapCallback cb,
EapStateMachine eapStateMachine,
Executor executor,
long timeoutMillis) {
super(looper);
mCb = cb;
mStateMachine = eapStateMachine;
mWorkerPool = executor;
mTimeoutMillis = timeoutMillis;
}
@Override
public void handleMessage(Message msg) {
// No messages processed here. Only runnables. Drop all messages.
}
/**
* Processes the given msgBytes within the context of the current EAP Session.
*
* <p>If the given message is successfully processed, the relevant {@link IEapCallback} function
* is used. Otherwise, {@link IEapCallback#onError(Throwable)} is called.
*
* @param msgBytes the byte-array encoded EAP message to be processed
*/
public void processEapMessage(byte[] msgBytes) {
// reset
mCallbackFired = false;
postDelayed(
() -> {
if (!mCallbackFired) {
// Fire failed callback
mCallbackFired = true;
LOG.e(TAG, "Timeout occurred in EapStateMachine");
mCb.onError(new TimeoutException("Timeout while processing message"));
}
},
EapAuthenticator.this,
mTimeoutMillis);
// proxy to worker thread for async processing
mWorkerPool.execute(
() -> {
// Any unhandled exceptions within the state machine are caught here to make
// sure that the caller does not wait for the full timeout duration before being
// notified of a failure.
EapResult processResponse;
try {
processResponse = mStateMachine.process(msgBytes);
} catch (Exception ex) {
LOG.e(TAG, "Exception thrown while processing message", ex);
processResponse = new EapError(ex);
}
final EapResult finalProcessResponse = processResponse;
EapAuthenticator.this.post(
() -> {
// No synchronization needed, since Handler serializes
if (!mCallbackFired) {
LOG.i(
TAG,
"EapStateMachine returned "
+ finalProcessResponse
.getClass()
.getSimpleName());
if (finalProcessResponse instanceof EapResponse) {
mCb.onResponse(((EapResponse) finalProcessResponse).packet);
} else if (finalProcessResponse instanceof EapError) {
EapError eapError = (EapError) finalProcessResponse;
LOG.e(
TAG,
"EapError returned with cause=" + eapError.cause);
mCb.onError(eapError.cause);
} else if (finalProcessResponse instanceof EapSuccess) {
EapSuccess eapSuccess = (EapSuccess) finalProcessResponse;
LOG.d(
TAG,
"EapSuccess with"
+ " MSK=" + LOG.pii(eapSuccess.msk)
+ " EMSK=" + LOG.pii(eapSuccess.msk));
mCb.onSuccess(eapSuccess.msk, eapSuccess.emsk);
} else { // finalProcessResponse instanceof EapFailure
mCb.onFail();
}
mCallbackFired = true;
// Ensure delayed timeout runnable does not fire
EapAuthenticator.this.removeCallbacksAndMessages(
EapAuthenticator.this);
}
});
});
}
}