aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/net/eap/EapAuthenticator.java
blob: a6869d7d0f293230eafa120057cde25f8787e4c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
 * 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;

import android.content.Context;
import android.net.eap.EapSessionConfig;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.eap.EapResult.EapError;
import com.android.internal.net.eap.EapResult.EapResponse;
import com.android.internal.net.eap.EapResult.EapSuccess;
import com.android.internal.net.eap.statemachine.EapStateMachine;
import com.android.internal.net.utils.Log;

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);
                                }
                            });
                });
    }
}