1 /*
2  * Copyright (C) 2020 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.test.statemachine;
18 
19 import static android.net.eap.test.EapSessionConfig.EapMethodConfig.EAP_TYPE_TTLS;
20 
21 import static androidx.test.InstrumentationRegistry.getInstrumentation;
22 
23 import static com.android.internal.net.TestUtils.hexStringToByteArray;
24 import static com.android.internal.net.eap.test.crypto.TlsSession.TLS_STATUS_CLOSED;
25 import static com.android.internal.net.eap.test.message.EapData.EAP_NOTIFICATION;
26 import static com.android.internal.net.eap.test.message.EapMessage.EAP_CODE_FAILURE;
27 import static com.android.internal.net.eap.test.message.EapMessage.EAP_CODE_REQUEST;
28 import static com.android.internal.net.eap.test.message.EapMessage.EAP_CODE_SUCCESS;
29 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.EAP_RESPONSE_NOTIFICATION_PACKET;
30 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.EAP_RESPONSE_TTLS_WITH_LENGTH;
31 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.EAP_TTLS_DUMMY_DATA_BYTES;
32 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.EAP_TTLS_DUMMY_DATA_FINAL_FRAGMENT_BYTES;
33 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.EAP_TTLS_DUMMY_DATA_INITIAL_FRAGMENT_BYTES;
34 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.ID_INT;
35 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD;
36 import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME;
37 
38 import static org.junit.Assert.assertArrayEquals;
39 import static org.junit.Assert.assertEquals;
40 import static org.junit.Assert.assertTrue;
41 import static org.mockito.ArgumentMatchers.any;
42 import static org.mockito.ArgumentMatchers.eq;
43 import static org.mockito.Mockito.any;
44 import static org.mockito.Mockito.mock;
45 import static org.mockito.Mockito.verify;
46 import static org.mockito.Mockito.when;
47 
48 import android.content.Context;
49 import android.net.eap.test.EapSessionConfig;
50 import android.net.eap.test.EapSessionConfig.EapTtlsConfig;
51 
52 import com.android.internal.net.eap.test.EapResult;
53 import com.android.internal.net.eap.test.EapResult.EapError;
54 import com.android.internal.net.eap.test.EapResult.EapFailure;
55 import com.android.internal.net.eap.test.EapResult.EapResponse;
56 import com.android.internal.net.eap.test.crypto.TlsSession;
57 import com.android.internal.net.eap.test.crypto.TlsSession.TlsResult;
58 import com.android.internal.net.eap.test.crypto.TlsSessionFactory;
59 import com.android.internal.net.eap.test.exceptions.EapInvalidRequestException;
60 import com.android.internal.net.eap.test.message.EapData;
61 import com.android.internal.net.eap.test.message.EapMessage;
62 import com.android.internal.net.eap.test.message.ttls.EapTtlsInboundFragmentationHelper;
63 import com.android.internal.net.eap.test.message.ttls.EapTtlsOutboundFragmentationHelper;
64 import com.android.internal.net.eap.test.message.ttls.EapTtlsTypeData;
65 import com.android.internal.net.eap.test.message.ttls.EapTtlsTypeData.EapTtlsTypeDataDecoder;
66 import com.android.internal.net.eap.test.message.ttls.EapTtlsTypeData.EapTtlsTypeDataDecoder.DecodeResult;
67 import com.android.internal.net.eap.test.statemachine.EapMethodStateMachine.EapMethodState;
68 import com.android.internal.net.eap.test.statemachine.EapMethodStateMachine.FinalState;
69 import com.android.internal.net.eap.test.statemachine.EapTtlsMethodStateMachine.ErroredAndAwaitingClosureState;
70 
71 import org.junit.AfterClass;
72 import org.junit.Before;
73 import org.junit.Test;
74 
75 import java.security.SecureRandom;
76 
77 public class EapTtlsStateTest {
78 
79     static final String NOTIFICATION_MESSAGE = "test";
80     static final byte[] DUMMY_EAP_TYPE_DATA = hexStringToByteArray("112233445566");
81     static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
82 
83     static final int BUFFER_SIZE_FRAGMENT_ONE = EAP_TTLS_DUMMY_DATA_INITIAL_FRAGMENT_BYTES.length;
84     static final int BUFFER_SIZE_FRAGMENT_TWO = EAP_TTLS_DUMMY_DATA_FINAL_FRAGMENT_BYTES.length;
85     static final int BUFFER_SIZE_ASSEMBLED_FRAGMENTS =
86             BUFFER_SIZE_FRAGMENT_ONE + BUFFER_SIZE_FRAGMENT_TWO;
87 
88     Context mContext;
89     SecureRandom mMockSecureRandom;
90     EapTtlsTypeDataDecoder mMockTypeDataDecoder;
91     TlsSessionFactory mMockTlsSessionFactory;
92     TlsSession mMockTlsSession;
93 
94     EapTtlsConfig mEapTtlsConfig;
95     EapTtlsMethodStateMachine mStateMachine;
96     EapTtlsInboundFragmentationHelper mInboundFragmentationHelper;
97     EapTtlsOutboundFragmentationHelper mOutboundFragmentationHelper;
98 
99     @Before
setUp()100     public void setUp() throws Exception {
101         mContext = getInstrumentation().getContext();
102         mMockSecureRandom = mock(SecureRandom.class);
103         mMockTypeDataDecoder = mock(EapTtlsTypeDataDecoder.class);
104         mMockTlsSessionFactory = mock(TlsSessionFactory.class);
105         mMockTlsSession = mock(TlsSession.class);
106 
107         EapSessionConfig innerEapSessionConfig =
108                 new EapSessionConfig.Builder()
109                         .setEapMsChapV2Config(MSCHAP_V2_USERNAME, MSCHAP_V2_PASSWORD)
110                         .build();
111         EapSessionConfig eapSessionConfig =
112                 new EapSessionConfig.Builder()
113                         .setEapTtlsConfig(null /* trustedCa */, innerEapSessionConfig)
114                         .build();
115         mEapTtlsConfig = eapSessionConfig.getEapTtlsConfig();
116 
117         mInboundFragmentationHelper = new EapTtlsInboundFragmentationHelper();
118         mOutboundFragmentationHelper =
119                 new EapTtlsOutboundFragmentationHelper(BUFFER_SIZE_FRAGMENT_ONE);
120 
121         mStateMachine =
122                 new EapTtlsMethodStateMachine(
123                         mContext,
124                         mEapTtlsConfig,
125                         mMockSecureRandom,
126                         mMockTypeDataDecoder,
127                         mInboundFragmentationHelper,
128                         mOutboundFragmentationHelper);
129         EapTtlsMethodStateMachine.sTlsSessionFactory = mMockTlsSessionFactory;
130         when(mMockTlsSessionFactory.newInstance(any(), any())).thenReturn(mMockTlsSession);
131     }
132 
133     @AfterClass
teardown()134     public static void teardown() {
135         EapTtlsMethodStateMachine.sTlsSessionFactory = new TlsSessionFactory();
136     }
137 
138     @Test
testHandleEapFailureNotification()139     public void testHandleEapFailureNotification() throws Exception {
140         EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_FAILURE, ID_INT, null));
141         assertTrue(result instanceof EapFailure);
142         assertTrue(mStateMachine.getState() instanceof FinalState);
143     }
144 
145     @Test
testHandleEapSuccessNotification()146     public void testHandleEapSuccessNotification() throws Exception {
147         EapResult result = mStateMachine.process(new EapMessage(EAP_CODE_SUCCESS, ID_INT, null));
148         EapError eapError = (EapError) result;
149         assertTrue(eapError.cause instanceof EapInvalidRequestException);
150     }
151 
152     @Test
testHandleEapNotification()153     public void testHandleEapNotification() throws Exception {
154         EapData eapData = new EapData(EAP_NOTIFICATION, NOTIFICATION_MESSAGE.getBytes());
155         EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData);
156         EapMethodState preNotification = (EapMethodState) mStateMachine.getState();
157 
158         EapResult result = mStateMachine.process(eapMessage);
159         assertEquals(preNotification, mStateMachine.getState());
160 
161         EapResponse eapResponse = (EapResponse) result;
162         assertArrayEquals(EAP_RESPONSE_NOTIFICATION_PACKET, eapResponse.packet);
163     }
164 
getEapTtlsStartTypeData()165     EapTtlsTypeData getEapTtlsStartTypeData() throws Exception {
166         return getEapTtlsTypeData(
167                 false /* isFragmented */, true /* isStart */, 0 /* length */, EMPTY_BYTE_ARRAY);
168     }
169 
getEapTtlsFragmentTypeData(boolean isFragment, int length, byte[] data)170     EapTtlsTypeData getEapTtlsFragmentTypeData(boolean isFragment, int length, byte[] data)
171             throws Exception {
172         return getEapTtlsTypeData(isFragment, false /* isStart */, length, data);
173     }
174 
getEapTtlsTypeData(byte[] data)175     EapTtlsTypeData getEapTtlsTypeData(byte[] data) throws Exception {
176         return getEapTtlsTypeData(
177                 false /* isFragmented */, false /* isStart */, 0 /* length */, data);
178     }
179 
getEapTtlsTypeData( boolean isFragmented, boolean isStart, int length, byte[] data)180     EapTtlsTypeData getEapTtlsTypeData(
181             boolean isFragmented, boolean isStart, int length, byte[] data) throws Exception {
182         return EapTtlsTypeData.getEapTtlsTypeData(
183                 isFragmented, isStart, 0 /* version */, length, data);
184     }
185 
mockTypeDataDecoding(EapTtlsTypeData decodedTypeData)186     void mockTypeDataDecoding(EapTtlsTypeData decodedTypeData) throws Exception {
187         when(mMockTypeDataDecoder.decodeEapTtlsRequestPacket(eq(DUMMY_EAP_TYPE_DATA)))
188                 .thenReturn(new DecodeResult(decodedTypeData));
189     }
190 
191     /** Runs a test and verifies the EAP response returned by the state */
processMessageAndVerifyEapResponse(byte[] expectedResponse)192     void processMessageAndVerifyEapResponse(byte[] expectedResponse) throws Exception {
193         EapData eapData = new EapData(EAP_TYPE_TTLS, DUMMY_EAP_TYPE_DATA);
194         EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData);
195 
196         EapResult result = mStateMachine.process(eapMessage);
197         EapResponse eapResponse = (EapResponse) result;
198         assertArrayEquals(expectedResponse, eapResponse.packet);
199     }
200 
201     /**
202      * Completes a run of operations that requires CloseConnection to be called
203      *
204      * @param decodedTypeData the type data that is decoded by the type data decoder
205      */
processMessageAndVerifyConnectionClosed(EapTtlsTypeData decodedTypeData)206     void processMessageAndVerifyConnectionClosed(EapTtlsTypeData decodedTypeData) throws Exception {
207         mockTypeDataDecoding(decodedTypeData);
208         when(mMockTlsSession.closeConnection())
209                 .thenReturn(
210                         mMockTlsSession
211                         .new TlsResult(EAP_TTLS_DUMMY_DATA_BYTES, TLS_STATUS_CLOSED));
212 
213         processMessageAndVerifyEapResponse(EAP_RESPONSE_TTLS_WITH_LENGTH);
214         verify(mMockTypeDataDecoder).decodeEapTtlsRequestPacket(eq(DUMMY_EAP_TYPE_DATA));
215         verify(mMockTlsSession).closeConnection();
216         assertTrue(mStateMachine.getState() instanceof ErroredAndAwaitingClosureState);
217     }
218 
219     /**
220      * Runs a test and verifies the EAP error returned by the state
221      *
222      * @param error the exception within the EapError
223      */
processMessageAndVerifyEapError(Class<? extends Exception> error)224     void processMessageAndVerifyEapError(Class<? extends Exception> error) throws Exception {
225         EapData eapData = new EapData(EAP_TYPE_TTLS, DUMMY_EAP_TYPE_DATA);
226         EapMessage eapMessage = new EapMessage(EAP_CODE_REQUEST, ID_INT, eapData);
227 
228         EapResult result = mStateMachine.process(eapMessage);
229         EapError eapError = (EapError) result;
230         assertTrue(error.isInstance(eapError.cause));
231     }
232 }
233