• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.ipsec.test.ike;
18 
19 import static android.net.ipsec.test.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
20 import static android.net.ipsec.test.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
21 import static android.net.ipsec.test.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD;
22 import static android.net.ipsec.test.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX;
23 import static android.net.ipsec.test.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS;
24 import static android.net.ipsec.test.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
25 import static android.net.ipsec.test.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
26 import static android.system.OsConstants.AF_INET;
27 
28 import static com.android.internal.net.TestUtils.createMockRandomFactory;
29 import static com.android.internal.net.ipsec.test.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_CHILD;
30 import static com.android.internal.net.ipsec.test.ike.AbstractSessionStateMachine.RETRY_INTERVAL_MS;
31 import static com.android.internal.net.ipsec.test.ike.ChildSessionStateMachine.CMD_FORCE_TRANSITION;
32 import static com.android.internal.net.ipsec.test.ike.IkeSessionStateMachine.REKEY_DELETE_TIMEOUT_MS;
33 import static com.android.internal.net.ipsec.test.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA;
34 import static com.android.internal.net.ipsec.test.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL;
35 import static com.android.internal.net.ipsec.test.ike.message.IkeMessage.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD;
36 import static com.android.internal.net.ipsec.test.ike.message.IkeMessage.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD;
37 import static com.android.internal.net.ipsec.test.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA;
38 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_CP;
39 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_DELETE;
40 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_KE;
41 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_NONCE;
42 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY;
43 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_SA;
44 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR;
45 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER;
46 import static com.android.internal.net.ipsec.test.ike.message.IkePayload.PROTOCOL_ID_ESP;
47 
48 import static org.junit.Assert.assertArrayEquals;
49 import static org.junit.Assert.assertEquals;
50 import static org.junit.Assert.assertFalse;
51 import static org.junit.Assert.assertNotNull;
52 import static org.junit.Assert.assertNull;
53 import static org.junit.Assert.assertTrue;
54 import static org.junit.Assert.fail;
55 import static org.mockito.ArgumentMatchers.any;
56 import static org.mockito.ArgumentMatchers.anyBoolean;
57 import static org.mockito.ArgumentMatchers.anyInt;
58 import static org.mockito.ArgumentMatchers.anyString;
59 import static org.mockito.ArgumentMatchers.argThat;
60 import static org.mockito.ArgumentMatchers.eq;
61 import static org.mockito.Mockito.atLeastOnce;
62 import static org.mockito.Mockito.doNothing;
63 import static org.mockito.Mockito.doReturn;
64 import static org.mockito.Mockito.inOrder;
65 import static org.mockito.Mockito.mock;
66 import static org.mockito.Mockito.never;
67 import static org.mockito.Mockito.reset;
68 import static org.mockito.Mockito.spy;
69 import static org.mockito.Mockito.times;
70 import static org.mockito.Mockito.verify;
71 import static org.mockito.Mockito.verifyNoMoreInteractions;
72 import static org.mockito.Mockito.when;
73 
74 import android.content.Context;
75 import android.content.pm.PackageManager;
76 import android.net.InetAddresses;
77 import android.net.IpSecManager;
78 import android.net.IpSecManager.UdpEncapsulationSocket;
79 import android.net.IpSecTransform;
80 import android.net.LinkAddress;
81 import android.net.ipsec.test.ike.ChildSaProposal;
82 import android.net.ipsec.test.ike.ChildSessionCallback;
83 import android.net.ipsec.test.ike.ChildSessionConfiguration;
84 import android.net.ipsec.test.ike.ChildSessionParams;
85 import android.net.ipsec.test.ike.IkeManager;
86 import android.net.ipsec.test.ike.IkeTrafficSelector;
87 import android.net.ipsec.test.ike.SaProposal;
88 import android.net.ipsec.test.ike.TunnelModeChildSessionParams;
89 import android.net.ipsec.test.ike.exceptions.IkeException;
90 import android.net.ipsec.test.ike.exceptions.IkeInternalException;
91 import android.net.ipsec.test.ike.exceptions.InvalidKeException;
92 import android.net.ipsec.test.ike.exceptions.InvalidSyntaxException;
93 import android.net.ipsec.test.ike.exceptions.NoAdditionalSasException;
94 import android.net.ipsec.test.ike.exceptions.NoValidProposalChosenException;
95 import android.os.Build;
96 import android.os.Handler;
97 import android.os.Message;
98 import android.os.test.TestLooper;
99 
100 import androidx.test.InstrumentationRegistry;
101 
102 import com.android.internal.net.TestUtils;
103 import com.android.internal.net.ipsec.test.ike.ChildSessionStateMachine.CreateChildSaHelper;
104 import com.android.internal.net.ipsec.test.ike.ChildSessionStateMachine.IChildSessionSmCallback;
105 import com.android.internal.net.ipsec.test.ike.ChildSessionStateMachine.IdleWithDeferredRequest;
106 import com.android.internal.net.ipsec.test.ike.SaRecord.ChildSaRecord;
107 import com.android.internal.net.ipsec.test.ike.SaRecord.ChildSaRecordConfig;
108 import com.android.internal.net.ipsec.test.ike.SaRecord.ISaRecordHelper;
109 import com.android.internal.net.ipsec.test.ike.SaRecord.SaLifetimeAlarmScheduler;
110 import com.android.internal.net.ipsec.test.ike.SaRecord.SaRecordHelper;
111 import com.android.internal.net.ipsec.test.ike.crypto.IkeCipher;
112 import com.android.internal.net.ipsec.test.ike.crypto.IkeMacIntegrity;
113 import com.android.internal.net.ipsec.test.ike.crypto.IkeMacPrf;
114 import com.android.internal.net.ipsec.test.ike.message.IkeConfigPayload;
115 import com.android.internal.net.ipsec.test.ike.message.IkeConfigPayload.ConfigAttribute;
116 import com.android.internal.net.ipsec.test.ike.message.IkeConfigPayload.ConfigAttributeIpv4Address;
117 import com.android.internal.net.ipsec.test.ike.message.IkeConfigPayload.ConfigAttributeIpv4Netmask;
118 import com.android.internal.net.ipsec.test.ike.message.IkeDeletePayload;
119 import com.android.internal.net.ipsec.test.ike.message.IkeKePayload;
120 import com.android.internal.net.ipsec.test.ike.message.IkeNoncePayload;
121 import com.android.internal.net.ipsec.test.ike.message.IkeNotifyPayload;
122 import com.android.internal.net.ipsec.test.ike.message.IkePayload;
123 import com.android.internal.net.ipsec.test.ike.message.IkeSaPayload;
124 import com.android.internal.net.ipsec.test.ike.message.IkeSaPayload.DhGroupTransform;
125 import com.android.internal.net.ipsec.test.ike.message.IkeSaPayload.EncryptionTransform;
126 import com.android.internal.net.ipsec.test.ike.message.IkeSaPayload.IntegrityTransform;
127 import com.android.internal.net.ipsec.test.ike.message.IkeSaPayload.PrfTransform;
128 import com.android.internal.net.ipsec.test.ike.message.IkeTestUtils;
129 import com.android.internal.net.ipsec.test.ike.message.IkeTsPayload;
130 import com.android.internal.net.ipsec.test.ike.testutils.MockIpSecTestUtils;
131 import com.android.internal.net.ipsec.test.ike.utils.IState;
132 import com.android.internal.net.ipsec.test.ike.utils.IkeMetrics;
133 import com.android.internal.net.ipsec.test.ike.utils.IpSecSpiGenerator;
134 import com.android.internal.net.ipsec.test.ike.utils.RandomnessFactory;
135 import com.android.internal.net.utils.test.Log;
136 import com.android.server.IpSecService;
137 import com.android.testutils.DevSdkIgnoreRule;
138 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
139 
140 import org.junit.After;
141 import org.junit.Before;
142 import org.junit.Ignore;
143 import org.junit.Rule;
144 import org.junit.Test;
145 import org.mockito.ArgumentCaptor;
146 import org.mockito.InOrder;
147 
148 import java.net.Inet4Address;
149 import java.net.Inet6Address;
150 import java.net.InetAddress;
151 import java.security.GeneralSecurityException;
152 import java.util.ArrayList;
153 import java.util.Arrays;
154 import java.util.List;
155 import java.util.concurrent.Executor;
156 
157 public final class ChildSessionStateMachineTest extends IkeSessionTestBase {
158     private static final String TAG = "ChildSessionStateMachineTest";
159 
160     @Rule public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
161 
162     private static final Inet4Address LOCAL_ADDRESS =
163             (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.200");
164     private static final Inet4Address UPDATED_LOCAL_ADDRESS =
165             (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.201");
166     private static final Inet4Address REMOTE_ADDRESS =
167             (Inet4Address) InetAddresses.parseNumericAddress("192.0.2.100");
168     private static final Inet4Address INTERNAL_ADDRESS =
169             (Inet4Address) InetAddresses.parseNumericAddress("203.0.113.100");
170     private static final Inet6Address LOCAL_ADDRESS_6 =
171             (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
172     private static final Inet6Address REMOTE_ADDRESS_6 =
173             (Inet6Address) InetAddresses.parseNumericAddress("2001:db8::2");
174 
175     private static final int IPV4_PREFIX_LEN = 32;
176 
177     private static final String IKE_AUTH_RESP_SA_PAYLOAD =
178             "2c00002c0000002801030403cae7019f0300000c0100000c800e0080"
179                     + "03000008030000020000000805000000";
180     private static final String REKEY_CHILD_RESP_SA_PAYLOAD =
181             "2800002c0000002801030403cd1736b30300000c0100000c800e0080"
182                     + "03000008030000020000000805000000";
183     private static final String REKEY_CHILD_REQ_SA_PAYLOAD =
184             "2800002c0000002801030403c88336490300000c0100000c800e0080"
185                     + "03000008030000020000000805000000";
186     private static final String REKEY_CHILD_UNACCEPTABLE_REQ_SA_PAYLOAD =
187             "2800002c0000002801030403c88336490300000c0100000c800e00c0"
188                     + "03000008030000020000000805000000";
189 
190     private static final int CURRENT_CHILD_SA_SPI_IN = 0x2ad4c0a2;
191     private static final int CURRENT_CHILD_SA_SPI_OUT = 0xcae7019f;
192 
193     private static final int LOCAL_INIT_NEW_CHILD_SA_SPI_IN = 0x57a09b0f;
194     private static final int LOCAL_INIT_NEW_CHILD_SA_SPI_OUT = 0xcd1736b3;
195 
196     private static final int REMOTE_INIT_NEW_CHILD_SA_SPI_IN = 0xd2d01795;
197     private static final int REMOTE_INIT_NEW_CHILD_SA_SPI_OUT = 0xc8833649;
198 
199     private static final String IKE_SK_D_HEX_STRING = "C86B56EFCF684DCC2877578AEF3137167FE0EBF6";
200     private static final byte[] SK_D = TestUtils.hexStringToByteArray(IKE_SK_D_HEX_STRING);
201 
202     private static final int KEY_LEN_IKE_SKD = 20;
203 
204     private static final int IKE_SESSION_UNIQUE_ID = 1;
205     private static final int IKE_DH_GROUP = SaProposal.DH_GROUP_4096_BIT_MODP;
206 
207     private IkeMacPrf mIkePrf;
208 
209     private Context mContext;
210     private Handler mMockIkeHandler;
211     private PackageManager mMockPackageManager;
212     private IpSecService mMockIpSecService;
213     private IpSecManager mMockIpSecManager;
214     private UdpEncapsulationSocket mMockUdpEncapSocket;
215 
216     private TestLooper mLooper;
217     private RandomnessFactory mMockRandomFactory;
218     private IpSecSpiGenerator mIpSecSpiGenerator;
219     private ChildSessionStateMachine mChildSessionStateMachine;
220 
221     private List<IkePayload> mFirstSaReqPayloads = new ArrayList<>();
222     private List<IkePayload> mFirstSaRespPayloads = new ArrayList<>();
223 
224     private ChildSaRecord mSpyCurrentChildSaRecord;
225     private ChildSaRecord mSpyLocalInitNewChildSaRecord;
226     private ChildSaRecord mSpyRemoteInitNewChildSaRecord;
227 
228     private Log mSpyIkeLog;
229 
230     private ISaRecordHelper mMockSaRecordHelper;
231 
232     private ChildSessionParams mChildSessionParams;
233     private EncryptionTransform mChildEncryptionTransform;
234     private IntegrityTransform mChildIntegrityTransform;
235     private DhGroupTransform mChildDhGroupTransform;
236 
237     private ChildSaProposal mMockNegotiatedProposal;
238 
239     private Executor mSpyUserCbExecutor;
240     private ChildSessionCallback mMockChildSessionCallback;
241     private IChildSessionSmCallback mMockChildSessionSmCallback;
242 
243     private ArgumentCaptor<ChildSaRecordConfig> mChildSaRecordConfigCaptor =
244             ArgumentCaptor.forClass(ChildSaRecordConfig.class);
245     private ArgumentCaptor<List<IkePayload>> mPayloadListCaptor =
246             ArgumentCaptor.forClass(List.class);
247     private ArgumentCaptor<ChildSessionConfiguration> mChildConfigCaptor =
248             ArgumentCaptor.forClass(ChildSessionConfiguration.class);
249 
ChildSessionStateMachineTest()250     public ChildSessionStateMachineTest() {
251         mMockSaRecordHelper = mock(SaRecord.ISaRecordHelper.class);
252         mMockChildSessionSmCallback = mock(IChildSessionSmCallback.class);
253 
254         mChildEncryptionTransform =
255                 new EncryptionTransform(
256                         SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128);
257         mChildIntegrityTransform =
258                 new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96);
259 
260         mChildDhGroupTransform = new DhGroupTransform(SaProposal.DH_GROUP_1024_BIT_MODP);
261     }
262 
263     @Before
setup()264     public void setup() throws Exception {
265         mSpyIkeLog = TestUtils.makeSpyLogThrowExceptionForWtf(TAG);
266         IkeManager.setIkeLog(mSpyIkeLog);
267 
268         mIkePrf = IkeMacPrf.create(new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1));
269 
270         mContext = spy(InstrumentationRegistry.getContext());
271         mMockIkeHandler = mock(Handler.class);
272         when(mMockIkeHandler.obtainMessage(anyInt(), anyInt(), anyInt(), any()))
273                 .thenReturn(mock(Message.class));
274 
275         mMockPackageManager = mock(PackageManager.class);
276         doReturn(mMockPackageManager).when(mContext).getPackageManager();
277 
278         mMockIpSecService = mock(IpSecService.class);
279         mMockIpSecManager = new IpSecManager(mContext, mMockIpSecService);
280         mMockUdpEncapSocket = mock(UdpEncapsulationSocket.class);
281 
282         mIpSecSpiGenerator = new IpSecSpiGenerator(mMockIpSecManager, createMockRandomFactory());
283 
284         mMockNegotiatedProposal = mock(ChildSaProposal.class);
285 
286         mSpyUserCbExecutor =
287                 spy(
288                         (command) -> {
289                             command.run();
290                         });
291 
292         mMockChildSessionCallback = mock(ChildSessionCallback.class);
293         mChildSessionParams = buildChildSessionParams();
294 
295         // Setup thread and looper
296         mLooper = new TestLooper();
297         mChildSessionStateMachine = buildChildSession(mChildSessionParams);
298         mChildSessionStateMachine.setDbg(true);
299         SaRecord.setSaRecordHelper(mMockSaRecordHelper);
300 
301         setUpFirstSaNegoPayloadLists();
302         setUpChildSaRecords();
303 
304         mChildSessionStateMachine.start();
305     }
306 
307     @After
tearDown()308     public void tearDown() {
309         mChildSessionStateMachine.setDbg(false);
310         IkeManager.resetIkeLog();
311         SaRecord.setSaRecordHelper(new SaRecordHelper());
312     }
313 
buildSaProposal()314     private ChildSaProposal buildSaProposal() throws Exception {
315         return new ChildSaProposal.Builder()
316                 .addEncryptionAlgorithm(
317                         SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
318                 .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
319                 .build();
320     }
321 
buildChildSessionParams()322     private ChildSessionParams buildChildSessionParams() throws Exception {
323         return new TunnelModeChildSessionParams.Builder()
324                 .addSaProposal(buildSaProposal())
325                 .addInternalAddressRequest(AF_INET)
326                 .addInternalAddressRequest(INTERNAL_ADDRESS)
327                 .build();
328     }
329 
setUpChildSaRecords()330     private void setUpChildSaRecords() {
331         mSpyCurrentChildSaRecord =
332                 makeSpyChildSaRecord(CURRENT_CHILD_SA_SPI_IN, CURRENT_CHILD_SA_SPI_OUT);
333         mSpyLocalInitNewChildSaRecord =
334                 makeSpyChildSaRecord(
335                         LOCAL_INIT_NEW_CHILD_SA_SPI_IN, LOCAL_INIT_NEW_CHILD_SA_SPI_OUT);
336         mSpyRemoteInitNewChildSaRecord =
337                 makeSpyChildSaRecord(
338                         REMOTE_INIT_NEW_CHILD_SA_SPI_IN, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT);
339     }
340 
setUpSpiResource(InetAddress address, int spiRequested)341     private void setUpSpiResource(InetAddress address, int spiRequested) throws Exception {
342         when(mMockIpSecService.allocateSecurityParameterIndex(
343                         eq(address.getHostAddress()), anyInt(), any()))
344                 .thenReturn(MockIpSecTestUtils.buildDummyIpSecSpiResponse(spiRequested));
345     }
346 
setUpFirstSaNegoPayloadLists()347     private void setUpFirstSaNegoPayloadLists() throws Exception {
348         // Build locally generated SA payload that has its SPI resource allocated.
349         setUpSpiResource(LOCAL_ADDRESS, CURRENT_CHILD_SA_SPI_IN);
350         IkeSaPayload reqSaPayload =
351                 IkeSaPayload.createChildSaRequestPayload(
352                         mChildSessionParams.getSaProposalsInternal(),
353                         mIpSecSpiGenerator,
354                         LOCAL_ADDRESS);
355         mFirstSaReqPayloads.add(reqSaPayload);
356 
357         // Build a remotely generated SA payload whoes SPI resource has not been allocated.
358         setUpSpiResource(REMOTE_ADDRESS, CURRENT_CHILD_SA_SPI_OUT);
359         IkeSaPayload respSaPayload =
360                 (IkeSaPayload)
361                         IkeTestUtils.hexStringToIkePayload(
362                                 IkePayload.PAYLOAD_TYPE_SA, true, IKE_AUTH_RESP_SA_PAYLOAD);
363         mFirstSaRespPayloads.add(respSaPayload);
364 
365         // Build TS Payloads
366         IkeTsPayload tsInitPayload =
367                 new IkeTsPayload(
368                         true /*isInitiator*/,
369                         mChildSessionParams.getInboundTrafficSelectorsInternal());
370         IkeTsPayload tsRespPayload =
371                 new IkeTsPayload(
372                         false /*isInitiator*/,
373                         mChildSessionParams.getOutboundTrafficSelectorsInternal());
374 
375         mFirstSaReqPayloads.add(tsInitPayload);
376         mFirstSaReqPayloads.add(tsRespPayload);
377         mFirstSaRespPayloads.add(tsInitPayload);
378         mFirstSaRespPayloads.add(tsRespPayload);
379 
380         // Build Nonce Payloads
381         mFirstSaReqPayloads.add(new IkeNoncePayload(createMockRandomFactory()));
382         mFirstSaRespPayloads.add(new IkeNoncePayload(createMockRandomFactory()));
383 
384         // Build Config Request Payload
385         List<ConfigAttribute> attrReqList = new ArrayList<>();
386         attrReqList.add(new ConfigAttributeIpv4Address(INTERNAL_ADDRESS));
387         attrReqList.add(new ConfigAttributeIpv4Netmask());
388         mFirstSaReqPayloads.add(new IkeConfigPayload(false /*isReply*/, attrReqList));
389 
390         // Build Config Reply Payload
391         List<ConfigAttribute> attrRespList = new ArrayList<>();
392         attrRespList.add(new ConfigAttributeIpv4Address(INTERNAL_ADDRESS));
393         mFirstSaRespPayloads.add(new IkeConfigPayload(true /*isReply*/, attrRespList));
394     }
395 
makeSpyChildSaRecord(int inboundSpi, int outboundSpi)396     private ChildSaRecord makeSpyChildSaRecord(int inboundSpi, int outboundSpi) {
397         ChildSaRecord child =
398                 spy(
399                         new ChildSaRecord(
400                                 inboundSpi,
401                                 outboundSpi,
402                                 true /*localInit*/,
403                                 null,
404                                 null,
405                                 null,
406                                 null,
407                                 null,
408                                 null,
409                                 mock(IpSecTransform.class),
410                                 mock(IpSecTransform.class),
411                                 mock(SaLifetimeAlarmScheduler.class)));
412         doNothing().when(child).close();
413         return child;
414     }
415 
quitAndVerify()416     private void quitAndVerify() {
417         mChildSessionStateMachine.mCurrentChildSaRecord = null;
418         mChildSessionStateMachine.mLocalInitNewChildSaRecord = null;
419         mChildSessionStateMachine.mRemoteInitNewChildSaRecord = null;
420 
421         reset(mMockChildSessionSmCallback);
422         mChildSessionStateMachine.quit();
423         mLooper.dispatchAll();
424 
425         verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
426         verify(mMockChildSessionSmCallback).onChildSessionClosed(mMockChildSessionCallback);
427     }
428 
verifyChildSaRecordConfig( ChildSaRecordConfig childSaRecordConfig, int initSpi, int respSpi, boolean isLocalInit)429     private void verifyChildSaRecordConfig(
430             ChildSaRecordConfig childSaRecordConfig,
431             int initSpi,
432             int respSpi,
433             boolean isLocalInit) {
434         verifyChildSaRecordConfig(
435                 childSaRecordConfig,
436                 initSpi,
437                 respSpi,
438                 isLocalInit,
439                 LOCAL_ADDRESS,
440                 REMOTE_ADDRESS,
441                 mMockUdpEncapSocket);
442     }
443 
verifyChildSaRecordConfig( ChildSaRecordConfig childSaRecordConfig, int initSpi, int respSpi, boolean isLocalInit, InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket newEncapSocket)444     private void verifyChildSaRecordConfig(
445             ChildSaRecordConfig childSaRecordConfig,
446             int initSpi,
447             int respSpi,
448             boolean isLocalInit,
449             InetAddress localAddress,
450             InetAddress remoteAddress,
451             UdpEncapsulationSocket newEncapSocket) {
452         assertEquals(mContext, childSaRecordConfig.context);
453         assertEquals(initSpi, childSaRecordConfig.initSpi.getSpi());
454         assertEquals(respSpi, childSaRecordConfig.respSpi.getSpi());
455 
456         if (isLocalInit) {
457             assertEquals(localAddress, childSaRecordConfig.initAddress);
458             assertEquals(remoteAddress, childSaRecordConfig.respAddress);
459         } else {
460             assertEquals(remoteAddress, childSaRecordConfig.initAddress);
461             assertEquals(localAddress, childSaRecordConfig.respAddress);
462         }
463 
464         assertEquals(newEncapSocket, childSaRecordConfig.udpEncapSocket);
465         assertEquals(mIkePrf, childSaRecordConfig.ikePrf);
466         assertArrayEquals(SK_D, childSaRecordConfig.skD);
467         assertFalse(childSaRecordConfig.isTransport);
468         assertEquals(isLocalInit, childSaRecordConfig.isLocalInit);
469         assertTrue(childSaRecordConfig.hasIntegrityAlgo);
470         assertNotNull(childSaRecordConfig.saLifetimeAlarmScheduler);
471     }
472 
verifyNotifyUsersCreateIpSecSa( ChildSaRecord childSaRecord, boolean expectInbound)473     private void verifyNotifyUsersCreateIpSecSa(
474             ChildSaRecord childSaRecord, boolean expectInbound) {
475         IpSecTransform transform =
476                 expectInbound
477                         ? childSaRecord.getInboundIpSecTransform()
478                         : childSaRecord.getOutboundIpSecTransform();
479         int direction = expectInbound ? IpSecManager.DIRECTION_IN : IpSecManager.DIRECTION_OUT;
480 
481         verify(mMockChildSessionCallback).onIpSecTransformCreated(eq(transform), eq(direction));
482     }
483 
verifyInitCreateChildResp( List<IkePayload> reqPayloads, List<IkePayload> respPayloads)484     private void verifyInitCreateChildResp(
485             List<IkePayload> reqPayloads, List<IkePayload> respPayloads) throws Exception {
486         verify(mMockChildSessionSmCallback)
487                 .onChildSaCreated(
488                         mSpyCurrentChildSaRecord.getRemoteSpi(), mChildSessionStateMachine);
489         verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
490         assertTrue(
491                 mChildSessionStateMachine.getCurrentState()
492                         instanceof ChildSessionStateMachine.Idle);
493 
494         // Validate negotiated SA proposal.
495         ChildSaProposal negotiatedProposal = mChildSessionStateMachine.mSaProposal;
496         assertNotNull(negotiatedProposal);
497         assertEquals(
498                 new EncryptionTransform[] {mChildEncryptionTransform},
499                 negotiatedProposal.getEncryptionTransforms());
500         assertEquals(
501                 new IntegrityTransform[] {mChildIntegrityTransform},
502                 negotiatedProposal.getIntegrityTransforms());
503 
504         // Validate current ChildSaRecord
505         verify(mMockSaRecordHelper)
506                 .makeChildSaRecord(
507                         eq(reqPayloads), eq(respPayloads), mChildSaRecordConfigCaptor.capture());
508         ChildSaRecordConfig childSaRecordConfig = mChildSaRecordConfigCaptor.getValue();
509 
510         verifyChildSaRecordConfig(
511                 childSaRecordConfig,
512                 CURRENT_CHILD_SA_SPI_IN,
513                 CURRENT_CHILD_SA_SPI_OUT,
514                 true /*isLocalInit*/);
515 
516         assertEquals(mSpyCurrentChildSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord);
517 
518         verify(mMockChildSessionSmCallback)
519                 .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine));
520         verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
521         assertTrue(
522                 mChildSessionStateMachine.getCurrentState()
523                         instanceof ChildSessionStateMachine.Idle);
524 
525         // Verify users have been notified
526         verify(mSpyUserCbExecutor).execute(any(Runnable.class));
527         verifyNotifyUsersCreateIpSecSa(mSpyCurrentChildSaRecord, true /*expectInbound*/);
528         verifyNotifyUsersCreateIpSecSa(mSpyCurrentChildSaRecord, false /*expectInbound*/);
529         verify(mMockChildSessionCallback).onOpened(mChildConfigCaptor.capture());
530 
531         // Verify Child Session Configuration
532         ChildSessionConfiguration sessionConfig = mChildConfigCaptor.getValue();
533         verifyTsList(
534                 Arrays.asList(mChildSessionParams.getInboundTrafficSelectorsInternal()),
535                 sessionConfig.getInboundTrafficSelectors());
536         verifyTsList(
537                 Arrays.asList(mChildSessionParams.getOutboundTrafficSelectorsInternal()),
538                 sessionConfig.getOutboundTrafficSelectors());
539 
540         List<LinkAddress> addrList = sessionConfig.getInternalAddresses();
541         assertEquals(1, addrList.size());
542         assertEquals(INTERNAL_ADDRESS, addrList.get(0).getAddress());
543         assertEquals(IPV4_PREFIX_LEN, addrList.get(0).getPrefixLength());
544         verifyIkeSaMetricsLogged(
545                 1,
546                 IkeMetrics.IKE_CALLER_UNKNOWN,
547                 IkeMetrics.IKE_SESSION_TYPE_CHILD,
548                 IkeMetrics.IKE_STATE_CHILD_CREATE_LOCAL_CREATE,
549                 SaProposal.DH_GROUP_NONE,
550                 IkeMetrics.ENCRYPTION_ALGORITHM_AES_CBC,
551                 IkeMetrics.KEY_LEN_AES_128,
552                 IkeMetrics.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
553                 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED,
554                 IkeMetrics.IKE_ERROR_NONE);
555     }
556 
verifyTsList( List<IkeTrafficSelector> expectedList, List<IkeTrafficSelector> tsList)557     private void verifyTsList(
558             List<IkeTrafficSelector> expectedList, List<IkeTrafficSelector> tsList) {
559         assertEquals(expectedList.size(), tsList.size());
560         for (int i = 0; i < expectedList.size(); i++) {
561             assertEquals(expectedList.get(i), tsList.get(i));
562         }
563     }
564 
565     @Ignore
disableTestCreateFirstChild()566     public void disableTestCreateFirstChild() throws Exception {
567         doReturn(mSpyCurrentChildSaRecord)
568                 .when(mMockSaRecordHelper)
569                 .makeChildSaRecord(any(), any(), any());
570 
571         mChildSessionStateMachine.handleFirstChildExchange(
572                 mFirstSaReqPayloads,
573                 mFirstSaRespPayloads,
574                 LOCAL_ADDRESS,
575                 REMOTE_ADDRESS,
576                 mMockUdpEncapSocket,
577                 mIkePrf,
578                 IKE_DH_GROUP,
579                 SK_D);
580         mLooper.dispatchAll();
581 
582         verifyInitCreateChildResp(mFirstSaReqPayloads, mFirstSaRespPayloads);
583 
584         quitAndVerify();
585     }
586 
validateCreateChild(boolean isFirstChild)587     private void validateCreateChild(boolean isFirstChild) {
588         assertEquals(mChildSessionStateMachine.mLocalAddress, LOCAL_ADDRESS);
589         assertEquals(mChildSessionStateMachine.mRemoteAddress, REMOTE_ADDRESS);
590         assertEquals(mChildSessionStateMachine.mUdpEncapSocket, mMockUdpEncapSocket);
591         assertEquals(mChildSessionStateMachine.mIkePrf, mIkePrf);
592         assertEquals(mChildSessionStateMachine.mIkeDhGroup, IKE_DH_GROUP);
593         assertEquals(mChildSessionStateMachine.mSkD, SK_D);
594         assertEquals(mChildSessionStateMachine.mIsFirstChild, isFirstChild);
595     }
596 
597     @Test
testHandleFirstChildExchange()598     public void testHandleFirstChildExchange() throws Exception {
599         mChildSessionStateMachine.handleFirstChildExchange(
600                 mFirstSaReqPayloads,
601                 mFirstSaRespPayloads,
602                 LOCAL_ADDRESS,
603                 REMOTE_ADDRESS,
604                 mMockUdpEncapSocket,
605                 mIkePrf,
606                 IKE_DH_GROUP,
607                 SK_D);
608         validateCreateChild(true /* isFirstChild */);
609     }
610 
verifyOutboundCreatePayloadTypes( List<IkePayload> outboundPayloads, boolean isRekey)611     private void verifyOutboundCreatePayloadTypes(
612             List<IkePayload> outboundPayloads, boolean isRekey) {
613         assertNotNull(
614                 IkePayload.getPayloadForTypeInProvidedList(
615                         PAYLOAD_TYPE_SA, IkeSaPayload.class, outboundPayloads));
616         assertNotNull(
617                 IkePayload.getPayloadForTypeInProvidedList(
618                         PAYLOAD_TYPE_TS_INITIATOR, IkeTsPayload.class, outboundPayloads));
619         assertNotNull(
620                 IkePayload.getPayloadForTypeInProvidedList(
621                         PAYLOAD_TYPE_TS_RESPONDER, IkeTsPayload.class, outboundPayloads));
622         assertNotNull(
623                 IkePayload.getPayloadForTypeInProvidedList(
624                         PAYLOAD_TYPE_NONCE, IkeNoncePayload.class, outboundPayloads));
625         assertNull(
626                 IkePayload.getPayloadForTypeInProvidedList(
627                         PAYLOAD_TYPE_KE, IkeKePayload.class, outboundPayloads));
628 
629         IkeConfigPayload configPayload =
630                 IkePayload.getPayloadForTypeInProvidedList(
631                         PAYLOAD_TYPE_CP, IkeConfigPayload.class, outboundPayloads);
632         if (isRekey) {
633             assertNull(configPayload);
634         } else {
635             assertNotNull(configPayload);
636             assertEquals(IkeConfigPayload.CONFIG_TYPE_REQUEST, configPayload.configType);
637         }
638     }
639 
checkCreateChildAndGetRequest()640     private List<IkePayload> checkCreateChildAndGetRequest() throws Exception {
641         doReturn(mSpyCurrentChildSaRecord)
642                 .when(mMockSaRecordHelper)
643                 .makeChildSaRecord(any(), any(), any());
644 
645         mChildSessionStateMachine.createChildSession(
646                 LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, IKE_DH_GROUP, SK_D);
647         mLooper.dispatchAll();
648 
649         // Validate outbound payload list
650         verify(mMockChildSessionSmCallback)
651                 .onOutboundPayloadsReady(
652                         eq(EXCHANGE_TYPE_CREATE_CHILD_SA),
653                         eq(false),
654                         mPayloadListCaptor.capture(),
655                         eq(mChildSessionStateMachine));
656 
657         List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue();
658         verifyOutboundCreatePayloadTypes(reqPayloadList, false /*isRekey*/);
659         assertTrue(
660                 IkePayload.getPayloadListForTypeInProvidedList(
661                                 PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, reqPayloadList)
662                         .isEmpty());
663 
664         mChildSessionStateMachine.receiveResponse(
665                 EXCHANGE_TYPE_CREATE_CHILD_SA, mFirstSaRespPayloads);
666         mLooper.dispatchAll();
667 
668         return reqPayloadList;
669     }
670 
671     @Test
testCreateChild()672     public void testCreateChild() throws Exception {
673         List<IkePayload> reqPayloadList = checkCreateChildAndGetRequest();
674         validateCreateChild(false /* isFirstChild */);
675 
676         verifyInitCreateChildResp(reqPayloadList, mFirstSaRespPayloads);
677         quitAndVerify();
678     }
679 
680     @Test
testCreateChildExecuteCbAfterKillSession()681     public void testCreateChildExecuteCbAfterKillSession() throws Exception {
682         mChildSessionStateMachine.quitNow();
683         mLooper.dispatchAll();
684 
685         LateExecuteExecutor lateExecutor = spy(new LateExecuteExecutor());
686         mChildSessionStateMachine = buildAndStartChildSession(lateExecutor);
687 
688         List<IkePayload> reqPayloadList = checkCreateChildAndGetRequest();
689 
690         mChildSessionStateMachine.killSession();
691         mLooper.dispatchAll();
692 
693         lateExecutor.actuallyExecute();
694 
695         // Verify users have been notified
696         verifyNotifyUsersCreateIpSecSa(mSpyCurrentChildSaRecord, true /*expectInbound*/);
697         verifyNotifyUsersCreateIpSecSa(mSpyCurrentChildSaRecord, false /*expectInbound*/);
698         verify(mMockChildSessionCallback).onOpened(any(ChildSessionConfiguration.class));
699     }
700 
verifyChildMetricsLogged(int stateCode, int exceptionCode)701     private void verifyChildMetricsLogged(int stateCode, int exceptionCode) {
702         verifyMetricsLogged(IkeMetrics.IKE_SESSION_TYPE_CHILD, stateCode, exceptionCode);
703     }
704 
verifyHandleFatalErrorAndQuit( IState state, Class<T> exceptionClass, int metricsExceptionType)705     private <T extends IkeException> void verifyHandleFatalErrorAndQuit(
706             IState state, Class<T> exceptionClass, int metricsExceptionType) {
707         assertNull(mChildSessionStateMachine.getCurrentState());
708         verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
709         verify(mMockChildSessionSmCallback).onChildSessionClosed(mMockChildSessionCallback);
710 
711         verify(mMockChildSessionCallback).onClosedWithException(any(exceptionClass));
712         verifyChildMetricsLogged(getStateCode(state), metricsExceptionType);
713     }
714 
createChildSessionAndReceiveErrorNotification(int notifyType)715     private void createChildSessionAndReceiveErrorNotification(int notifyType) throws Exception {
716         // Send out Create request
717         mChildSessionStateMachine.createChildSession(
718                 LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, IKE_DH_GROUP, SK_D);
719         mLooper.dispatchAll();
720 
721         // Receive error notification in Create response
722         IkeNotifyPayload notifyPayload = new IkeNotifyPayload(notifyType);
723         List<IkePayload> respPayloads = new ArrayList<>();
724         respPayloads.add(notifyPayload);
725         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads);
726         mLooper.dispatchAll();
727     }
728 
729     @Test
testCreateChildHandlesErrorNotifyResp()730     public void testCreateChildHandlesErrorNotifyResp() throws Exception {
731         createChildSessionAndReceiveErrorNotification(ERROR_TYPE_NO_PROPOSAL_CHOSEN);
732 
733         // Verify no SPI for provisional Child was registered.
734         verify(mMockChildSessionSmCallback, never())
735                 .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine));
736 
737         // Verify user was notified and state machine has quit.
738         verifyHandleFatalErrorAndQuit(
739                 mChildSessionStateMachine.mCreateChildLocalCreate,
740                 NoValidProposalChosenException.class,
741                 IkeMetrics.IKE_ERROR_PROTOCOL_NO_PROPOSAL_CHOSEN);
742     }
743 
744     @Test
testCreateChildHandlesTemporaryFailure()745     public void testCreateChildHandlesTemporaryFailure() throws Exception {
746         createChildSessionAndReceiveErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE);
747 
748         // Verify no SPI for provisional Child was registered.
749         verify(mMockChildSessionSmCallback, never())
750                 .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine));
751 
752         // Verify that Create Child re-enqueued
753         verify(mMockChildSessionSmCallback)
754                 .scheduleRetryLocalRequest(
755                         argThat(
756                                 childLocalRequest ->
757                                         childLocalRequest.procedureType
758                                                 == CMD_LOCAL_REQUEST_CREATE_CHILD));
759 
760         assertTrue(
761                 mChildSessionStateMachine.getCurrentState()
762                         instanceof ChildSessionStateMachine.Initial);
763     }
764 
765     @Test
testCreateChildHandlesRespWithMissingPayload()766     public void testCreateChildHandlesRespWithMissingPayload() throws Exception {
767         // Send out Create request
768         mChildSessionStateMachine.createChildSession(
769                 LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, IKE_DH_GROUP, SK_D);
770         mLooper.dispatchAll();
771 
772         // Receive response with no Nonce Payload
773         List<IkePayload> respPayloads = new ArrayList<>();
774         for (IkePayload payload : mFirstSaRespPayloads) {
775             if (IkePayload.PAYLOAD_TYPE_NONCE == payload.payloadType) continue;
776             respPayloads.add(payload);
777         }
778         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads);
779         mLooper.dispatchAll();
780 
781         // Verify SPI for provisional Child was registered and unregistered.
782         verify(mMockChildSessionSmCallback)
783                 .onChildSaCreated(CURRENT_CHILD_SA_SPI_OUT, mChildSessionStateMachine);
784         verify(mMockChildSessionSmCallback).onChildSaDeleted(CURRENT_CHILD_SA_SPI_OUT);
785 
786         // Verify user was notified and state machine has quit.
787         verifyHandleFatalErrorAndQuit(
788                 mChildSessionStateMachine.mCreateChildLocalCreate,
789                 InvalidSyntaxException.class,
790                 IkeMetrics.IKE_ERROR_PROTOCOL_INVALID_SYNTAX);
791     }
792 
793     @Test
testCreateChildHandlesKeyCalculationFail()794     public void testCreateChildHandlesKeyCalculationFail() throws Exception {
795         // Throw exception when building ChildSaRecord
796         when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any()))
797                 .thenThrow(
798                         new GeneralSecurityException("testCreateChildHandlesKeyCalculationFail"));
799 
800         // Send out and receive Create Child message
801         mChildSessionStateMachine.createChildSession(
802                 LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket, mIkePrf, IKE_DH_GROUP, SK_D);
803         mLooper.dispatchAll();
804         mChildSessionStateMachine.receiveResponse(
805                 EXCHANGE_TYPE_CREATE_CHILD_SA, mFirstSaRespPayloads);
806         mLooper.dispatchAll();
807 
808         // Verify SPI for provisional Child was registered and unregistered.
809         verify(mMockChildSessionSmCallback)
810                 .onChildSaCreated(CURRENT_CHILD_SA_SPI_OUT, mChildSessionStateMachine);
811         verify(mMockChildSessionSmCallback).onChildSaDeleted(CURRENT_CHILD_SA_SPI_OUT);
812 
813         // Verify user was notified and state machine has quit.
814         verifyHandleFatalErrorAndQuit(
815                 mChildSessionStateMachine.mCreateChildLocalCreate,
816                 IkeInternalException.class,
817                 IkeMetrics.IKE_ERROR_INTERNAL);
818     }
819 
setupIdleStateMachine()820     private void setupIdleStateMachine() throws Exception {
821         mChildSessionStateMachine.mLocalAddress = LOCAL_ADDRESS;
822         mChildSessionStateMachine.mRemoteAddress = REMOTE_ADDRESS;
823         mChildSessionStateMachine.mUdpEncapSocket = mMockUdpEncapSocket;
824         mChildSessionStateMachine.mIkePrf = mIkePrf;
825         mChildSessionStateMachine.mIkeDhGroup = IKE_DH_GROUP;
826         mChildSessionStateMachine.mSkD = SK_D;
827 
828         mChildSessionStateMachine.mSaProposal = buildSaProposal();
829         mChildSessionStateMachine.mChildCipher = mock(IkeCipher.class);
830         mChildSessionStateMachine.mChildIntegrity = mock(IkeMacIntegrity.class);
831         mChildSessionStateMachine.mLocalTs =
832                 mChildSessionParams.getInboundTrafficSelectorsInternal();
833         mChildSessionStateMachine.mRemoteTs =
834                 mChildSessionParams.getOutboundTrafficSelectorsInternal();
835 
836         mChildSessionStateMachine.mCurrentChildSaRecord = mSpyCurrentChildSaRecord;
837 
838         mChildSessionStateMachine.sendMessage(
839                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mIdle);
840         mLooper.dispatchAll();
841 
842         assertTrue(
843                 mChildSessionStateMachine.getCurrentState()
844                         instanceof ChildSessionStateMachine.Idle);
845     }
846 
makeDeletePayloads(int spi)847     private List<IkePayload> makeDeletePayloads(int spi) {
848         List<IkePayload> inboundPayloads = new ArrayList<>(1);
849         inboundPayloads.add(new IkeDeletePayload(new int[] {spi}));
850         return inboundPayloads;
851     }
852 
verifyOutboundDeletePayload(int expectedSpi, boolean isResp)853     private void verifyOutboundDeletePayload(int expectedSpi, boolean isResp) {
854         verify(mMockChildSessionSmCallback)
855                 .onOutboundPayloadsReady(
856                         eq(EXCHANGE_TYPE_INFORMATIONAL),
857                         eq(isResp),
858                         mPayloadListCaptor.capture(),
859                         eq(mChildSessionStateMachine));
860 
861         List<IkePayload> outPayloadList = mPayloadListCaptor.getValue();
862         assertEquals(1, outPayloadList.size());
863 
864         List<IkeDeletePayload> deletePayloads =
865                 IkePayload.getPayloadListForTypeInProvidedList(
866                         PAYLOAD_TYPE_DELETE, IkeDeletePayload.class, outPayloadList);
867         assertEquals(1, deletePayloads.size());
868         IkeDeletePayload deletePayload = deletePayloads.get(0);
869         assertEquals(expectedSpi, deletePayload.spisToDelete[0]);
870     }
871 
verifyNotifyUserDeleteChildSa(ChildSaRecord childSaRecord)872     private void verifyNotifyUserDeleteChildSa(ChildSaRecord childSaRecord) {
873         verify(mMockChildSessionCallback)
874                 .onIpSecTransformDeleted(
875                         eq(childSaRecord.getInboundIpSecTransform()),
876                         eq(IpSecManager.DIRECTION_IN));
877         verify(mMockChildSessionCallback)
878                 .onIpSecTransformDeleted(
879                         eq(childSaRecord.getOutboundIpSecTransform()),
880                         eq(IpSecManager.DIRECTION_OUT));
881     }
882 
verifyNotifyUsersDeleteSession(IState state)883     private void verifyNotifyUsersDeleteSession(IState state) {
884         verifyNotifyUsersDeleteSession(mSpyUserCbExecutor, state);
885     }
886 
verifyNotifyUsersDeleteSession(Executor spyExecutor, IState state)887     private void verifyNotifyUsersDeleteSession(Executor spyExecutor, IState state) {
888         verifyNotifyUsersDeleteSession(spyExecutor, state, null, IkeMetrics.IKE_ERROR_NONE);
889     }
890 
verifyNotifyUsersDeleteSession( Executor spyExecutor, IState state, Class<? extends IkeException> exceptionClass, int metricsExceptionType)891     private void verifyNotifyUsersDeleteSession(
892             Executor spyExecutor,
893             IState state,
894             Class<? extends IkeException> exceptionClass,
895             int metricsExceptionType) {
896         verify(spyExecutor, atLeastOnce()).execute(any(Runnable.class));
897         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
898 
899         InOrder orderVerifier = inOrder(mMockChildSessionCallback);
900         orderVerifier
901                 .verify(mMockChildSessionCallback, times(2))
902                 .onIpSecTransformDeleted(any(), anyInt());
903 
904         if (exceptionClass == null) {
905             orderVerifier.verify(mMockChildSessionCallback).onClosed();
906         } else {
907             orderVerifier
908                     .verify(mMockChildSessionCallback)
909                     .onClosedWithException(any(exceptionClass));
910         }
911         verifyChildMetricsLogged(getStateCode(state), metricsExceptionType);
912     }
913 
914     @Test
testDeleteChildLocal()915     public void testDeleteChildLocal() throws Exception {
916         setupIdleStateMachine();
917 
918         // Test initiating Delete request
919         mChildSessionStateMachine.deleteChildSession();
920         mLooper.dispatchAll();
921 
922         assertTrue(
923                 mChildSessionStateMachine.getCurrentState()
924                         instanceof ChildSessionStateMachine.DeleteChildLocalDelete);
925         verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), false /*isResp*/);
926 
927         // Test receiving Delete response
928         mChildSessionStateMachine.receiveResponse(
929                 EXCHANGE_TYPE_INFORMATIONAL,
930                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
931         mLooper.dispatchAll();
932 
933         assertNull(mChildSessionStateMachine.getCurrentState());
934 
935         verifyNotifyUsersDeleteSession(mChildSessionStateMachine.mDeleteChildLocalDelete);
936     }
937 
938     @Test
testDeleteChildLocalExecuteCbAfterKillSession()939     public void testDeleteChildLocalExecuteCbAfterKillSession() throws Exception {
940         mChildSessionStateMachine.quitNow();
941         mLooper.dispatchAll();
942 
943         LateExecuteExecutor lateExecutor = spy(new LateExecuteExecutor());
944         mChildSessionStateMachine = buildAndStartChildSession(lateExecutor);
945 
946         setupIdleStateMachine();
947 
948         mChildSessionStateMachine.deleteChildSession();
949         mChildSessionStateMachine.receiveResponse(
950                 EXCHANGE_TYPE_INFORMATIONAL,
951                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
952         mLooper.dispatchAll();
953 
954         assertNull(mChildSessionStateMachine.getCurrentState());
955 
956         lateExecutor.actuallyExecute();
957         verifyNotifyUsersDeleteSession(
958                 lateExecutor, mChildSessionStateMachine.mDeleteChildLocalDelete);
959     }
960 
961     @Test
testDeleteChildLocalHandlesInvalidResp()962     public void testDeleteChildLocalHandlesInvalidResp() throws Exception {
963         setupIdleStateMachine();
964 
965         // Test initiating Delete request
966         mChildSessionStateMachine.deleteChildSession();
967         mLooper.dispatchAll();
968 
969         // Test receiving response with no Delete Payload
970         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_INFORMATIONAL, new ArrayList<>());
971         mLooper.dispatchAll();
972 
973         assertNull(mChildSessionStateMachine.getCurrentState());
974         verify(mMockChildSessionCallback).onClosedWithException(any(InvalidSyntaxException.class));
975         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
976     }
977 
978     @Test
testDeleteChildLocalInInitial()979     public void testDeleteChildLocalInInitial() throws Exception {
980         mChildSessionStateMachine.deleteChildSession();
981         mLooper.dispatchAll();
982 
983         assertNull(mChildSessionStateMachine.getCurrentState());
984         verify(mSpyUserCbExecutor).execute(any(Runnable.class));
985         verify(mMockChildSessionCallback).onClosed();
986     }
987 
988     @Test
testSimultaneousDeleteChild()989     public void testSimultaneousDeleteChild() throws Exception {
990         setupIdleStateMachine();
991 
992         mChildSessionStateMachine.deleteChildSession();
993         mChildSessionStateMachine.receiveRequest(
994                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
995                 EXCHANGE_TYPE_INFORMATIONAL,
996                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
997         mLooper.dispatchAll();
998 
999         verify(mMockChildSessionSmCallback)
1000                 .onOutboundPayloadsReady(
1001                         eq(EXCHANGE_TYPE_INFORMATIONAL),
1002                         eq(true),
1003                         mPayloadListCaptor.capture(),
1004                         eq(mChildSessionStateMachine));
1005         List<IkePayload> respPayloadList = mPayloadListCaptor.getValue();
1006         assertTrue(respPayloadList.isEmpty());
1007 
1008         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_INFORMATIONAL, new ArrayList<>());
1009         mLooper.dispatchAll();
1010 
1011         assertNull(mChildSessionStateMachine.getCurrentState());
1012 
1013         verifyNotifyUsersDeleteSession(mChildSessionStateMachine.mDeleteChildLocalDelete);
1014     }
1015 
1016     @Test
testReplyRekeyRequestDuringDeletion()1017     public void testReplyRekeyRequestDuringDeletion() throws Exception {
1018         setupIdleStateMachine();
1019 
1020         mChildSessionStateMachine.deleteChildSession();
1021         mChildSessionStateMachine.receiveRequest(
1022                 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, mock(List.class));
1023         mLooper.dispatchAll();
1024 
1025         // Verify outbound response to Rekey Child request
1026         verify(mMockChildSessionSmCallback)
1027                 .onOutboundPayloadsReady(
1028                         eq(EXCHANGE_TYPE_INFORMATIONAL),
1029                         eq(true),
1030                         mPayloadListCaptor.capture(),
1031                         eq(mChildSessionStateMachine));
1032         List<IkePayload> respPayloadList = mPayloadListCaptor.getValue();
1033         assertEquals(1, respPayloadList.size());
1034 
1035         IkeNotifyPayload notifyPayload = (IkeNotifyPayload) respPayloadList.get(0);
1036         assertEquals(ERROR_TYPE_TEMPORARY_FAILURE, notifyPayload.notifyType);
1037         assertEquals(0, notifyPayload.notifyData.length);
1038 
1039         assertTrue(
1040                 mChildSessionStateMachine.getCurrentState()
1041                         instanceof ChildSessionStateMachine.DeleteChildLocalDelete);
1042     }
1043 
1044     @Test
testDeleteChildRemote()1045     public void testDeleteChildRemote() throws Exception {
1046         setupIdleStateMachine();
1047 
1048         mChildSessionStateMachine.receiveRequest(
1049                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
1050                 EXCHANGE_TYPE_INFORMATIONAL,
1051                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
1052         mLooper.dispatchAll();
1053 
1054         assertNull(mChildSessionStateMachine.getCurrentState());
1055         // Verify response
1056         verify(mMockChildSessionSmCallback)
1057                 .onOutboundPayloadsReady(
1058                         eq(EXCHANGE_TYPE_INFORMATIONAL),
1059                         eq(true),
1060                         mPayloadListCaptor.capture(),
1061                         eq(mChildSessionStateMachine));
1062         List<IkePayload> respPayloadList = mPayloadListCaptor.getValue();
1063 
1064         assertEquals(1, respPayloadList.size());
1065         assertArrayEquals(
1066                 new int[] {mSpyCurrentChildSaRecord.getLocalSpi()},
1067                 ((IkeDeletePayload) respPayloadList.get(0)).spisToDelete);
1068 
1069         verifyNotifyUsersDeleteSession(mChildSessionStateMachine.mDeleteChildRemoteDelete);
1070     }
1071 
verifyOutboundRekeySaPayload(List<IkePayload> outboundPayloads, boolean isResp)1072     private void verifyOutboundRekeySaPayload(List<IkePayload> outboundPayloads, boolean isResp) {
1073         IkeSaPayload saPayload =
1074                 IkePayload.getPayloadForTypeInProvidedList(
1075                         PAYLOAD_TYPE_SA, IkeSaPayload.class, outboundPayloads);
1076         assertEquals(isResp, saPayload.isSaResponse);
1077         assertEquals(1, saPayload.proposalList.size());
1078 
1079         IkeSaPayload.ChildProposal proposal =
1080                 (IkeSaPayload.ChildProposal) saPayload.proposalList.get(0);
1081         assertEquals(1, proposal.number); // Must be 1-indexed
1082         assertEquals(mChildSessionStateMachine.mSaProposal, proposal.saProposal);
1083     }
1084 
verifyOutboundRekeyNotifyPayload(List<IkePayload> outboundPayloads)1085     private void verifyOutboundRekeyNotifyPayload(List<IkePayload> outboundPayloads) {
1086         List<IkeNotifyPayload> notifyPayloads =
1087                 IkePayload.getPayloadListForTypeInProvidedList(
1088                         PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, outboundPayloads);
1089         assertEquals(1, notifyPayloads.size());
1090         IkeNotifyPayload notifyPayload = notifyPayloads.get(0);
1091         assertEquals(NOTIFY_TYPE_REKEY_SA, notifyPayload.notifyType);
1092         assertEquals(PROTOCOL_ID_ESP, notifyPayload.protocolId);
1093         assertEquals(mSpyCurrentChildSaRecord.getLocalSpi(), notifyPayload.spi);
1094     }
1095 
1096     @Test
testRekeyChildLocalCreateSendsRequest()1097     public void testRekeyChildLocalCreateSendsRequest() throws Exception {
1098         setupIdleStateMachine();
1099 
1100         // Send Rekey-Create request
1101         mChildSessionStateMachine.rekeyChildSession();
1102         mLooper.dispatchAll();
1103         assertTrue(
1104                 mChildSessionStateMachine.getCurrentState()
1105                         instanceof ChildSessionStateMachine.RekeyChildLocalCreate);
1106         verify(mMockChildSessionSmCallback)
1107                 .onOutboundPayloadsReady(
1108                         eq(EXCHANGE_TYPE_CREATE_CHILD_SA),
1109                         eq(false),
1110                         mPayloadListCaptor.capture(),
1111                         eq(mChildSessionStateMachine));
1112 
1113         // Verify outbound payload list
1114         List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue();
1115         verifyOutboundCreatePayloadTypes(reqPayloadList, true /*isRekey*/);
1116 
1117         verifyOutboundRekeySaPayload(reqPayloadList, false /*isResp*/);
1118         verifyOutboundRekeyNotifyPayload(reqPayloadList);
1119     }
1120 
makeInboundRekeyChildPayloads( int remoteSpi, String inboundSaHexString, boolean isLocalInitRekey)1121     private List<IkePayload> makeInboundRekeyChildPayloads(
1122             int remoteSpi, String inboundSaHexString, boolean isLocalInitRekey) throws Exception {
1123         IkeSaPayload saPayload =
1124                 (IkeSaPayload)
1125                         IkeTestUtils.hexStringToIkePayload(
1126                                 IkePayload.PAYLOAD_TYPE_SA, true, inboundSaHexString);
1127 
1128         return makeInboundRekeyChildPayloads(remoteSpi, saPayload, isLocalInitRekey);
1129     }
1130 
makeInboundRekeyChildPayloads( int remoteSpi, IkeSaPayload saPayload, boolean isLocalInitRekey)1131     private List<IkePayload> makeInboundRekeyChildPayloads(
1132             int remoteSpi, IkeSaPayload saPayload, boolean isLocalInitRekey) throws Exception {
1133         List<IkePayload> inboundPayloads = new ArrayList<>();
1134 
1135         inboundPayloads.add(saPayload);
1136 
1137         // Build TS Payloads
1138         IkeTrafficSelector[] initTs =
1139                 isLocalInitRekey
1140                         ? mChildSessionStateMachine.mLocalTs
1141                         : mChildSessionStateMachine.mRemoteTs;
1142         IkeTrafficSelector[] respTs =
1143                 isLocalInitRekey
1144                         ? mChildSessionStateMachine.mRemoteTs
1145                         : mChildSessionStateMachine.mLocalTs;
1146         inboundPayloads.add(new IkeTsPayload(true /*isInitiator*/, initTs));
1147         inboundPayloads.add(new IkeTsPayload(false /*isInitiator*/, respTs));
1148 
1149         // Build Nonce Payloads
1150         inboundPayloads.add(new IkeNoncePayload(createMockRandomFactory()));
1151 
1152         if (isLocalInitRekey) {
1153             // Rekey-Create response without Notify-Rekey payload is valid.
1154             return inboundPayloads;
1155         }
1156 
1157         // Build Rekey-Notification
1158         inboundPayloads.add(
1159                 new IkeNotifyPayload(
1160                         PROTOCOL_ID_ESP,
1161                         mSpyCurrentChildSaRecord.getRemoteSpi(),
1162                         NOTIFY_TYPE_REKEY_SA,
1163                         new byte[0]));
1164 
1165         return inboundPayloads;
1166     }
1167 
receiveRekeyChildRequest()1168     private List<IkePayload> receiveRekeyChildRequest() throws Exception {
1169         List<IkePayload> rekeyReqPayloads =
1170                 makeInboundRekeyChildPayloads(
1171                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT,
1172                         REKEY_CHILD_REQ_SA_PAYLOAD,
1173                         false /*isLocalInitRekey*/);
1174         when(mMockSaRecordHelper.makeChildSaRecord(
1175                         eq(rekeyReqPayloads), any(List.class), any(ChildSaRecordConfig.class)))
1176                 .thenReturn(mSpyRemoteInitNewChildSaRecord);
1177 
1178         // Receive rekey Child request
1179         mChildSessionStateMachine.receiveRequest(
1180                 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
1181         mLooper.dispatchAll();
1182 
1183         return rekeyReqPayloads;
1184     }
1185 
receiveRekeyChildResponse()1186     private List<IkePayload> receiveRekeyChildResponse() throws Exception {
1187         List<IkePayload> rekeyRespPayloads =
1188                 makeInboundRekeyChildPayloads(
1189                         LOCAL_INIT_NEW_CHILD_SA_SPI_OUT,
1190                         REKEY_CHILD_RESP_SA_PAYLOAD,
1191                         true /*isLocalInitRekey*/);
1192         when(mMockSaRecordHelper.makeChildSaRecord(
1193                         any(List.class), eq(rekeyRespPayloads), any(ChildSaRecordConfig.class)))
1194                 .thenReturn(mSpyLocalInitNewChildSaRecord);
1195 
1196         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyRespPayloads);
1197         mLooper.dispatchAll();
1198 
1199         return rekeyRespPayloads;
1200     }
1201 
setupStateMachineAndSpiForLocalRekey()1202     private void setupStateMachineAndSpiForLocalRekey() throws Exception {
1203         setupStateMachineAndSpiForLocalRekey(LOCAL_ADDRESS, REMOTE_ADDRESS);
1204     }
1205 
setupStateMachineAndSpiForLocalRekey( InetAddress updatedLocalAddress, InetAddress updatedRemoteAddress)1206     private void setupStateMachineAndSpiForLocalRekey(
1207             InetAddress updatedLocalAddress, InetAddress updatedRemoteAddress) throws Exception {
1208         setupIdleStateMachine();
1209         setUpSpiResource(updatedLocalAddress, LOCAL_INIT_NEW_CHILD_SA_SPI_IN);
1210         setUpSpiResource(updatedRemoteAddress, LOCAL_INIT_NEW_CHILD_SA_SPI_OUT);
1211     }
1212 
1213     @Test
testRekeyChildLocalCreateValidatesResponse()1214     public void testRekeyChildLocalCreateValidatesResponse() throws Exception {
1215         setupStateMachineAndSpiForLocalRekey();
1216 
1217         // Send Rekey-Create request
1218         mChildSessionStateMachine.rekeyChildSession();
1219         mLooper.dispatchAll();
1220 
1221         verifyRekeyChildLocalCreateHandlesResponse(
1222                 ChildSessionStateMachine.RekeyChildLocalCreate.class,
1223                 false /* isMobikeRekey */,
1224                 LOCAL_ADDRESS,
1225                 REMOTE_ADDRESS);
1226     }
1227 
verifyRekeyChildLocalCreateHandlesResponse( Class<?> expectedState, boolean isMobikeRekey, InetAddress localAddress, InetAddress remoteAddress)1228     private void verifyRekeyChildLocalCreateHandlesResponse(
1229             Class<?> expectedState,
1230             boolean isMobikeRekey,
1231             InetAddress localAddress,
1232             InetAddress remoteAddress)
1233             throws Exception {
1234         verifyRekeyChildLocalCreateHandlesResponse(
1235                 expectedState, isMobikeRekey, localAddress, remoteAddress, mMockUdpEncapSocket);
1236     }
1237 
verifyRekeyChildLocalCreateHandlesResponse( Class<?> expectedState, boolean isMobikeRekey, InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket newEncapSocket)1238     private void verifyRekeyChildLocalCreateHandlesResponse(
1239             Class<?> expectedState,
1240             boolean isMobikeRekey,
1241             InetAddress localAddress,
1242             InetAddress remoteAddress,
1243             UdpEncapsulationSocket newEncapSocket)
1244             throws Exception {
1245         assertTrue(expectedState.isInstance(mChildSessionStateMachine.getCurrentState()));
1246 
1247         List<IkePayload> rekeyRespPayloads = receiveRekeyChildResponse();
1248         verifyLocalRekeyCreateIsDone(
1249                 rekeyRespPayloads, isMobikeRekey, localAddress, remoteAddress, newEncapSocket);
1250     }
1251 
verifyLocalRekeyCreateIsDone( List<IkePayload> rekeyRespPayloads, boolean isMobikeRekey, InetAddress localAddress, InetAddress remoteAddress)1252     private void verifyLocalRekeyCreateIsDone(
1253             List<IkePayload> rekeyRespPayloads,
1254             boolean isMobikeRekey,
1255             InetAddress localAddress,
1256             InetAddress remoteAddress)
1257             throws Exception {
1258         verifyLocalRekeyCreateIsDone(
1259                 rekeyRespPayloads, isMobikeRekey, localAddress, remoteAddress, mMockUdpEncapSocket);
1260     }
1261 
verifyLocalRekeyCreateIsDone( List<IkePayload> rekeyRespPayloads, boolean isMobikeRekey, InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket newEncapSocket)1262     private void verifyLocalRekeyCreateIsDone(
1263             List<IkePayload> rekeyRespPayloads,
1264             boolean isMobikeRekey,
1265             InetAddress localAddress,
1266             InetAddress remoteAddress,
1267             UdpEncapsulationSocket newEncapSocket)
1268             throws Exception {
1269         // Verify state transition
1270         assertTrue(
1271                 mChildSessionStateMachine.getCurrentState()
1272                         instanceof ChildSessionStateMachine.RekeyChildLocalDelete);
1273 
1274         // Verify newly created ChildSaRecord
1275         assertEquals(
1276                 mSpyLocalInitNewChildSaRecord,
1277                 mChildSessionStateMachine.mLocalInitNewChildSaRecord);
1278         verify(mMockChildSessionSmCallback)
1279                 .onChildSaCreated(
1280                         eq(mSpyLocalInitNewChildSaRecord.getRemoteSpi()),
1281                         eq(mChildSessionStateMachine));
1282 
1283         verify(mMockSaRecordHelper)
1284                 .makeChildSaRecord(
1285                         any(List.class),
1286                         eq(rekeyRespPayloads),
1287                         mChildSaRecordConfigCaptor.capture());
1288         ChildSaRecordConfig childSaRecordConfig = mChildSaRecordConfigCaptor.getValue();
1289         verifyChildSaRecordConfig(
1290                 childSaRecordConfig,
1291                 LOCAL_INIT_NEW_CHILD_SA_SPI_IN,
1292                 LOCAL_INIT_NEW_CHILD_SA_SPI_OUT,
1293                 true /*isLocalInit*/,
1294                 localAddress,
1295                 remoteAddress,
1296                 newEncapSocket);
1297 
1298         // Verify users have been notified
1299         verify(mSpyUserCbExecutor).execute(any(Runnable.class));
1300 
1301         if (isMobikeRekey) {
1302             verify(mMockChildSessionCallback)
1303                     .onIpSecTransformsMigrated(
1304                             mSpyLocalInitNewChildSaRecord.getInboundIpSecTransform(),
1305                             mSpyLocalInitNewChildSaRecord.getOutboundIpSecTransform());
1306         } else {
1307             verifyNotifyUsersCreateIpSecSa(mSpyLocalInitNewChildSaRecord, true /*expectInbound*/);
1308             verifyNotifyUsersCreateIpSecSa(mSpyLocalInitNewChildSaRecord, false /*expectInbound*/);
1309         }
1310     }
1311 
1312     @Test
testRekeyLocalCreateHandlesErrorNotifyResp()1313     public void testRekeyLocalCreateHandlesErrorNotifyResp() throws Exception {
1314         setupIdleStateMachine();
1315         setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN);
1316 
1317         // Send Rekey-Create request
1318         mChildSessionStateMachine.rekeyChildSession();
1319         mLooper.dispatchAll();
1320 
1321         // Receive error notification in Create response
1322         IkeNotifyPayload notifyPayload = new IkeNotifyPayload(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE);
1323         List<IkePayload> respPayloads = new ArrayList<>();
1324         respPayloads.add(notifyPayload);
1325         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads);
1326         mLooper.dispatchAll();
1327 
1328         // Verify rekey has been rescheduled and Child Session is alive
1329         verify(mSpyCurrentChildSaRecord).rescheduleRekey(eq(RETRY_INTERVAL_MS));
1330         assertTrue(
1331                 mChildSessionStateMachine.getCurrentState()
1332                         instanceof ChildSessionStateMachine.Idle);
1333 
1334         // Verify no SPI for provisional Child was registered.
1335         verify(mMockChildSessionSmCallback, never())
1336                 .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine));
1337     }
1338 
verifySendOutboundIkeDeleteRequest()1339     private void verifySendOutboundIkeDeleteRequest() {
1340         verify(mMockChildSessionSmCallback)
1341                 .onOutboundPayloadsReady(
1342                         eq(EXCHANGE_TYPE_INFORMATIONAL),
1343                         eq(false),
1344                         mPayloadListCaptor.capture(),
1345                         eq(mChildSessionStateMachine));
1346 
1347         List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue();
1348 
1349         List<IkeDeletePayload> deletePayloads =
1350                 IkePayload.getPayloadListForTypeInProvidedList(
1351                         PAYLOAD_TYPE_DELETE, IkeDeletePayload.class, reqPayloadList);
1352         assertEquals(deletePayloads.size(), 1);
1353 
1354         IkeDeletePayload deletePayload = deletePayloads.get(0);
1355         assertEquals(deletePayload.protocolId, IkePayload.PROTOCOL_ID_IKE);
1356         assertEquals(deletePayload.spisToDelete.length, 0);
1357     }
1358 
verifyIkeSessionFatalErrorAndSendOutboundIkeDeletePayload( Class<T> exceptionClass, IState expectedState, int expectedErrorCode)1359     private <T extends IkeException> void verifyIkeSessionFatalErrorAndSendOutboundIkeDeletePayload(
1360             Class<T> exceptionClass, IState expectedState, int expectedErrorCode) {
1361         verifySendOutboundIkeDeleteRequest();
1362 
1363         // Verify callback onFatalIkeSessionError() has been invoked
1364         verify(mMockChildSessionSmCallback).onFatalIkeSessionError(any(exceptionClass));
1365 
1366         // Verify retry was not scheduled
1367         verify(mMockChildSessionSmCallback, never()).scheduleRetryLocalRequest(any());
1368 
1369         verifyMetricsLogged(
1370                 IkeMetrics.IKE_SESSION_TYPE_CHILD, getStateCode(expectedState), expectedErrorCode);
1371     }
1372 
1373     @Test
testMobikeRekeyLocalCreateAndHandlesErrorNotifyResp()1374     public void testMobikeRekeyLocalCreateAndHandlesErrorNotifyResp() throws Exception {
1375         setupStateMachineAndSpiForLocalRekey(UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS);
1376 
1377         // Send Mobike-Rekey-Create request
1378         mChildSessionStateMachine.performRekeyMigration(
1379                 UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket);
1380         mLooper.dispatchAll();
1381 
1382         // Receive error notification in Create response
1383         mChildSessionStateMachine.receiveResponse(
1384                 EXCHANGE_TYPE_CREATE_CHILD_SA,
1385                 Arrays.asList(new IkeNotifyPayload(ERROR_TYPE_NO_ADDITIONAL_SAS)));
1386         mLooper.dispatchAll();
1387 
1388         verifyIkeSessionFatalErrorAndSendOutboundIkeDeletePayload(
1389                 NoAdditionalSasException.class,
1390                 mChildSessionStateMachine.mMobikeRekeyChildLocalCreate,
1391                 IkeMetrics.IKE_ERROR_PROTOCOL_NO_ADDITIONAL_SAS);
1392     }
1393 
1394     @Test
testMobikeRekeyLocalCreateAndHandlesMissingPayloadInResp()1395     public void testMobikeRekeyLocalCreateAndHandlesMissingPayloadInResp() throws Exception {
1396         setupStateMachineAndSpiForLocalRekey(UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS);
1397 
1398         // Send Mobike-Rekey-Create request
1399         mChildSessionStateMachine.performRekeyMigration(
1400                 UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket);
1401         mLooper.dispatchAll();
1402 
1403         // Receive response with no SA Payload
1404         List<IkePayload> validRekeyRespPayloads =
1405                 makeInboundRekeyChildPayloads(
1406                         LOCAL_INIT_NEW_CHILD_SA_SPI_OUT,
1407                         REKEY_CHILD_RESP_SA_PAYLOAD,
1408                         true /*isLocalInitRekey*/);
1409         List<IkePayload> respPayloads = new ArrayList<>();
1410         for (IkePayload payload : validRekeyRespPayloads) {
1411             if (IkePayload.PAYLOAD_TYPE_SA == payload.payloadType) continue;
1412             respPayloads.add(payload);
1413         }
1414         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads);
1415         mLooper.dispatchAll();
1416 
1417         verifyIkeSessionFatalErrorAndSendOutboundIkeDeletePayload(
1418                 IkeException.class,
1419                 mChildSessionStateMachine.mMobikeRekeyChildLocalCreate,
1420                 IkeMetrics.IKE_ERROR_PROTOCOL_INVALID_SYNTAX);
1421     }
1422 
1423     @Test
testRekeyLocalCreateHandlesRekeyRequest()1424     public void testRekeyLocalCreateHandlesRekeyRequest() throws Exception {
1425         setupStateMachineAndSpiForLocalRekey();
1426 
1427         // Send Rekey-Create request
1428         mChildSessionStateMachine.rekeyChildSession();
1429         mLooper.dispatchAll();
1430 
1431         receiveRekeyChildRequest();
1432 
1433         // Verify error notification was sent and state machine stays in the same state
1434         verifyOutboundErrorNotify(EXCHANGE_TYPE_INFORMATIONAL, ERROR_TYPE_TEMPORARY_FAILURE);
1435         assertTrue(
1436                 mChildSessionStateMachine.getCurrentState()
1437                         instanceof ChildSessionStateMachine.RekeyChildLocalCreate);
1438 
1439         // Receive Rekey Create response and verify creation is done
1440         List<IkePayload> rekeyRespPayloads = receiveRekeyChildResponse();
1441         verifyLocalRekeyCreateIsDone(
1442                 rekeyRespPayloads, false /* isMobikeRekey */, LOCAL_ADDRESS, REMOTE_ADDRESS);
1443         verifyIkeSaMetricsLogged(
1444                 1,
1445                 IkeMetrics.IKE_CALLER_UNKNOWN,
1446                 IkeMetrics.IKE_SESSION_TYPE_CHILD,
1447                 IkeMetrics.IKE_STATE_CHILD_REKEY_LOCAL_CREATE,
1448                 SaProposal.DH_GROUP_NONE,
1449                 IkeMetrics.ENCRYPTION_ALGORITHM_AES_CBC,
1450                 IkeMetrics.KEY_LEN_AES_128,
1451                 IkeMetrics.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
1452                 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED,
1453                 IkeMetrics.IKE_ERROR_NONE);
1454     }
1455 
1456     @Test
testRekeyLocalCreateHandlesDeleteRequest()1457     public void testRekeyLocalCreateHandlesDeleteRequest() throws Exception {
1458         setupStateMachineAndSpiForLocalRekey();
1459 
1460         // Send Rekey-Create request
1461         mChildSessionStateMachine.rekeyChildSession();
1462         mLooper.dispatchAll();
1463 
1464         // Receive Delete request
1465         mChildSessionStateMachine.receiveRequest(
1466                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
1467                 EXCHANGE_TYPE_INFORMATIONAL,
1468                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
1469         mLooper.dispatchAll();
1470 
1471         // Verify Delete response was sent, users were notified and statemachine is still running
1472         verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), true /*isResp*/);
1473         verifyNotifyUsersDeleteSession(
1474                 mSpyUserCbExecutor, mChildSessionStateMachine.mRekeyChildLocalCreate);
1475         assertNotNull(mChildSessionStateMachine.getCurrentState());
1476 
1477         // Receive Rekey Create response and verify Child Session is closed
1478         List<IkePayload> rekeyRespPayloads = receiveRekeyChildResponse();
1479         assertNull(mChildSessionStateMachine.getCurrentState());
1480         verifyNoMoreInteractions(mIkeMetrics);
1481     }
1482 
1483     @Test
testRekeyLocalCreateHandlesRespWithMissingPayload()1484     public void testRekeyLocalCreateHandlesRespWithMissingPayload() throws Exception {
1485         setupIdleStateMachine();
1486         setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN);
1487         reset(mMockChildSessionSmCallback);
1488 
1489         // Send Rekey-Create request
1490         mChildSessionStateMachine.rekeyChildSession();
1491         mLooper.dispatchAll();
1492 
1493         // Receive response with no SA Payload
1494         List<IkePayload> validRekeyRespPayloads =
1495                 makeInboundRekeyChildPayloads(
1496                         LOCAL_INIT_NEW_CHILD_SA_SPI_OUT,
1497                         REKEY_CHILD_RESP_SA_PAYLOAD,
1498                         true /*isLocalInitRekey*/);
1499         List<IkePayload> respPayloads = new ArrayList<>();
1500         for (IkePayload payload : validRekeyRespPayloads) {
1501             if (IkePayload.PAYLOAD_TYPE_SA == payload.payloadType) continue;
1502             respPayloads.add(payload);
1503         }
1504         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, respPayloads);
1505         mLooper.dispatchAll();
1506 
1507         // Verify user was notified and state machine has quit.
1508         verifyNotifyUsersDeleteSession(
1509                 mSpyUserCbExecutor,
1510                 mChildSessionStateMachine.mRekeyChildLocalCreate,
1511                 InvalidSyntaxException.class,
1512                 IkeMetrics.IKE_ERROR_PROTOCOL_INVALID_SYNTAX);
1513 
1514         // Verify no SPI for provisional Child was registered.
1515         verify(mMockChildSessionSmCallback, never())
1516                 .onChildSaCreated(anyInt(), eq(mChildSessionStateMachine));
1517 
1518         // Verify retry was not scheduled
1519         verify(mMockChildSessionSmCallback, never()).scheduleRetryLocalRequest(any());
1520     }
1521 
1522     @Ignore
disableTestRekeyLocalCreateChildHandlesKeyCalculationFail()1523     public void disableTestRekeyLocalCreateChildHandlesKeyCalculationFail() throws Exception {
1524         // Throw exception when building ChildSaRecord
1525         when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any()))
1526                 .thenThrow(
1527                         new GeneralSecurityException(
1528                                 "testRekeyCreateChildHandlesKeyCalculationFail"));
1529 
1530         // Setup for rekey negotiation
1531         setupIdleStateMachine();
1532         setUpSpiResource(LOCAL_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_IN);
1533         setUpSpiResource(REMOTE_ADDRESS, LOCAL_INIT_NEW_CHILD_SA_SPI_OUT);
1534         reset(mMockChildSessionSmCallback);
1535 
1536         // Send Rekey-Create request
1537         mChildSessionStateMachine.rekeyChildSession();
1538         mLooper.dispatchAll();
1539         assertTrue(
1540                 mChildSessionStateMachine.getCurrentState()
1541                         instanceof ChildSessionStateMachine.RekeyChildLocalCreate);
1542 
1543         // Receive Rekey response
1544         List<IkePayload> rekeyRespPayloads =
1545                 makeInboundRekeyChildPayloads(
1546                         LOCAL_INIT_NEW_CHILD_SA_SPI_OUT,
1547                         REKEY_CHILD_RESP_SA_PAYLOAD,
1548                         true /*isLocalInitRekey*/);
1549         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyRespPayloads);
1550         mLooper.dispatchAll();
1551 
1552         // Verify user was notified and state machine has quit.
1553         verifyNotifyUsersDeleteSession(
1554                 mSpyUserCbExecutor,
1555                 mChildSessionStateMachine.mRekeyChildLocalCreate,
1556                 IkeInternalException.class,
1557                 IkeMetrics.IKE_ERROR_INTERNAL);
1558 
1559         // Verify SPI for provisional Child was registered and unregistered.
1560         verify(mMockChildSessionSmCallback)
1561                 .onChildSaCreated(LOCAL_INIT_NEW_CHILD_SA_SPI_OUT, mChildSessionStateMachine);
1562         verify(mMockChildSessionSmCallback).onChildSaDeleted(LOCAL_INIT_NEW_CHILD_SA_SPI_OUT);
1563 
1564         // Verify retry was not scheduled
1565         verify(mMockChildSessionSmCallback, never()).scheduleRetryLocalRequest(any());
1566     }
1567 
1568     @Test
testRekeyChildLocalDeleteSendsRequest()1569     public void testRekeyChildLocalDeleteSendsRequest() throws Exception {
1570         setupIdleStateMachine();
1571 
1572         // Seed fake rekey data and force transition to RekeyChildLocalDelete
1573         mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord;
1574         mChildSessionStateMachine.sendMessage(
1575                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete);
1576         mLooper.dispatchAll();
1577 
1578         // Verify outbound delete request
1579         assertTrue(
1580                 mChildSessionStateMachine.getCurrentState()
1581                         instanceof ChildSessionStateMachine.RekeyChildLocalDelete);
1582         verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), false /*isResp*/);
1583 
1584         assertEquals(mSpyCurrentChildSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord);
1585         assertEquals(
1586                 mSpyLocalInitNewChildSaRecord, mChildSessionStateMachine.mChildSaRecordSurviving);
1587     }
1588 
verifyChildSaUpdated(ChildSaRecord oldSaRecord, ChildSaRecord newSaRecord)1589     void verifyChildSaUpdated(ChildSaRecord oldSaRecord, ChildSaRecord newSaRecord) {
1590         verify(mMockChildSessionSmCallback).onChildSaDeleted(oldSaRecord.getRemoteSpi());
1591         verify(oldSaRecord).close();
1592 
1593         assertNull(mChildSessionStateMachine.mChildSaRecordSurviving);
1594         assertEquals(newSaRecord, mChildSessionStateMachine.mCurrentChildSaRecord);
1595     }
1596 
mockRekeyChildLocalCreate()1597     private void mockRekeyChildLocalCreate() throws Exception {
1598         setupIdleStateMachine();
1599 
1600         // Seed fake rekey data and force transition to RekeyChildLocalDelete
1601         mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord;
1602         mChildSessionStateMachine.sendMessage(
1603                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete);
1604         mLooper.dispatchAll();
1605     }
1606 
1607     @Test
testRekeyChildLocalDeleteValidatesResponse()1608     public void testRekeyChildLocalDeleteValidatesResponse() throws Exception {
1609         mockRekeyChildLocalCreate();
1610 
1611         // Test receiving Delete response
1612         mChildSessionStateMachine.receiveResponse(
1613                 EXCHANGE_TYPE_INFORMATIONAL,
1614                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
1615         mLooper.dispatchAll();
1616 
1617         verifyRekeyChildLocalDeleteIsDone();
1618     }
1619 
verifyRekeyChildLocalDeleteIsDone()1620     private void verifyRekeyChildLocalDeleteIsDone() throws Exception {
1621         assertTrue(
1622                 mChildSessionStateMachine.getCurrentState()
1623                         instanceof ChildSessionStateMachine.Idle);
1624 
1625         // First invoked in #setupIdleStateMachine
1626         verify(mMockChildSessionSmCallback, times(2))
1627                 .onProcedureFinished(mChildSessionStateMachine);
1628 
1629         verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyLocalInitNewChildSaRecord);
1630 
1631         verify(mSpyUserCbExecutor).execute(any(Runnable.class));
1632         verify(mMockChildSessionCallback, never()).onClosed();
1633         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
1634     }
1635 
1636     @Test
testRekeyLocalDeleteHandlesRekeyRequest()1637     public void testRekeyLocalDeleteHandlesRekeyRequest() throws Exception {
1638         mockRekeyChildLocalCreate();
1639 
1640         receiveRekeyChildRequest();
1641 
1642         // Verify error notification was sent and state machine stays in the same state
1643         verifyOutboundErrorNotify(EXCHANGE_TYPE_INFORMATIONAL, ERROR_TYPE_TEMPORARY_FAILURE);
1644         assertTrue(
1645                 mChildSessionStateMachine.getCurrentState()
1646                         instanceof ChildSessionStateMachine.RekeyChildLocalDelete);
1647 
1648         // Test receiving Delete response
1649         mChildSessionStateMachine.receiveResponse(
1650                 EXCHANGE_TYPE_INFORMATIONAL,
1651                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
1652         mLooper.dispatchAll();
1653         verifyRekeyChildLocalDeleteIsDone();
1654     }
1655 
1656     @Test
testRekeyLocalDeleteHandlesDeleteRequest()1657     public void testRekeyLocalDeleteHandlesDeleteRequest() throws Exception {
1658         mockRekeyChildLocalCreate();
1659 
1660         // Test receiving Delete request
1661         mChildSessionStateMachine.receiveRequest(
1662                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
1663                 EXCHANGE_TYPE_INFORMATIONAL,
1664                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
1665         mLooper.dispatchAll();
1666 
1667         // Verify empty message was sent and state machine stays in the same state
1668         verify(mMockChildSessionSmCallback)
1669                 .onOutboundPayloadsReady(
1670                         eq(EXCHANGE_TYPE_INFORMATIONAL),
1671                         eq(true /*isResp*/),
1672                         mPayloadListCaptor.capture(),
1673                         eq(mChildSessionStateMachine));
1674         assertTrue(mPayloadListCaptor.getValue().isEmpty());
1675         assertTrue(
1676                 mChildSessionStateMachine.getCurrentState()
1677                         instanceof ChildSessionStateMachine.RekeyChildLocalDelete);
1678 
1679         // Test receiving Delete response
1680         mChildSessionStateMachine.receiveResponse(EXCHANGE_TYPE_INFORMATIONAL, new ArrayList<>());
1681         mLooper.dispatchAll();
1682         verifyRekeyChildLocalDeleteIsDone();
1683     }
1684 
1685     @Test
testRekeyChildLocalDeleteHandlesInvalidResp()1686     public void testRekeyChildLocalDeleteHandlesInvalidResp() throws Exception {
1687         setupIdleStateMachine();
1688 
1689         // Seed fake rekey data and force transition to RekeyChildLocalDelete
1690         mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord;
1691         mChildSessionStateMachine.sendMessage(
1692                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete);
1693         mLooper.dispatchAll();
1694 
1695         // Test receiving Delete response with missing Delete payload
1696         mChildSessionStateMachine.receiveResponse(
1697                 EXCHANGE_TYPE_INFORMATIONAL, new ArrayList<IkePayload>());
1698         mLooper.dispatchAll();
1699 
1700         // Verify rekey has finished
1701         assertTrue(
1702                 mChildSessionStateMachine.getCurrentState()
1703                         instanceof ChildSessionStateMachine.Idle);
1704         verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyLocalInitNewChildSaRecord);
1705         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
1706 
1707         // First invoked in #setupIdleStateMachine
1708         verify(mMockChildSessionSmCallback, times(2))
1709                 .onProcedureFinished(mChildSessionStateMachine);
1710     }
1711 
1712     @Test
testRekeyChildRemoteCreate()1713     public void testRekeyChildRemoteCreate() throws Exception {
1714         setupIdleStateMachine();
1715 
1716         // Setup for new Child SA negotiation.
1717         setUpSpiResource(LOCAL_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_IN);
1718         setUpSpiResource(REMOTE_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT);
1719 
1720         List<IkePayload> rekeyReqPayloads = receiveRekeyChildRequest();
1721 
1722         assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
1723 
1724         assertTrue(
1725                 mChildSessionStateMachine.getCurrentState()
1726                         instanceof ChildSessionStateMachine.RekeyChildRemoteDelete);
1727 
1728         // Verify outbound rekey response
1729         verify(mMockChildSessionSmCallback)
1730                 .onOutboundPayloadsReady(
1731                         eq(EXCHANGE_TYPE_CREATE_CHILD_SA),
1732                         eq(true),
1733                         mPayloadListCaptor.capture(),
1734                         eq(mChildSessionStateMachine));
1735         List<IkePayload> respPayloadList = mPayloadListCaptor.getValue();
1736         verifyOutboundCreatePayloadTypes(respPayloadList, true /*isRekey*/);
1737 
1738         verifyOutboundRekeySaPayload(respPayloadList, true /*isResp*/);
1739         verifyOutboundRekeyNotifyPayload(respPayloadList);
1740 
1741         // Verify new Child SA
1742         assertEquals(
1743                 mSpyRemoteInitNewChildSaRecord,
1744                 mChildSessionStateMachine.mRemoteInitNewChildSaRecord);
1745 
1746         verify(mMockChildSessionSmCallback)
1747                 .onChildSaCreated(
1748                         eq(mSpyRemoteInitNewChildSaRecord.getRemoteSpi()),
1749                         eq(mChildSessionStateMachine));
1750 
1751         verify(mMockSaRecordHelper)
1752                 .makeChildSaRecord(
1753                         eq(rekeyReqPayloads),
1754                         any(List.class),
1755                         mChildSaRecordConfigCaptor.capture());
1756         ChildSaRecordConfig childSaRecordConfig = mChildSaRecordConfigCaptor.getValue();
1757         verifyChildSaRecordConfig(
1758                 childSaRecordConfig,
1759                 REMOTE_INIT_NEW_CHILD_SA_SPI_OUT,
1760                 REMOTE_INIT_NEW_CHILD_SA_SPI_IN,
1761                 false /*isLocalInit*/);
1762 
1763         // Verify that users are notified the creation of new inbound IpSecTransform
1764         verify(mSpyUserCbExecutor).execute(any(Runnable.class));
1765         verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, true /*expectInbound*/);
1766         verifyIkeSaMetricsLogged(
1767                 1,
1768                 IkeMetrics.IKE_CALLER_UNKNOWN,
1769                 IkeMetrics.IKE_SESSION_TYPE_CHILD,
1770                 IkeMetrics.IKE_STATE_CHILD_REKEY_REMOTE_CREATE,
1771                 SaProposal.DH_GROUP_NONE,
1772                 IkeMetrics.ENCRYPTION_ALGORITHM_AES_CBC,
1773                 IkeMetrics.KEY_LEN_AES_128,
1774                 IkeMetrics.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
1775                 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED,
1776                 IkeMetrics.IKE_ERROR_NONE);
1777     }
1778 
verifyOutboundErrorNotify(int exchangeType, int errorCode)1779     private void verifyOutboundErrorNotify(int exchangeType, int errorCode) {
1780         verify(mMockChildSessionSmCallback)
1781                 .onOutboundPayloadsReady(
1782                         eq(exchangeType),
1783                         eq(true),
1784                         mPayloadListCaptor.capture(),
1785                         eq(mChildSessionStateMachine));
1786         List<IkePayload> respPayloadList = mPayloadListCaptor.getValue();
1787 
1788         assertEquals(1, respPayloadList.size());
1789         IkePayload payload = respPayloadList.get(0);
1790         assertEquals(IkePayload.PAYLOAD_TYPE_NOTIFY, payload.payloadType);
1791         assertEquals(errorCode, ((IkeNotifyPayload) payload).notifyType);
1792     }
1793 
1794     @Test
testRekeyChildRemoteCreateHandlesInvalidReq()1795     public void testRekeyChildRemoteCreateHandlesInvalidReq() throws Exception {
1796         setupIdleStateMachine();
1797 
1798         List<IkePayload> rekeyReqPayloads =
1799                 makeInboundRekeyChildPayloads(
1800                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT,
1801                         REKEY_CHILD_UNACCEPTABLE_REQ_SA_PAYLOAD,
1802                         false /*isLocalInitRekey*/);
1803 
1804         // Receive rekey Child request
1805         mChildSessionStateMachine.receiveRequest(
1806                 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
1807         mLooper.dispatchAll();
1808 
1809         // Verify error notification was sent and state machind was back to Idle
1810         verifyOutboundErrorNotify(EXCHANGE_TYPE_CREATE_CHILD_SA, ERROR_TYPE_NO_PROPOSAL_CHOSEN);
1811 
1812         assertTrue(
1813                 mChildSessionStateMachine.getCurrentState()
1814                         instanceof ChildSessionStateMachine.Idle);
1815     }
1816 
1817     @Test
testRekeyChildRemoteCreateSaCreationFail()1818     public void testRekeyChildRemoteCreateSaCreationFail() throws Exception {
1819         // Throw exception when building ChildSaRecord
1820         when(mMockSaRecordHelper.makeChildSaRecord(any(), any(), any()))
1821                 .thenThrow(
1822                         new GeneralSecurityException("testRekeyChildRemoteCreateSaCreationFail"));
1823 
1824         setupIdleStateMachine();
1825 
1826         List<IkePayload> rekeyReqPayloads =
1827                 makeInboundRekeyChildPayloads(
1828                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT,
1829                         REKEY_CHILD_REQ_SA_PAYLOAD,
1830                         false /*isLocalInitRekey*/);
1831 
1832         // Receive rekey Child request
1833         mChildSessionStateMachine.receiveRequest(
1834                 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
1835         mLooper.dispatchAll();
1836 
1837         // Verify error notification was sent and state machind was back to Idle
1838         verifyOutboundErrorNotify(EXCHANGE_TYPE_CREATE_CHILD_SA, ERROR_TYPE_NO_PROPOSAL_CHOSEN);
1839 
1840         assertTrue(
1841                 mChildSessionStateMachine.getCurrentState()
1842                         instanceof ChildSessionStateMachine.Idle);
1843     }
1844 
1845     @Test
testRekeyChildRemoteDelete()1846     public void testRekeyChildRemoteDelete() throws Exception {
1847         setupIdleStateMachine();
1848 
1849         // Seed fake rekey data and force transition to RekeyChildRemoteDelete
1850         mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord;
1851         mChildSessionStateMachine.sendMessage(
1852                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete);
1853 
1854         // Test receiving Delete request
1855         mChildSessionStateMachine.receiveRequest(
1856                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
1857                 EXCHANGE_TYPE_INFORMATIONAL,
1858                 makeDeletePayloads(mSpyCurrentChildSaRecord.getRemoteSpi()));
1859         mLooper.dispatchAll();
1860 
1861         // Verify outbound Delete response
1862         verifyOutboundDeletePayload(mSpyCurrentChildSaRecord.getLocalSpi(), true /*isResp*/);
1863 
1864         // Verify Child SA has been updated
1865         verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyRemoteInitNewChildSaRecord);
1866 
1867         // Verify procedure has been finished. #onProcedureFinished was first invoked in
1868         // #setupIdleStateMachine
1869         verify(mMockChildSessionSmCallback, times(2))
1870                 .onProcedureFinished(mChildSessionStateMachine);
1871         assertTrue(
1872                 mChildSessionStateMachine.getCurrentState()
1873                         instanceof ChildSessionStateMachine.Idle);
1874 
1875         verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class));
1876 
1877         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
1878         verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/);
1879         verify(mMockChildSessionCallback, never()).onClosed();
1880     }
1881 
1882     @Test
testRekeyChildLocalDeleteWithReqForNewSa()1883     public void testRekeyChildLocalDeleteWithReqForNewSa() throws Exception {
1884         setupIdleStateMachine();
1885         reset(mMockChildSessionSmCallback);
1886 
1887         // Seed fake rekey data and force transition to RekeyChildLocalDelete
1888         mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord;
1889         mChildSessionStateMachine.sendMessage(
1890                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete);
1891         mLooper.dispatchAll();
1892 
1893         // Test receiving Delete new Child SA request
1894         mChildSessionStateMachine.receiveRequest(
1895                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
1896                 EXCHANGE_TYPE_INFORMATIONAL,
1897                 makeDeletePayloads(mSpyLocalInitNewChildSaRecord.getRemoteSpi()));
1898 
1899         // Only dispatch the Message of receiving request
1900         mLooper.dispatchNext();
1901         assertTrue(mChildSessionStateMachine.getCurrentState() instanceof IdleWithDeferredRequest);
1902         verify(mMockChildSessionSmCallback, never()).onProcedureFinished(mChildSessionStateMachine);
1903 
1904         // Continue dispatching the deferred request
1905         mLooper.dispatchAll();
1906 
1907         // Verify outbound Delete response on new Child SA
1908         verifyOutboundDeletePayload(mSpyLocalInitNewChildSaRecord.getLocalSpi(), true /*isResp*/);
1909         verify(mMockChildSessionSmCallback)
1910                 .onChildSaDeleted(mSpyLocalInitNewChildSaRecord.getRemoteSpi());
1911         verify(mSpyLocalInitNewChildSaRecord).close();
1912 
1913         assertNull(mChildSessionStateMachine.getCurrentState());
1914 
1915         verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class));
1916 
1917         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
1918         verifyNotifyUserDeleteChildSa(mSpyLocalInitNewChildSaRecord);
1919 
1920         verify(mMockChildSessionCallback).onClosed();
1921 
1922         // #onProcedureFinished is only called when Child Session is closed
1923         verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
1924     }
1925 
1926     @Test
testRekeyChildRemoteDeleteWithReqForNewSa()1927     public void testRekeyChildRemoteDeleteWithReqForNewSa() throws Exception {
1928         setupIdleStateMachine();
1929         reset(mMockChildSessionSmCallback);
1930 
1931         // Seed fake rekey data and force transition to RekeyChildRemoteDelete
1932         mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord;
1933         mChildSessionStateMachine.sendMessage(
1934                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete);
1935         mLooper.dispatchAll();
1936 
1937         // Test receiving Delete new Child SA request
1938         mChildSessionStateMachine.receiveRequest(
1939                 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD,
1940                 EXCHANGE_TYPE_INFORMATIONAL,
1941                 makeDeletePayloads(mSpyRemoteInitNewChildSaRecord.getRemoteSpi()));
1942 
1943         // Only dispatch the Message of receiving request
1944         mLooper.dispatchNext();
1945         assertTrue(mChildSessionStateMachine.getCurrentState() instanceof IdleWithDeferredRequest);
1946         verify(mMockChildSessionSmCallback, never()).onProcedureFinished(mChildSessionStateMachine);
1947 
1948         // Continue dispatching the deferred request
1949         mLooper.dispatchAll();
1950 
1951         // Verify outbound Delete response on new Child SA
1952         verifyOutboundDeletePayload(mSpyRemoteInitNewChildSaRecord.getLocalSpi(), true /*isResp*/);
1953         verify(mMockChildSessionSmCallback)
1954                 .onChildSaDeleted(mSpyRemoteInitNewChildSaRecord.getRemoteSpi());
1955         verify(mSpyRemoteInitNewChildSaRecord).close();
1956 
1957         assertNull(mChildSessionStateMachine.getCurrentState());
1958 
1959         verify(mSpyUserCbExecutor, times(3)).execute(any(Runnable.class));
1960 
1961         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
1962         verifyNotifyUserDeleteChildSa(mSpyRemoteInitNewChildSaRecord);
1963         verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/);
1964 
1965         verify(mMockChildSessionCallback).onClosed();
1966 
1967         // #onProcedureFinished is only called when Child Session is closed
1968         verify(mMockChildSessionSmCallback).onProcedureFinished(mChildSessionStateMachine);
1969     }
1970 
1971     @Test
testRekeyChildRemoteDeleteTimeout()1972     public void testRekeyChildRemoteDeleteTimeout() throws Exception {
1973         setupIdleStateMachine();
1974 
1975         // Seed fake rekey data and force transition to RekeyChildRemoteDelete
1976         mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord;
1977         mChildSessionStateMachine.sendMessage(
1978                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete);
1979         mLooper.dispatchAll();
1980 
1981         mLooper.moveTimeForward(REKEY_DELETE_TIMEOUT_MS);
1982         mLooper.dispatchAll();
1983 
1984         // Verify no response sent.
1985         verify(mMockChildSessionSmCallback, never())
1986                 .onOutboundPayloadsReady(anyInt(), anyBoolean(), any(List.class), any());
1987 
1988         // Verify Child SA has been renewed
1989         verifyChildSaUpdated(mSpyCurrentChildSaRecord, mSpyRemoteInitNewChildSaRecord);
1990 
1991         // Verify procedure has been finished. #onProcedureFinished was first invoked in
1992         // #setupIdleStateMachine
1993         verify(mMockChildSessionSmCallback, times(2))
1994                 .onProcedureFinished(mChildSessionStateMachine);
1995         assertTrue(
1996                 mChildSessionStateMachine.getCurrentState()
1997                         instanceof ChildSessionStateMachine.Idle);
1998 
1999         verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class));
2000 
2001         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
2002         verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/);
2003 
2004         verify(mMockChildSessionCallback, never()).onClosed();
2005     }
2006 
2007     @Test
testRekeyChildRemoteDeleteExitAndRenter()2008     public void testRekeyChildRemoteDeleteExitAndRenter() throws Exception {
2009         setupIdleStateMachine();
2010 
2011         // Seed fake rekey data and force transition to RekeyChildRemoteDelete
2012         mChildSessionStateMachine.mRemoteInitNewChildSaRecord = mSpyRemoteInitNewChildSaRecord;
2013         mChildSessionStateMachine.sendMessage(
2014                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete);
2015         mLooper.dispatchAll();
2016 
2017         // Trigger a timeout, and immediately re-enter remote-delete
2018         mLooper.moveTimeForward(REKEY_DELETE_TIMEOUT_MS / 2 + 1);
2019         mChildSessionStateMachine.sendMessage(ChildSessionStateMachine.TIMEOUT_REKEY_REMOTE_DELETE);
2020         mChildSessionStateMachine.sendMessage(
2021                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildRemoteDelete);
2022         mLooper.dispatchAll();
2023 
2024         // Shift time forward
2025         mLooper.moveTimeForward(REKEY_DELETE_TIMEOUT_MS / 2 + 1);
2026         mLooper.dispatchAll();
2027 
2028         // Verify final state has not changed - timeout was not triggered.
2029         assertTrue(
2030                 mChildSessionStateMachine.getCurrentState()
2031                         instanceof ChildSessionStateMachine.RekeyChildRemoteDelete);
2032 
2033         verify(mSpyUserCbExecutor, times(2)).execute(any(Runnable.class));
2034 
2035         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
2036         verifyNotifyUsersCreateIpSecSa(mSpyRemoteInitNewChildSaRecord, false /*expectInbound*/);
2037 
2038         verify(mMockChildSessionCallback, never()).onClosed();
2039     }
2040 
2041     @Test
testCloseSessionNow()2042     public void testCloseSessionNow() throws Exception {
2043         setupIdleStateMachine();
2044 
2045         // Seed fake rekey data and force transition to RekeyChildLocalDelete
2046         mChildSessionStateMachine.mLocalInitNewChildSaRecord = mSpyLocalInitNewChildSaRecord;
2047         mChildSessionStateMachine.sendMessage(
2048                 CMD_FORCE_TRANSITION, mChildSessionStateMachine.mRekeyChildLocalDelete);
2049 
2050         mChildSessionStateMachine.killSession();
2051         mLooper.dispatchAll();
2052 
2053         assertNull(mChildSessionStateMachine.getCurrentState());
2054 
2055         verify(mSpyUserCbExecutor, times(3)).execute(any(Runnable.class));
2056 
2057         verifyNotifyUserDeleteChildSa(mSpyCurrentChildSaRecord);
2058         verifyNotifyUserDeleteChildSa(mSpyLocalInitNewChildSaRecord);
2059 
2060         verify(mMockChildSessionCallback).onClosed();
2061         verify(mIkeMetrics, never()).logSessionTerminated(anyInt(), anyInt(), anyInt(), anyInt());
2062     }
2063 
2064     @Test
testValidateExpectKeExistCase()2065     public void testValidateExpectKeExistCase() throws Exception {
2066         doReturn(new DhGroupTransform[] {mChildDhGroupTransform})
2067                 .when(mMockNegotiatedProposal)
2068                 .getDhGroupTransforms();
2069         List<IkePayload> payloadList = new ArrayList<>();
2070         payloadList.add(
2071                 IkeKePayload.createOutboundKePayload(
2072                         SaProposal.DH_GROUP_1024_BIT_MODP, createMockRandomFactory()));
2073 
2074         CreateChildSaHelper.validateKePayloads(
2075                 payloadList, true /*isResp*/, mMockNegotiatedProposal);
2076         CreateChildSaHelper.validateKePayloads(
2077                 payloadList, false /*isResp*/, mMockNegotiatedProposal);
2078     }
2079 
2080     @Test
testValidateExpectNoKeExistCase()2081     public void testValidateExpectNoKeExistCase() throws Exception {
2082         doReturn(new DhGroupTransform[0]).when(mMockNegotiatedProposal).getDhGroupTransforms();
2083         List<IkePayload> payloadList = new ArrayList<>();
2084 
2085         CreateChildSaHelper.validateKePayloads(
2086                 payloadList, true /*isResp*/, mMockNegotiatedProposal);
2087         CreateChildSaHelper.validateKePayloads(
2088                 payloadList, false /*isResp*/, mMockNegotiatedProposal);
2089     }
2090 
2091     @Test
testThrowWhenKeMissing()2092     public void testThrowWhenKeMissing() throws Exception {
2093         doReturn(new DhGroupTransform[] {mChildDhGroupTransform})
2094                 .when(mMockNegotiatedProposal)
2095                 .getDhGroupTransforms();
2096         List<IkePayload> payloadList = new ArrayList<>();
2097 
2098         try {
2099             CreateChildSaHelper.validateKePayloads(
2100                     payloadList, true /*isResp*/, mMockNegotiatedProposal);
2101             fail("Expected to fail due to the absence of KE Payload");
2102         } catch (InvalidSyntaxException expected) {
2103         }
2104 
2105         try {
2106             CreateChildSaHelper.validateKePayloads(
2107                     payloadList, false /*isResp*/, mMockNegotiatedProposal);
2108             fail("Expected to fail due to the absence of KE Payload");
2109         } catch (InvalidKeException expected) {
2110         }
2111     }
2112 
2113     @Test
testThrowWhenKeHasMismatchedDhGroup()2114     public void testThrowWhenKeHasMismatchedDhGroup() throws Exception {
2115         doReturn(new DhGroupTransform[] {mChildDhGroupTransform})
2116                 .when(mMockNegotiatedProposal)
2117                 .getDhGroupTransforms();
2118         List<IkePayload> payloadList = new ArrayList<>();
2119         payloadList.add(
2120                 IkeKePayload.createOutboundKePayload(
2121                         SaProposal.DH_GROUP_2048_BIT_MODP, createMockRandomFactory()));
2122 
2123         try {
2124             CreateChildSaHelper.validateKePayloads(
2125                     payloadList, true /*isResp*/, mMockNegotiatedProposal);
2126             fail("Expected to fail due to mismatched DH Group");
2127         } catch (InvalidSyntaxException expected) {
2128         }
2129 
2130         try {
2131             CreateChildSaHelper.validateKePayloads(
2132                     payloadList, false /*isResp*/, mMockNegotiatedProposal);
2133             fail("Expected to fail due to mismatched DH Group");
2134         } catch (InvalidKeException expected) {
2135         }
2136     }
2137 
2138     @Test
testThrowForUnexpectedKe()2139     public void testThrowForUnexpectedKe() throws Exception {
2140         DhGroupTransform noneGroup = new DhGroupTransform(SaProposal.DH_GROUP_NONE);
2141         doReturn(new DhGroupTransform[] {noneGroup})
2142                 .when(mMockNegotiatedProposal)
2143                 .getDhGroupTransforms();
2144         List<IkePayload> payloadList = new ArrayList<>();
2145         payloadList.add(
2146                 IkeKePayload.createOutboundKePayload(
2147                         SaProposal.DH_GROUP_2048_BIT_MODP, createMockRandomFactory()));
2148 
2149         try {
2150             CreateChildSaHelper.validateKePayloads(
2151                     payloadList, true /*isResp*/, mMockNegotiatedProposal);
2152             fail("Expected to fail due to unexpected KE payload.");
2153         } catch (InvalidSyntaxException expected) {
2154         }
2155 
2156         CreateChildSaHelper.validateKePayloads(
2157                 payloadList, false /*isResp*/, mMockNegotiatedProposal);
2158     }
2159 
2160     @Test
testHandleUnexpectedException()2161     public void testHandleUnexpectedException() throws Exception {
2162         Log spyIkeLog = TestUtils.makeSpyLogDoLogErrorForWtf(TAG);
2163         IkeManager.setIkeLog(spyIkeLog);
2164 
2165         mChildSessionStateMachine.createChildSession(
2166                 null /*localAddress*/,
2167                 REMOTE_ADDRESS,
2168                 mMockUdpEncapSocket,
2169                 mIkePrf,
2170                 IKE_DH_GROUP,
2171                 SK_D);
2172         mLooper.dispatchAll();
2173 
2174         verifyHandleFatalErrorAndQuit(
2175                 mChildSessionStateMachine.mCreateChildLocalCreate,
2176                 IkeInternalException.class,
2177                 IkeMetrics.IKE_ERROR_INTERNAL);
2178         verify(spyIkeLog).wtf(anyString(), anyString(), any(RuntimeException.class));
2179     }
2180 
2181     @Test
testFirstChildLocalRekey()2182     public void testFirstChildLocalRekey() throws Exception {
2183         ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP);
2184         ChildSessionParams childSessionParams =
2185                 new TunnelModeChildSessionParams.Builder()
2186                         .addSaProposal(saProposal)
2187                         .addInternalAddressRequest(AF_INET)
2188                         .addInternalAddressRequest(INTERNAL_ADDRESS)
2189                         .build();
2190         mChildSessionStateMachine = buildChildSession(childSessionParams);
2191         mChildSessionStateMachine.mIsFirstChild = true;
2192         mChildSessionStateMachine.setDbg(true);
2193         mChildSessionStateMachine.start();
2194 
2195         setupIdleStateMachine();
2196 
2197         assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
2198 
2199         // Send Rekey-Create request
2200         mChildSessionStateMachine.rekeyChildSession();
2201         mLooper.dispatchAll();
2202 
2203         assertTrue(
2204                 mChildSessionStateMachine.getCurrentState()
2205                         instanceof ChildSessionStateMachine.RekeyChildLocalCreate);
2206 
2207         verifyOutboundRekeyKePayload(false /*isResp*/);
2208     }
2209 
verifyOutboundRekeyKePayload(boolean isResp)2210     private void verifyOutboundRekeyKePayload(boolean isResp) {
2211         verify(mMockChildSessionSmCallback)
2212                 .onOutboundPayloadsReady(
2213                         eq(EXCHANGE_TYPE_CREATE_CHILD_SA),
2214                         eq(isResp),
2215                         mPayloadListCaptor.capture(),
2216                         eq(mChildSessionStateMachine));
2217 
2218         // Verify outbound payload list
2219         List<IkePayload> reqPayloadList = mPayloadListCaptor.getValue();
2220 
2221         assertNotNull(
2222                 IkePayload.getPayloadForTypeInProvidedList(
2223                         PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloadList));
2224     }
2225 
buildChildSession( ChildSessionParams childSessionParams, Executor executor)2226     private ChildSessionStateMachine buildChildSession(
2227             ChildSessionParams childSessionParams, Executor executor) {
2228         return new ChildSessionStateMachine(
2229                 new IkeContext(mLooper.getLooper(), mContext, createMockRandomFactory()),
2230                 new ChildSessionStateMachine.Config(
2231                         IKE_SESSION_UNIQUE_ID,
2232                         mMockIkeHandler,
2233                         childSessionParams,
2234                         mMockIpSecManager,
2235                         mIpSecSpiGenerator,
2236                         executor),
2237                 mMockChildSessionCallback,
2238                 mMockChildSessionSmCallback);
2239     }
2240 
buildChildSession(ChildSessionParams childSessionParams)2241     private ChildSessionStateMachine buildChildSession(ChildSessionParams childSessionParams) {
2242         return buildChildSession(childSessionParams, mSpyUserCbExecutor);
2243     }
2244 
buildChildSession(Executor executor)2245     private ChildSessionStateMachine buildChildSession(Executor executor) {
2246         return buildChildSession(mChildSessionParams, executor);
2247     }
2248 
buildAndStartChildSession(Executor executor)2249     private ChildSessionStateMachine buildAndStartChildSession(Executor executor) {
2250         ChildSessionStateMachine childSession = buildChildSession(executor);
2251         childSession.setDbg(true);
2252         childSession.start();
2253         mLooper.dispatchAll();
2254 
2255         return childSession;
2256     }
2257 
buildAndStartStateMachineWithProposal( ChildSaProposal childProposal)2258     private ChildSessionStateMachine buildAndStartStateMachineWithProposal(
2259             ChildSaProposal childProposal) {
2260         ChildSessionParams childSessionParams =
2261                 new TunnelModeChildSessionParams.Builder()
2262                         .addSaProposal(childProposal)
2263                         .addInternalAddressRequest(AF_INET)
2264                         .addInternalAddressRequest(INTERNAL_ADDRESS)
2265                         .build();
2266         ChildSessionStateMachine childSession = buildChildSession(childSessionParams);
2267         childSession.setDbg(true);
2268         childSession.start();
2269         return childSession;
2270     }
2271 
buildSaProposalWithDhGroup(int dhGroup)2272     private ChildSaProposal buildSaProposalWithDhGroup(int dhGroup) {
2273         return new ChildSaProposal.Builder()
2274                 .addEncryptionAlgorithm(
2275                         SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128)
2276                 .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
2277                 .addDhGroup(dhGroup)
2278                 .build();
2279     }
2280 
verifyRemoteRekeyWithKePayload(ChildSaProposal requestSaProposal, int expectedDh)2281     private void verifyRemoteRekeyWithKePayload(ChildSaProposal requestSaProposal, int expectedDh)
2282             throws Exception {
2283         // Setup for new Child SA negotiation.
2284         setUpSpiResource(LOCAL_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_IN);
2285         setUpSpiResource(REMOTE_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT);
2286 
2287         IkeSaPayload saPayload =
2288                 IkeSaPayload.createChildSaRequestPayload(
2289                         new ChildSaProposal[] {requestSaProposal},
2290                         mIpSecSpiGenerator,
2291                         LOCAL_ADDRESS);
2292         List<IkePayload> rekeyReqPayloads =
2293                 makeInboundRekeyChildPayloads(
2294                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /*isLocalInitRekey*/);
2295 
2296         rekeyReqPayloads.add(
2297                 IkeKePayload.createOutboundKePayload(expectedDh, createMockRandomFactory()));
2298 
2299         when(mMockSaRecordHelper.makeChildSaRecord(
2300                         eq(rekeyReqPayloads), any(List.class), any(ChildSaRecordConfig.class)))
2301                 .thenReturn(mSpyRemoteInitNewChildSaRecord);
2302 
2303         // Receive rekey Child request
2304         mChildSessionStateMachine.receiveRequest(
2305                 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
2306         mLooper.dispatchAll();
2307 
2308         assertTrue(
2309                 mChildSessionStateMachine.getCurrentState()
2310                         instanceof ChildSessionStateMachine.RekeyChildRemoteDelete);
2311 
2312         verifyOutboundRekeyKePayload(true /*isResp*/);
2313 
2314         assertEquals(expectedDh, (int) mChildSessionStateMachine.mSaProposal.getDhGroups().get(0));
2315     }
2316 
2317     @Test
testRemoteRekeyWithUserSpecifiedKePayload()2318     public void testRemoteRekeyWithUserSpecifiedKePayload() throws Exception {
2319         // Use child session params with dh group to initiate the state machine
2320         ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP);
2321         mChildSessionStateMachine.quitNow();
2322         mChildSessionStateMachine = buildAndStartStateMachineWithProposal(saProposal);
2323 
2324         setupIdleStateMachine();
2325         assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
2326 
2327         verifyRemoteRekeyWithKePayload(saProposal, SaProposal.DH_GROUP_2048_BIT_MODP);
2328     }
2329 
2330     @Test
testRemoteRekeyWithIkeNegotiatedKePayload()2331     public void testRemoteRekeyWithIkeNegotiatedKePayload() throws Exception {
2332         setupIdleStateMachine();
2333 
2334         assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
2335         assertEquals(IKE_DH_GROUP, mChildSessionStateMachine.mIkeDhGroup);
2336         for (SaProposal userProposal :
2337                 mChildSessionStateMachine.mChildSessionParams.getChildSaProposals()) {
2338             assertTrue(userProposal.getDhGroups().isEmpty());
2339         }
2340 
2341         ChildSaProposal saProposal = buildSaProposalWithDhGroup(IKE_DH_GROUP);
2342         verifyRemoteRekeyWithKePayload(saProposal, IKE_DH_GROUP);
2343     }
2344 
verifyRcvRekeyReqAndRejectWithErrorNotify( List<IkePayload> rekeyReqPayloads, int expectedErrorType)2345     private void verifyRcvRekeyReqAndRejectWithErrorNotify(
2346             List<IkePayload> rekeyReqPayloads, int expectedErrorType) {
2347         mChildSessionStateMachine.receiveRequest(
2348                 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads);
2349         mLooper.dispatchAll();
2350 
2351         assertTrue(
2352                 mChildSessionStateMachine.getCurrentState()
2353                         instanceof ChildSessionStateMachine.Idle);
2354 
2355         verifyOutboundErrorNotify(EXCHANGE_TYPE_CREATE_CHILD_SA, expectedErrorType);
2356     }
2357 
2358     @Test
testRemoteRekeyWithInvalidKePayload()2359     public void testRemoteRekeyWithInvalidKePayload() throws Exception {
2360         setupIdleStateMachine();
2361 
2362         assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size());
2363         assertEquals(IKE_DH_GROUP, mChildSessionStateMachine.mIkeDhGroup);
2364         for (SaProposal userProposal :
2365                 mChildSessionStateMachine.mChildSessionParams.getChildSaProposals()) {
2366             assertTrue(userProposal.getDhGroups().isEmpty());
2367         }
2368 
2369         // Build an inbound Rekey Child request
2370         // Build an SA Payload that includes a Proposal with IKE_DH_GROUP
2371         IkeSaPayload saPayload =
2372                 IkeSaPayload.createChildSaRequestPayload(
2373                         new ChildSaProposal[] {buildSaProposalWithDhGroup(IKE_DH_GROUP)},
2374                         mIpSecSpiGenerator,
2375                         LOCAL_ADDRESS);
2376         List<IkePayload> rekeyReqPayloads =
2377                 makeInboundRekeyChildPayloads(
2378                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /*isLocalInitRekey*/);
2379 
2380         // Build a KE Payload that uses a different DH group from the IKE_DH_GROUP
2381         rekeyReqPayloads.add(
2382                 IkeKePayload.createOutboundKePayload(
2383                         DH_GROUP_2048_BIT_MODP, createMockRandomFactory()));
2384 
2385         verifyRcvRekeyReqAndRejectWithErrorNotify(rekeyReqPayloads, ERROR_TYPE_INVALID_KE_PAYLOAD);
2386     }
2387 
2388     @Test
testRejectRemoteRekeyWithoutDhGroupInProposal()2389     public void testRejectRemoteRekeyWithoutDhGroupInProposal() throws Exception {
2390         // Use child session params with dh group to initiate the state machine
2391         mChildSessionStateMachine.quitNow();
2392         ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP);
2393         mChildSessionStateMachine = buildAndStartStateMachineWithProposal(saProposal);
2394 
2395         setupIdleStateMachine();
2396         mChildSessionStateMachine.mSaProposal = saProposal;
2397 
2398         // Build a Rekey request that does not propose DH groups.
2399         IkeSaPayload saPayload =
2400                 IkeSaPayload.createChildSaRequestPayload(
2401                         new ChildSaProposal[] {buildSaProposal()}, // Proposal with no DH group
2402                         mIpSecSpiGenerator,
2403                         LOCAL_ADDRESS);
2404         List<IkePayload> rekeyReqPayloads =
2405                 makeInboundRekeyChildPayloads(
2406                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /* isLocalInitRekey */);
2407         rekeyReqPayloads.add(
2408                 IkeKePayload.createOutboundKePayload(
2409                         DH_GROUP_2048_BIT_MODP, createMockRandomFactory()));
2410 
2411         verifyRcvRekeyReqAndRejectWithErrorNotify(rekeyReqPayloads, ERROR_TYPE_NO_PROPOSAL_CHOSEN);
2412     }
2413 
2414     @Test
testRejectRemoteRekeyWithoutKePayload()2415     public void testRejectRemoteRekeyWithoutKePayload() throws Exception {
2416         // Use child session params with dh group to initiate the state machine
2417         mChildSessionStateMachine.quitNow();
2418         ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP);
2419         mChildSessionStateMachine = buildAndStartStateMachineWithProposal(saProposal);
2420 
2421         setupIdleStateMachine();
2422         mChildSessionStateMachine.mSaProposal = saProposal;
2423 
2424         // Build a Rekey request that proposes DH groups but does not include a KE payload
2425         IkeSaPayload saPayload =
2426                 IkeSaPayload.createChildSaRequestPayload(
2427                         new ChildSaProposal[] {saProposal}, mIpSecSpiGenerator, LOCAL_ADDRESS);
2428         List<IkePayload> rekeyReqPayloads =
2429                 makeInboundRekeyChildPayloads(
2430                         REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /* isLocalInitRekey */);
2431 
2432         verifyRcvRekeyReqAndRejectWithErrorNotify(rekeyReqPayloads, ERROR_TYPE_INVALID_SYNTAX);
2433     }
2434 
verifyMobikeRekeyFallback(UdpEncapsulationSocket newEncapSocket)2435     private void verifyMobikeRekeyFallback(UdpEncapsulationSocket newEncapSocket) throws Exception {
2436         mLooper.dispatchAll();
2437 
2438         verifyRekeyChildLocalCreateHandlesResponse(
2439                 ChildSessionStateMachine.MobikeRekeyChildLocalCreate.class,
2440                 true /* isMobikeRekey */,
2441                 UPDATED_LOCAL_ADDRESS,
2442                 REMOTE_ADDRESS,
2443                 newEncapSocket);
2444 
2445         assertEquals(UPDATED_LOCAL_ADDRESS, mChildSessionStateMachine.mLocalAddress);
2446         assertEquals(REMOTE_ADDRESS, mChildSessionStateMachine.mRemoteAddress);
2447         assertEquals(newEncapSocket, mChildSessionStateMachine.mUdpEncapSocket);
2448     }
2449 
2450     @Test
testMobikeRekeyChildLocalCreateHandlesResp()2451     public void testMobikeRekeyChildLocalCreateHandlesResp() throws Exception {
2452         setupStateMachineAndSpiForLocalRekey(UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS);
2453 
2454         // Send MOBIKE Rekey-Create request
2455         mChildSessionStateMachine.performRekeyMigration(
2456                 UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket);
2457         verifyMobikeRekeyFallback(mMockUdpEncapSocket);
2458     }
2459 
2460     @Test
testMobikeRekeyChildExecuteCbAfterKillSession()2461     public void testMobikeRekeyChildExecuteCbAfterKillSession() throws Exception {
2462         mChildSessionStateMachine.quitNow();
2463         mLooper.dispatchAll();
2464 
2465         LateExecuteExecutor lateExecutor = spy(new LateExecuteExecutor());
2466         mChildSessionStateMachine = buildAndStartChildSession(lateExecutor);
2467 
2468         setupStateMachineAndSpiForLocalRekey(UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS);
2469 
2470         // MOBIKE Rekey
2471         mChildSessionStateMachine.performRekeyMigration(
2472                 UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS, mMockUdpEncapSocket);
2473         mLooper.dispatchAll();
2474         receiveRekeyChildResponse();
2475         mLooper.dispatchAll();
2476 
2477         mChildSessionStateMachine.killSession();
2478         mLooper.dispatchAll();
2479 
2480         lateExecutor.actuallyExecute();
2481         verify(mMockChildSessionCallback)
2482                 .onIpSecTransformsMigrated(
2483                         mSpyLocalInitNewChildSaRecord.getInboundIpSecTransform(),
2484                         mSpyLocalInitNewChildSaRecord.getOutboundIpSecTransform());
2485     }
2486 
2487     @Test
2488     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
testMobike_usesKernelMobikeForSameAddressFamilyAndEncapSocket()2489     public void testMobike_usesKernelMobikeForSameAddressFamilyAndEncapSocket() throws Exception {
2490         verifyMobike_usesKernelMobikeForSameEncapSocket(UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS);
2491     }
2492 
2493     @Test
2494     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
testMobike_usesKernelMobikeForDifferentAddressFamilyAndSameEncapSocket()2495     public void testMobike_usesKernelMobikeForDifferentAddressFamilyAndSameEncapSocket()
2496             throws Exception {
2497         verifyMobike_usesKernelMobikeForSameEncapSocket(LOCAL_ADDRESS_6, REMOTE_ADDRESS_6);
2498     }
2499 
verifyMobike_usesKernelMobikeForSameEncapSocket( InetAddress newLocalAddress, InetAddress newRemoteAddress)2500     private void verifyMobike_usesKernelMobikeForSameEncapSocket(
2501             InetAddress newLocalAddress, InetAddress newRemoteAddress) throws Exception {
2502         doReturn(true)
2503                 .when(mMockPackageManager)
2504                 .hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNEL_MIGRATION);
2505         setupIdleStateMachine();
2506 
2507         // Send MOBIKE request
2508         mChildSessionStateMachine.performMigration(
2509                 newLocalAddress, newRemoteAddress, mMockUdpEncapSocket);
2510         mLooper.dispatchAll();
2511 
2512         // Verify kernel MOBIKE methods called
2513         verify(mMockIpSecService)
2514                 .migrateTransform(
2515                         anyInt(),
2516                         eq(newLocalAddress.getHostAddress()),
2517                         eq(newRemoteAddress.getHostAddress()),
2518                         any());
2519         verify(mMockIpSecService)
2520                 .migrateTransform(
2521                         anyInt(),
2522                         eq(newRemoteAddress.getHostAddress()),
2523                         eq(newLocalAddress.getHostAddress()),
2524                         any());
2525 
2526         // Verify callbacks called, and state machine goes back to Idle state
2527         verify(mMockChildSessionCallback)
2528                 .onIpSecTransformsMigrated(
2529                         mSpyCurrentChildSaRecord.getInboundIpSecTransform(),
2530                         mSpyCurrentChildSaRecord.getOutboundIpSecTransform());
2531         assertTrue(
2532                 mChildSessionStateMachine.getCurrentState()
2533                         instanceof ChildSessionStateMachine.Idle);
2534 
2535         // Verify addresses and sockets correct
2536         assertEquals(newLocalAddress, mChildSessionStateMachine.mLocalAddress);
2537         assertEquals(newRemoteAddress, mChildSessionStateMachine.mRemoteAddress);
2538         assertEquals(mMockUdpEncapSocket, mChildSessionStateMachine.mUdpEncapSocket);
2539     }
2540 
verifyKernelMobikeFallbackForUnsupportedMigrations( UdpEncapsulationSocket newEncapSocket)2541     private void verifyKernelMobikeFallbackForUnsupportedMigrations(
2542             UdpEncapsulationSocket newEncapSocket) throws Exception {
2543         doReturn(true)
2544                 .when(mMockPackageManager)
2545                 .hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNEL_MIGRATION);
2546         setupStateMachineAndSpiForLocalRekey(UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS);
2547 
2548         // Send MOBIKE request
2549         mChildSessionStateMachine.performMigration(
2550                 UPDATED_LOCAL_ADDRESS, REMOTE_ADDRESS, newEncapSocket);
2551         verifyMobikeRekeyFallback(newEncapSocket);
2552     }
2553 
2554     @Test
2555     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
testMobike_usesRekeyMobikeFallbackForChangingEncapSocket()2556     public void testMobike_usesRekeyMobikeFallbackForChangingEncapSocket() throws Exception {
2557         verifyKernelMobikeFallbackForUnsupportedMigrations(mock(UdpEncapsulationSocket.class));
2558     }
2559 
2560     @Test
2561     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
testMobike_usesRekeyMobikeFallbackForChangingEncapType()2562     public void testMobike_usesRekeyMobikeFallbackForChangingEncapType() throws Exception {
2563         verifyKernelMobikeFallbackForUnsupportedMigrations(null);
2564     }
2565 
2566     // TODO (b/277668745): Add tests for 6 -> 4 and 6 -> 6 migrations with and without UDP encap.
2567 
2568     private static class LateExecuteExecutor implements Executor {
2569         private final List<Runnable> mCommands = new ArrayList<>();
2570 
2571         @Override
execute(Runnable command)2572         public void execute(Runnable command) {
2573             mCommands.add(command);
2574         }
2575 
actuallyExecute()2576         public void actuallyExecute() {
2577             for (Runnable c : mCommands) {
2578                 c.run();
2579             }
2580         }
2581     }
2582 }
2583