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