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 package com.android.internal.net.ipsec.ike;
17 
18 import static android.net.ipsec.ike.IkeManager.getIkeLog;
19 import static android.net.ipsec.ike.SaProposal.DH_GROUP_NONE;
20 import static android.net.ipsec.ike.exceptions.IkeException.wrapAsIkeException;
21 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
22 
23 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.BUNDLE_KEY_CHILD_REMOTE_SPI;
24 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_ALARM_FIRED;
25 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA;
26 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_IKE_AUTH;
27 import static com.android.internal.net.ipsec.ike.message.IkeHeader.EXCHANGE_TYPE_INFORMATIONAL;
28 import static com.android.internal.net.ipsec.ike.message.IkeHeader.ExchangeType;
29 import static com.android.internal.net.ipsec.ike.message.IkeMessage.IKE_EXCHANGE_SUBTYPE_DELETE_CHILD;
30 import static com.android.internal.net.ipsec.ike.message.IkeMessage.IKE_EXCHANGE_SUBTYPE_REKEY_CHILD;
31 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA;
32 import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE;
33 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_CP;
34 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_DELETE;
35 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_KE;
36 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NONCE;
37 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY;
38 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA;
39 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_INITIATOR;
40 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_TS_RESPONDER;
41 import static com.android.internal.net.ipsec.ike.message.IkePayload.PROTOCOL_ID_ESP;
42 import static com.android.internal.net.ipsec.ike.utils.IkeAlarm.IkeAlarmConfig;
43 import static com.android.internal.net.ipsec.ike.utils.IkeAlarm.buildIkeAlarmIntent;
44 import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_CHILD;
45 import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_REKEY_CHILD;
46 
47 import android.annotation.IntDef;
48 import android.annotation.Nullable;
49 import android.app.PendingIntent;
50 import android.net.IpSecManager;
51 import android.net.IpSecManager.ResourceUnavailableException;
52 import android.net.IpSecManager.SecurityParameterIndex;
53 import android.net.IpSecManager.SpiUnavailableException;
54 import android.net.IpSecManager.UdpEncapsulationSocket;
55 import android.net.IpSecTransform;
56 import android.net.ipsec.ike.ChildSaProposal;
57 import android.net.ipsec.ike.ChildSessionCallback;
58 import android.net.ipsec.ike.ChildSessionConfiguration;
59 import android.net.ipsec.ike.ChildSessionParams;
60 import android.net.ipsec.ike.IkeTrafficSelector;
61 import android.net.ipsec.ike.SaProposal;
62 import android.net.ipsec.ike.TunnelModeChildSessionParams;
63 import android.net.ipsec.ike.exceptions.IkeException;
64 import android.net.ipsec.ike.exceptions.IkeProtocolException;
65 import android.net.ipsec.ike.exceptions.InvalidKeException;
66 import android.net.ipsec.ike.exceptions.InvalidSyntaxException;
67 import android.net.ipsec.ike.exceptions.NoValidProposalChosenException;
68 import android.net.ipsec.ike.exceptions.TemporaryFailureException;
69 import android.net.ipsec.ike.exceptions.TsUnacceptableException;
70 import android.os.Bundle;
71 import android.os.Handler;
72 import android.os.Message;
73 import android.util.Pair;
74 import android.util.SparseArray;
75 
76 import com.android.internal.annotations.VisibleForTesting;
77 import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest;
78 import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequestFactory;
79 import com.android.internal.net.ipsec.ike.SaRecord.ChildSaRecord;
80 import com.android.internal.net.ipsec.ike.SaRecord.SaLifetimeAlarmScheduler;
81 import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
82 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
83 import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf;
84 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload;
85 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute;
86 import com.android.internal.net.ipsec.ike.message.IkeDeletePayload;
87 import com.android.internal.net.ipsec.ike.message.IkeKePayload;
88 import com.android.internal.net.ipsec.ike.message.IkeMessage.IkeExchangeSubType;
89 import com.android.internal.net.ipsec.ike.message.IkeNoncePayload;
90 import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload;
91 import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NotifyType;
92 import com.android.internal.net.ipsec.ike.message.IkePayload;
93 import com.android.internal.net.ipsec.ike.message.IkeSaPayload;
94 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.ChildProposal;
95 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
96 import com.android.internal.net.ipsec.ike.message.IkeTsPayload;
97 import com.android.internal.net.ipsec.ike.shim.ShimUtils;
98 import com.android.internal.net.ipsec.ike.utils.IkeMetrics;
99 import com.android.internal.net.ipsec.ike.utils.IpSecSpiGenerator;
100 import com.android.internal.net.ipsec.ike.utils.RandomnessFactory;
101 import com.android.internal.util.State;
102 
103 import java.io.IOException;
104 import java.lang.annotation.Retention;
105 import java.lang.annotation.RetentionPolicy;
106 import java.net.InetAddress;
107 import java.security.GeneralSecurityException;
108 import java.util.ArrayList;
109 import java.util.Arrays;
110 import java.util.LinkedHashSet;
111 import java.util.LinkedList;
112 import java.util.List;
113 import java.util.Set;
114 import java.util.concurrent.Executor;
115 
116 /**
117  * ChildSessionStateMachine tracks states and manages exchanges of this Child Session.
118  *
119  * <p>ChildSessionStateMachine has two types of states. One type are states where there is no
120  * ongoing procedure affecting Child Session (non-procedure state), including Initial, Idle and
121  * Receiving. All other states are "procedure" states which are named as follows:
122  *
123  * <pre>
124  * State Name = [Procedure Type] + [Exchange Initiator] + [Exchange Type].
125  * - An IKE procedure consists of one or two IKE exchanges:
126  *      Procedure Type = {CreateChild | DeleteChild | Info | RekeyChild | SimulRekeyChild}.
127  * - Exchange Initiator indicates whether local or remote peer is the exchange initiator:
128  *      Exchange Initiator = {Local | Remote}
129  * - Exchange type defines the function of this exchange.
130  *      Exchange Type = {Create | Delete}
131  * </pre>
132  */
133 public class ChildSessionStateMachine extends AbstractSessionStateMachine {
134     private static final String TAG = "ChildSessionStateMachine";
135 
136     private static final int SPI_NOT_REGISTERED = 0;
137 
138     private static final int CMD_GENERAL_BASE = CMD_PRIVATE_BASE;
139 
140     /** Receive request for negotiating first Child SA. */
141     private static final int CMD_HANDLE_FIRST_CHILD_EXCHANGE = CMD_GENERAL_BASE + 1;
142     /** Receive a request from the remote. */
143     private static final int CMD_HANDLE_RECEIVED_REQUEST = CMD_GENERAL_BASE + 2;
144     /** Receive a reponse from the remote. */
145     private static final int CMD_HANDLE_RECEIVED_RESPONSE = CMD_GENERAL_BASE + 3;
146 
147     private static final SparseArray<String> CMD_TO_STR;
148 
149     static {
150         CMD_TO_STR = new SparseArray<>();
CMD_TO_STR.put(CMD_HANDLE_FIRST_CHILD_EXCHANGE, "Handle First Child")151         CMD_TO_STR.put(CMD_HANDLE_FIRST_CHILD_EXCHANGE, "Handle First Child");
CMD_TO_STR.put(CMD_HANDLE_RECEIVED_REQUEST, "Rcv request")152         CMD_TO_STR.put(CMD_HANDLE_RECEIVED_REQUEST, "Rcv request");
CMD_TO_STR.put(CMD_HANDLE_RECEIVED_RESPONSE, "Rcv response")153         CMD_TO_STR.put(CMD_HANDLE_RECEIVED_RESPONSE, "Rcv response");
154     }
155 
156     private final int mIkeSessionId;
157     private final Handler mIkeHandler;
158     private final IpSecManager mIpSecManager;
159 
160     /**
161      * mIpSecSpiGenerator will be used by all Child SA creations in this Child Session to avoid SPI
162      * collision in test mode.
163      */
164     private final IpSecSpiGenerator mIpSecSpiGenerator;
165 
166     private final LocalRequestFactory mLocalRequestFactory = new LocalRequestFactory();
167 
168     /** User provided configurations. */
169     @VisibleForTesting final ChildSessionParams mChildSessionParams;
170 
171     private final ChildSessionCallback mUserCallback;
172 
173     /** Callback to notify IKE Session the state changes. */
174     private final IChildSessionSmCallback mChildSmCallback;
175 
176     // TODO: Also store ChildSessionCallback for notifying users.
177 
178     /** Local address assigned on device. */
179     @VisibleForTesting InetAddress mLocalAddress;
180     /** Remote address configured by users. */
181     @VisibleForTesting InetAddress mRemoteAddress;
182 
183     /**
184      * UDP-Encapsulated socket that allows IPsec traffic to pass through a NAT. Null if UDP
185      * encapsulation is not needed.
186      */
187     @VisibleForTesting @Nullable UdpEncapsulationSocket mUdpEncapSocket;
188 
189     /** Crypto parameters. Updated upon initial negotiation or IKE SA rekey. */
190     @VisibleForTesting IkeMacPrf mIkePrf;
191 
192     @VisibleForTesting byte[] mSkD;
193 
194     /**
195      * Negotiated IKE DH group
196      *
197      * <p>First Child SA, and all additional Child SAs that do not have user specified DH group are
198      * set up with crypto keys that are implicitly generated by the negotiated IKE DH group. For
199      * those Child SAs, incoming rekey requests that match the negotiated IKE DH group should also
200      * be acceptable. This for improving the interoperability with other IKE implementations.
201      */
202     @VisibleForTesting int mIkeDhGroup;
203 
204     /** Package private ChildSaProposal that represents the negotiated Child SA proposal. */
205     @VisibleForTesting ChildSaProposal mSaProposal;
206 
207     /** Negotiated local Traffic Selector. */
208     @VisibleForTesting IkeTrafficSelector[] mLocalTs;
209     /** Negotiated remote Traffic Selector. */
210     @VisibleForTesting IkeTrafficSelector[] mRemoteTs;
211 
212     @VisibleForTesting IkeCipher mChildCipher;
213     @VisibleForTesting IkeMacIntegrity mChildIntegrity;
214 
215     /** Package private */
216     @VisibleForTesting ChildSaRecord mCurrentChildSaRecord;
217     /** Package private */
218     @VisibleForTesting ChildSaRecord mLocalInitNewChildSaRecord;
219     /** Package private */
220     @VisibleForTesting ChildSaRecord mRemoteInitNewChildSaRecord;
221 
222     /** Package private */
223     @VisibleForTesting ChildSaRecord mChildSaRecordSurviving;
224 
225     @VisibleForTesting final State mKillChildSessionParent = new KillChildSessionParent();
226 
227     @VisibleForTesting final State mInitial = new Initial();
228     @VisibleForTesting final State mCreateChildLocalCreate = new CreateChildLocalCreate();
229     @VisibleForTesting final State mIdle = new Idle();
230     @VisibleForTesting final State mIdleWithDeferredRequest = new IdleWithDeferredRequest();
231     @VisibleForTesting final State mClosedAndAwaitResponse = new ClosedAndAwaitResponse();
232     @VisibleForTesting final State mDeleteChildLocalDelete = new DeleteChildLocalDelete();
233     @VisibleForTesting final State mDeleteChildRemoteDelete = new DeleteChildRemoteDelete();
234     @VisibleForTesting final State mRekeyChildLocalCreate = new RekeyChildLocalCreate();
235     @VisibleForTesting final State mMobikeRekeyChildLocalCreate = new MobikeRekeyChildLocalCreate();
236     @VisibleForTesting final State mRekeyChildRemoteCreate = new RekeyChildRemoteCreate();
237     @VisibleForTesting final State mRekeyChildLocalDelete = new RekeyChildLocalDelete();
238     @VisibleForTesting final State mRekeyChildRemoteDelete = new RekeyChildRemoteDelete();
239     @VisibleForTesting boolean mIsFirstChild;
240 
241     /**
242      * Builds a new uninitialized ChildSessionStateMachine
243      *
244      * <p>Upon creation, this state machine will await either the handleFirstChildExchange
245      * (IKE_AUTH), or the createChildSession (Additional child creation beyond the first child) to
246      * be called, both of which must pass keying and SA information.
247      *
248      * <p>This two-stage initialization is required to allow race-free user interaction with the IKE
249      * Session keyed on the child state machine callbacks.
250      *
251      * <p>Package private
252      */
ChildSessionStateMachine( IkeContext ikeContext, ChildSessionStateMachine.Config childSmConfig, ChildSessionCallback userCallback, IChildSessionSmCallback childSmCallback)253     ChildSessionStateMachine(
254             IkeContext ikeContext,
255             ChildSessionStateMachine.Config childSmConfig,
256             ChildSessionCallback userCallback,
257             IChildSessionSmCallback childSmCallback) {
258         super(TAG, ikeContext, childSmConfig.userCbExecutor);
259 
260         mIkeSessionId = childSmConfig.ikeSessionId;
261         mIkeHandler = childSmConfig.ikeHandler;
262         mIpSecManager = childSmConfig.ipSecManager;
263         mIpSecSpiGenerator = childSmConfig.ipSecSpiGenerator;
264 
265         mChildSessionParams = childSmConfig.sessionParams;
266         mUserCallback = userCallback;
267         mChildSmCallback = childSmCallback;
268 
269         addState(mKillChildSessionParent);
270 
271         addState(mInitial, mKillChildSessionParent);
272         addState(mCreateChildLocalCreate, mKillChildSessionParent);
273         addState(mIdle, mKillChildSessionParent);
274         addState(mIdleWithDeferredRequest, mKillChildSessionParent);
275         addState(mClosedAndAwaitResponse, mKillChildSessionParent);
276         addState(mDeleteChildLocalDelete, mKillChildSessionParent);
277         addState(mDeleteChildRemoteDelete, mKillChildSessionParent);
278         addState(mRekeyChildLocalCreate, mKillChildSessionParent);
279         addState(mMobikeRekeyChildLocalCreate, mKillChildSessionParent);
280         addState(mRekeyChildRemoteCreate, mKillChildSessionParent);
281         addState(mRekeyChildLocalDelete, mKillChildSessionParent);
282         addState(mRekeyChildRemoteDelete, mKillChildSessionParent);
283 
284         setInitialState(mInitial);
285     }
286 
287     // Configurations provided by an IKE Session for building the Child Session
288     static class Config {
289         public final int ikeSessionId;
290         public final Handler ikeHandler;
291         public final ChildSessionParams sessionParams;
292         public final IpSecManager ipSecManager;
293         public final IpSecSpiGenerator ipSecSpiGenerator;
294         public final Executor userCbExecutor;
295 
Config( int ikeSessionId, Handler ikeHandler, ChildSessionParams sessionParams, IpSecManager ipSecManager, IpSecSpiGenerator ipSecSpiGenerator, Executor userCbExecutor)296         Config(
297                 int ikeSessionId,
298                 Handler ikeHandler,
299                 ChildSessionParams sessionParams,
300                 IpSecManager ipSecManager,
301                 IpSecSpiGenerator ipSecSpiGenerator,
302                 Executor userCbExecutor) {
303             this.ikeSessionId = ikeSessionId;
304             this.ikeHandler = ikeHandler;
305             this.sessionParams = sessionParams;
306             this.ipSecManager = ipSecManager;
307             this.ipSecSpiGenerator = ipSecSpiGenerator;
308             this.userCbExecutor = userCbExecutor;
309         }
310     }
311 
312     /**
313      * Interface for ChildSessionStateMachine to notify IkeSessionStateMachine of state changes.
314      */
315     interface IChildSessionSmCallback {
316         /** Notify that new Child SA is created. */
onChildSaCreated(int remoteSpi, ChildSessionStateMachine childSession)317         void onChildSaCreated(int remoteSpi, ChildSessionStateMachine childSession);
318 
319         /** Notify that a Child SA is deleted. */
onChildSaDeleted(int remoteSpi)320         void onChildSaDeleted(int remoteSpi);
321 
322         /** Schedule retry for a Create Child Request on the LocalRequestScheduler. */
scheduleRetryLocalRequest(ChildLocalRequest futureRequest)323         void scheduleRetryLocalRequest(ChildLocalRequest futureRequest);
324 
325         /** Notify the IKE Session to send out IKE message for this Child Session. */
onOutboundPayloadsReady( @xchangeType int exchangeType, boolean isResp, List<IkePayload> payloadList, ChildSessionStateMachine childSession)326         void onOutboundPayloadsReady(
327                 @ExchangeType int exchangeType,
328                 boolean isResp,
329                 List<IkePayload> payloadList,
330                 ChildSessionStateMachine childSession);
331 
332         /** Notify that a Child procedure has been finished. */
onProcedureFinished(ChildSessionStateMachine childSession)333         void onProcedureFinished(ChildSessionStateMachine childSession);
334 
335         /**
336          * Notify the IKE Session State Machine that this Child has been fully shut down.
337          *
338          * <p>This method MUST be called after the user callbacks have been fired, and MUST always
339          * be called before the state machine can shut down.
340          */
onChildSessionClosed(ChildSessionCallback userCallbacks)341         void onChildSessionClosed(ChildSessionCallback userCallbacks);
342 
343         /**
344          * Notify that a Child procedure has been finished and the IKE Session should close itself
345          * because of a fatal error.
346          *
347          * <p>Child Session may encounter an IKE Session fatal error in three cases with different
348          * handling rules:
349          *
350          * <pre>
351          * - When there is a fatal error in an inbound request, onOutboundPayloadsReady will be
352          *   called first to send out an error notification, and then onFatalIkeSessionError() will
353          *   be called to locally close the IKE Session.
354          * - When there is a fatal error in an inbound response, onOutboundPayloadsReady will be
355          *   called first to send out an IKE Delete message, and then onFatalIkeSessionError() will
356          *   be called to locally close the IKE Session.
357          * - When there is a fatal error notification in an inbound response,
358          *   onFatalIkeSessionError() will be called to locally close the IKE Session"
359          * </pre>
360          */
onFatalIkeSessionError(Exception exception)361         void onFatalIkeSessionError(Exception exception);
362     }
363 
364     /**
365      * Receive requesting and responding payloads for negotiating first Child SA.
366      *
367      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
368      * as an asynchronous job to the ChildStateMachine handler.
369      *
370      * @param reqPayloads SA negotiation related payloads in IKE_AUTH request.
371      * @param respPayloads SA negotiation related payloads in IKE_AUTH response.
372      * @param localAddress The local (outer) address of the Child Session.
373      * @param remoteAddress The remote (outer) address of the Child Session.
374      * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed.
375      * @param ikePrf The pseudo-random function to use for key derivation
376      * @param ikeDh The negotiated IKE DH group
377      * @param skD The key for which to derive new keying information from.
378      */
handleFirstChildExchange( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket, IkeMacPrf ikePrf, int ikeDh, byte[] skD)379     public void handleFirstChildExchange(
380             List<IkePayload> reqPayloads,
381             List<IkePayload> respPayloads,
382             InetAddress localAddress,
383             InetAddress remoteAddress,
384             UdpEncapsulationSocket udpEncapSocket,
385             IkeMacPrf ikePrf,
386             int ikeDh,
387             byte[] skD) {
388 
389         this.mLocalAddress = localAddress;
390         this.mRemoteAddress = remoteAddress;
391         this.mUdpEncapSocket = udpEncapSocket;
392         this.mIkePrf = ikePrf;
393         this.mIkeDhGroup = ikeDh;
394         this.mSkD = skD;
395         mIsFirstChild = true;
396 
397         int spi = registerProvisionalChildAndGetSpi(respPayloads);
398         sendMessage(
399                 CMD_HANDLE_FIRST_CHILD_EXCHANGE,
400                 new FirstChildNegotiationData(reqPayloads, respPayloads, spi));
401     }
402 
403     /**
404      * Initiate Create Child procedure.
405      *
406      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
407      * as an asynchronous job to the ChildStateMachine handler.
408      *
409      * @param localAddress The local (outer) address from which traffic will originate.
410      * @param remoteAddress The remote (outer) address to which traffic will be sent.
411      * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed.
412      * @param ikePrf The pseudo-random function to use for key derivation
413      * @param ikeDh The negotiated IKE DH group
414      * @param skD The key for which to derive new keying information from.
415      */
createChildSession( InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket, IkeMacPrf ikePrf, int ikeDh, byte[] skD)416     public void createChildSession(
417             InetAddress localAddress,
418             InetAddress remoteAddress,
419             UdpEncapsulationSocket udpEncapSocket,
420             IkeMacPrf ikePrf,
421             int ikeDh,
422             byte[] skD) {
423         this.mLocalAddress = localAddress;
424         this.mRemoteAddress = remoteAddress;
425         this.mUdpEncapSocket = udpEncapSocket;
426         this.mIkePrf = ikePrf;
427         this.mIkeDhGroup = ikeDh;
428         this.mSkD = skD;
429         mIsFirstChild = false;
430 
431         sendMessage(CMD_LOCAL_REQUEST_CREATE_CHILD);
432     }
433 
434     /**
435      * Initiate Delete Child procedure.
436      *
437      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
438      * as an asynchronous job to the ChildStateMachine handler.
439      */
deleteChildSession()440     public void deleteChildSession() {
441         sendMessage(CMD_LOCAL_REQUEST_DELETE_CHILD);
442     }
443 
444     /**
445      * Initiate Rekey Child procedure.
446      *
447      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
448      * as an asynchronous job to the ChildStateMachine handler.
449      */
rekeyChildSession()450     public void rekeyChildSession() {
451         sendMessage(CMD_LOCAL_REQUEST_REKEY_CHILD);
452     }
453 
454     /**
455      * Update IPsec SAs via MOBIKE.
456      *
457      * <p>This method is called synchronously from IkeStateMachine, and may complete synchronously
458      * if kernel MOBIKE can be used. Otherwise, it will fall back to using rekeys to synchronize
459      * IPsec state.
460      *
461      * @param localAddress The local (outer) address from which traffic will originate.
462      * @param remoteAddress The remote (outer) address to which traffic will be sent.
463      * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed.
464      */
performMigration( InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket)465     public void performMigration(
466             InetAddress localAddress,
467             InetAddress remoteAddress,
468             UdpEncapsulationSocket udpEncapSocket) {
469 
470         final UdpEncapsulationSocket oldEncapSocket = mUdpEncapSocket;
471 
472         this.mLocalAddress = localAddress;
473         this.mRemoteAddress = remoteAddress;
474         this.mUdpEncapSocket = udpEncapSocket;
475 
476         if (oldEncapSocket == mUdpEncapSocket
477                 && ShimUtils.getInstance()
478                         .supportsSameSocketKernelMigration(mIkeContext.getContext())) {
479             mIpSecManager.startTunnelModeTransformMigration(
480                     mCurrentChildSaRecord.getInboundIpSecTransform(),
481                     mRemoteAddress,
482                     mLocalAddress);
483             mIpSecManager.startTunnelModeTransformMigration(
484                     mCurrentChildSaRecord.getOutboundIpSecTransform(),
485                     mLocalAddress,
486                     mRemoteAddress);
487             executeUserCallback(() -> {
488                 mUserCallback.onIpSecTransformsMigrated(
489                         mCurrentChildSaRecord.getInboundIpSecTransform(),
490                         mCurrentChildSaRecord.getOutboundIpSecTransform());
491             });
492 
493             mChildSmCallback.onProcedureFinished(ChildSessionStateMachine.this);
494         } else {
495             performRekeyMigration(localAddress, remoteAddress, udpEncapSocket);
496         }
497     }
498 
499     /**
500      * Initiate Rekey Child procedure for MOBIKE (instead of migrating IPsec SAs).
501      *
502      * <p>This method should only be used as a fallback mode for devices that do not have
503      * XFRM_MIGRATE kernel support.
504      *
505      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
506      * as an asynchronous job to the ChildStateMachine handler.
507      *
508      * <p>This method works similarly to {@link #rekeyChildSession()} in that it rekeys the Child
509      * SAs associated with this state machine. However, the caller is notified of Child SA creation
510      * via {@link ChildSessionCallback#onIpSecTransformsMigrated(android.net.IpSecTransform,
511      * android.net.IpSecTransform)};
512      *
513      * @param localAddress The local (outer) address from which traffic will originate.
514      * @param remoteAddress The remote (outer) address to which traffic will be sent.
515      * @param udpEncapSocket The socket to use for UDP encapsulation, or NULL if no encap needed.
516      */
performRekeyMigration( InetAddress localAddress, InetAddress remoteAddress, UdpEncapsulationSocket udpEncapSocket)517     public void performRekeyMigration(
518             InetAddress localAddress,
519             InetAddress remoteAddress,
520             UdpEncapsulationSocket udpEncapSocket) {
521 
522         this.mLocalAddress = localAddress;
523         this.mRemoteAddress = remoteAddress;
524         this.mUdpEncapSocket = udpEncapSocket;
525 
526         sendMessage(CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE);
527     }
528 
529     /**
530      * Receive a request
531      *
532      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
533      * as an asynchronous job to the ChildStateMachine handler.
534      *
535      * @param exchangeSubtype the exchange subtype of this inbound request.
536      * @param exchangeType the exchange type in the request message.
537      * @param payloadList the Child-procedure-related payload list in the request message that needs
538      *     validation.
539      */
receiveRequest( @keExchangeSubType int exchangeSubtype, @ExchangeType int exchangeType, List<IkePayload> payloadList)540     public void receiveRequest(
541             @IkeExchangeSubType int exchangeSubtype,
542             @ExchangeType int exchangeType,
543             List<IkePayload> payloadList) {
544         sendMessage(
545                 CMD_HANDLE_RECEIVED_REQUEST,
546                 new ReceivedRequest(exchangeSubtype, exchangeType, payloadList));
547     }
548 
549     /**
550      * Receive a response.
551      *
552      * <p>This method is called synchronously from IkeStateMachine. It proxies the synchronous call
553      * as an asynchronous job to the ChildStateMachine handler.
554      *
555      * @param exchangeType the exchange type in the response message that needs validation.
556      * @param payloadList the Child-procedure-related payload list in the response message that
557      *     needs validation.
558      */
receiveResponse(@xchangeType int exchangeType, List<IkePayload> payloadList)559     public void receiveResponse(@ExchangeType int exchangeType, List<IkePayload> payloadList) {
560         if (!isAwaitingCreateResp()) {
561             sendMessage(
562                     CMD_HANDLE_RECEIVED_RESPONSE, new ReceivedResponse(exchangeType, payloadList));
563         }
564 
565         // If we are waiting for a Create/RekeyCreate response and the received message contains SA
566         // payload we need to register for this provisional Child.
567         int spi = registerProvisionalChildAndGetSpi(payloadList);
568         sendMessage(
569                 CMD_HANDLE_RECEIVED_RESPONSE,
570                 new ReceivedCreateResponse(exchangeType, payloadList, spi));
571     }
572 
isAwaitingCreateResp()573     private boolean isAwaitingCreateResp() {
574         return (getCurrentState() == mCreateChildLocalCreate
575                 || getCurrentState() == mMobikeRekeyChildLocalCreate
576                 || getCurrentState() == mRekeyChildLocalCreate);
577     }
578 
579     /**
580      * Update SK_d with provided value when IKE SA is rekeyed.
581      *
582      * <p>It MUST be only called at the end of Rekey IKE procedure, which guarantees this Child
583      * Session is not in Create Child or Rekey Child procedure.
584      *
585      * @param skD the new skD in byte array.
586      */
setSkD(byte[] skD)587     public void setSkD(byte[] skD) {
588         mSkD = skD;
589     }
590 
591     /**
592      * Register provisioning ChildSessionStateMachine in IChildSessionSmCallback
593      *
594      * <p>This method is for avoiding CHILD_SA_NOT_FOUND error in IkeSessionStateMachine when remote
595      * peer sends request for delete/rekey this Child SA before ChildSessionStateMachine sends
596      * FirstChildNegotiationData or Create response to itself.
597      */
registerProvisionalChildAndGetSpi(List<IkePayload> respPayloads)598     private int registerProvisionalChildAndGetSpi(List<IkePayload> respPayloads) {
599         IkeSaPayload saPayload =
600                 IkePayload.getPayloadForTypeInProvidedList(
601                         PAYLOAD_TYPE_SA, IkeSaPayload.class, respPayloads);
602 
603         if (saPayload == null) return SPI_NOT_REGISTERED;
604 
605         // IkeSaPayload.Proposal stores SPI in long type so as to be applied to both 8-byte IKE SPI
606         // and 4-byte Child SPI. Here we cast the stored SPI to int to represent a Child SPI.
607         int remoteGenSpi = (int) (saPayload.proposalList.get(0).spi);
608         mChildSmCallback.onChildSaCreated(remoteGenSpi, this);
609         return remoteGenSpi;
610     }
611 
replyErrorNotification(@otifyType int notifyType)612     private void replyErrorNotification(@NotifyType int notifyType) {
613         replyErrorNotification(notifyType, new byte[0]);
614     }
615 
replyErrorNotification(@otifyType int notifyType, byte[] notifyData)616     private void replyErrorNotification(@NotifyType int notifyType, byte[] notifyData) {
617         List<IkePayload> outPayloads = new ArrayList<>(1);
618         IkeNotifyPayload notifyPayload = new IkeNotifyPayload(notifyType, notifyData);
619         outPayloads.add(notifyPayload);
620 
621         mChildSmCallback.onOutboundPayloadsReady(
622                 EXCHANGE_TYPE_INFORMATIONAL, true /*isResp*/, outPayloads, this);
623     }
624 
sendDeleteIkeRequest()625     private void sendDeleteIkeRequest() {
626         List<IkePayload> outIkePayloads = new ArrayList<>(1);
627         outIkePayloads.add(new IkeDeletePayload());
628 
629         mChildSmCallback.onOutboundPayloadsReady(
630                 EXCHANGE_TYPE_INFORMATIONAL,
631                 false /*isResp*/,
632                 outIkePayloads,
633                 ChildSessionStateMachine.this);
634     }
635 
636     class OnIpSecSaPairCreatedRunnable implements Runnable {
637         private final IpSecTransform mOut;
638         private final IpSecTransform mIn;
639 
OnIpSecSaPairCreatedRunnable(ChildSaRecord childSaRecord)640         OnIpSecSaPairCreatedRunnable(ChildSaRecord childSaRecord) {
641             mOut = childSaRecord.getOutboundIpSecTransform();
642             mIn = childSaRecord.getInboundIpSecTransform();
643         }
644 
645         @Override
run()646         public void run() {
647             mUserCallback.onIpSecTransformCreated(mOut, IpSecManager.DIRECTION_OUT);
648             mUserCallback.onIpSecTransformCreated(mIn, IpSecManager.DIRECTION_IN);
649         }
650     }
651 
652     class OnIpSecSaPairDeletedRunnable implements Runnable {
653         private final IpSecTransform mOut;
654         private final IpSecTransform mIn;
655 
OnIpSecSaPairDeletedRunnable(ChildSaRecord childSaRecord)656         OnIpSecSaPairDeletedRunnable(ChildSaRecord childSaRecord) {
657             mOut = childSaRecord.getOutboundIpSecTransform();
658             mIn = childSaRecord.getInboundIpSecTransform();
659         }
660 
661         @Override
run()662         public void run() {
663             mUserCallback.onIpSecTransformDeleted(mOut, IpSecManager.DIRECTION_OUT);
664             mUserCallback.onIpSecTransformDeleted(mIn, IpSecManager.DIRECTION_IN);
665         }
666     }
667 
668     /**
669      * ReceivedRequest contains exchange subtype and payloads that are extracted from a request
670      * message to the current Child procedure.
671      */
672     private static class ReceivedRequest {
673         @IkeExchangeSubType public final int exchangeSubtype;
674         @ExchangeType public final int exchangeType;
675         public final List<IkePayload> requestPayloads;
676 
ReceivedRequest( @keExchangeSubType int eSubtype, @ExchangeType int eType, List<IkePayload> reqPayloads)677         ReceivedRequest(
678                 @IkeExchangeSubType int eSubtype,
679                 @ExchangeType int eType,
680                 List<IkePayload> reqPayloads) {
681             exchangeSubtype = eSubtype;
682             exchangeType = eType;
683             requestPayloads = reqPayloads;
684         }
685     }
686 
687     /**
688      * ReceivedResponse contains exchange type and payloads that are extracted from a response
689      * message to the current Child procedure.
690      */
691     private static class ReceivedResponse {
692         @ExchangeType public final int exchangeType;
693         public final List<IkePayload> responsePayloads;
694 
ReceivedResponse(@xchangeType int eType, List<IkePayload> respPayloads)695         ReceivedResponse(@ExchangeType int eType, List<IkePayload> respPayloads) {
696             exchangeType = eType;
697             responsePayloads = respPayloads;
698         }
699     }
700 
701     private static class ReceivedCreateResponse extends ReceivedResponse {
702         public final int registeredSpi;
703 
ReceivedCreateResponse(@xchangeType int eType, List<IkePayload> respPayloads, int spi)704         ReceivedCreateResponse(@ExchangeType int eType, List<IkePayload> respPayloads, int spi) {
705             super(eType, respPayloads);
706             registeredSpi = spi;
707         }
708     }
709 
710     /**
711      * FirstChildNegotiationData contains payloads for negotiating first Child SA in IKE_AUTH
712      * request and IKE_AUTH response and callback to notify IkeSessionStateMachine the SA
713      * negotiation result.
714      */
715     private static class FirstChildNegotiationData extends ReceivedCreateResponse {
716         public final List<IkePayload> requestPayloads;
717 
FirstChildNegotiationData( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int spi)718         FirstChildNegotiationData(
719                 List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int spi) {
720             super(EXCHANGE_TYPE_IKE_AUTH, respPayloads, spi);
721             requestPayloads = reqPayloads;
722         }
723     }
724 
725     /** Top level state for handling uncaught exceptions for all subclasses. */
726     abstract class ExceptionHandler extends ExceptionHandlerBase {
727         @Override
cleanUpAndQuit(RuntimeException e)728         protected void cleanUpAndQuit(RuntimeException e) {
729             // TODO: b/140123526 Send a response if exception was caught when processing a request.
730 
731             // Clean up all SaRecords.
732             closeAllSaRecords(false /*expectSaClosed*/);
733 
734             executeUserCallback(
735                     () -> {
736                         mUserCallback.onClosedWithException(wrapAsIkeException(e));
737                     });
738 
739             recordMetricsEvent_sessionTerminated(wrapAsIkeException(e));
740             logWtf("Unexpected exception in " + getCurrentStateName(), e);
741             quitSessionNow();
742         }
743 
744         @Override
getCmdString(int cmd)745         protected String getCmdString(int cmd) {
746             return CMD_TO_STR.get(cmd);
747         }
748     }
749 
750     /** Called when this StateMachine quits. */
751     @Override
onQuitting()752     protected void onQuitting() {
753         // Clean up all SaRecords.
754         closeAllSaRecords(true /*expectSaClosed*/);
755 
756         mChildSmCallback.onProcedureFinished(this);
757         mChildSmCallback.onChildSessionClosed(mUserCallback);
758     }
759 
closeAllSaRecords(boolean expectSaClosed)760     private void closeAllSaRecords(boolean expectSaClosed) {
761         closeChildSaRecord(mCurrentChildSaRecord, expectSaClosed);
762         closeChildSaRecord(mLocalInitNewChildSaRecord, expectSaClosed);
763         closeChildSaRecord(mRemoteInitNewChildSaRecord, expectSaClosed);
764 
765         mCurrentChildSaRecord = null;
766         mLocalInitNewChildSaRecord = null;
767         mRemoteInitNewChildSaRecord = null;
768     }
769 
closeChildSaRecord(ChildSaRecord childSaRecord, boolean expectSaClosed)770     private void closeChildSaRecord(ChildSaRecord childSaRecord, boolean expectSaClosed) {
771         if (childSaRecord == null) return;
772 
773         OnIpSecSaPairDeletedRunnable delRunnable = new OnIpSecSaPairDeletedRunnable(childSaRecord);
774         executeUserCallback(delRunnable);
775 
776         mChildSmCallback.onChildSaDeleted(childSaRecord.getRemoteSpi());
777         childSaRecord.close();
778 
779         if (!expectSaClosed) return;
780 
781         logWtf(
782                 "ChildSaRecord with local SPI: "
783                         + childSaRecord.getLocalSpi()
784                         + " is not correctly closed.");
785     }
786 
handleChildFatalError(Exception error)787     private void handleChildFatalError(Exception error) {
788         IkeException ikeException = wrapAsIkeException(error);
789         loge("Child Session fatal error", ikeException);
790 
791         // Clean up all SaRecords and quit
792         closeAllSaRecords(false /*expectSaClosed*/);
793         executeUserCallback(
794                 () -> {
795                     mUserCallback.onClosedWithException(ikeException);
796                 });
797 
798         recordMetricsEvent_sessionTerminated(ikeException);
799         quitSessionNow();
800     }
801 
802     /**
803      * This state handles the request to close Child Session immediately without initiating any
804      * exchange.
805      *
806      * <p>Request for closing Child Session immediately is usually caused by the closing of IKE
807      * Session. All states MUST be a child state of KillChildSessionParent to handle the closing
808      * request.
809      */
810     private class KillChildSessionParent extends ExceptionHandler {
811         @Override
processStateMessage(Message message)812         public boolean processStateMessage(Message message) {
813             switch (message.what) {
814                 case CMD_KILL_SESSION:
815                     closeAllSaRecords(false /*expectSaClosed*/);
816                     executeUserCallback(
817                             () -> {
818                                 mUserCallback.onClosed();
819                             });
820 
821                     // ChildSessionTerminated Metrics not recorded; this is a result of the parent
822                     // session tearing down.
823                     quitSessionNow();
824                     return HANDLED;
825                 default:
826                     return NOT_HANDLED;
827             }
828         }
829 
830         @Override
getMetricsStateCode()831         protected @IkeMetrics.IkeState int getMetricsStateCode() {
832             return IkeMetrics.IKE_STATE_CHILD_KILL;
833         }
834     }
835 
836     /**
837      * CreateChildLocalCreateBase represents the common information for a locally-initiated initial
838      * Child SA negotiation for setting up this Child Session.
839      */
840     private abstract class CreateChildLocalCreateBase extends ExceptionHandler {
validateAndBuildChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, int registeredSpi)841         protected void validateAndBuildChild(
842                 List<IkePayload> reqPayloads,
843                 List<IkePayload> respPayloads,
844                 @ExchangeType int exchangeType,
845                 @ExchangeType int expectedExchangeType,
846                 int registeredSpi) {
847             validateAndBuildChild(
848                     reqPayloads,
849                     respPayloads,
850                     registeredSpi,
851                     CreateChildSaHelper.validateAndNegotiateInitChild(
852                             reqPayloads,
853                             respPayloads,
854                             exchangeType,
855                             expectedExchangeType,
856                             mChildSessionParams.isTransportMode(),
857                             mIpSecSpiGenerator,
858                             mRemoteAddress));
859         }
860 
validateAndBuildChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, int registeredSpi, CreateChildResult createChildResult)861         protected void validateAndBuildChild(
862                 List<IkePayload> reqPayloads,
863                 List<IkePayload> respPayloads,
864                 int registeredSpi,
865                 CreateChildResult createChildResult) {
866             switch (createChildResult.status) {
867                 case CREATE_STATUS_OK:
868                     try {
869                         setUpNegotiatedResult(createChildResult);
870 
871                         mCurrentChildSaRecord =
872                                 ChildSaRecord.makeChildSaRecord(
873                                         mIkeContext.getContext(),
874                                         reqPayloads,
875                                         respPayloads,
876                                         createChildResult.initSpi,
877                                         createChildResult.respSpi,
878                                         mLocalAddress,
879                                         mRemoteAddress,
880                                         mUdpEncapSocket,
881                                         mIkePrf,
882                                         mChildIntegrity,
883                                         mChildCipher,
884                                         mSkD,
885                                         mChildSessionParams.isTransportMode(),
886                                         true /*isLocalInit*/,
887                                         buildSaLifetimeAlarmSched(
888                                                 createChildResult.respSpi.getSpi()));
889 
890                         ChildSessionConfiguration sessionConfig =
891                                 buildChildSessionConfigFromResp(createChildResult, respPayloads);
892 
893                         OnIpSecSaPairCreatedRunnable createRunnable =
894                                 new OnIpSecSaPairCreatedRunnable(mCurrentChildSaRecord);
895                         executeUserCallback(
896                                 () -> {
897                                     createRunnable.run();
898                                     mUserCallback.onOpened(sessionConfig);
899                                 });
900 
901                         List<Integer> integrityAlgorithms = mSaProposal.getIntegrityAlgorithms();
902                         List<Integer> dhGroups = mSaProposal.getDhGroups();
903 
904                         recordMetricsEvent_SaNegotiation(
905                                 dhGroups.isEmpty()
906                                         ? IkeMetrics.DH_GROUP_UNSPECIFIED
907                                         : dhGroups.get(0),
908                                 mSaProposal.getEncryptionTransforms()[0].id,
909                                 mSaProposal.getEncryptionTransforms()[0].getSpecifiedKeyLength(),
910                                 integrityAlgorithms.isEmpty()
911                                         ? IkeMetrics.INTEGRITY_ALGORITHM_NONE
912                                         : integrityAlgorithms.get(0),
913                                 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED,
914                                 null);
915 
916                         transitionTo(mIdle);
917                     } catch (GeneralSecurityException
918                             | ResourceUnavailableException
919                             | SpiUnavailableException
920                             | IOException e) {
921                         // #makeChildSaRecord failed.
922 
923                         // TODO: Initiate deletion
924                         mChildSmCallback.onChildSaDeleted(createChildResult.respSpi.getSpi());
925                         handleChildFatalError(e);
926                     } finally {
927                         // In the successful case the transform in ChildSaRecord has taken ownership
928                         // of the SPI (in IpSecService), and will keep it alive.
929                         createChildResult.initSpi.close();
930                         createChildResult.respSpi.close();
931                     }
932                     break;
933                 case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
934                     // TODO: Initiate deletion
935                     handleCreationFailAndQuit(registeredSpi, createChildResult.exception);
936                     break;
937                 case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY:
938                     handleCreationFailAndQuit(registeredSpi, createChildResult.exception);
939                     break;
940                 default:
941                     cleanUpAndQuit(
942                             new IllegalStateException(
943                                     "Unrecognized status: " + createChildResult.status));
944             }
945         }
946 
setUpNegotiatedResult(CreateChildResult createChildResult)947         private void setUpNegotiatedResult(CreateChildResult createChildResult) {
948             // Build crypto tools using negotiated ChildSaProposal. It is ensured by {@link
949             // IkeSaPayload#getVerifiedNegotiatedChildProposalPair} that the negotiated
950             // ChildSaProposal is valid. The negotiated ChildSaProposal has exactly one encryption
951             // algorithm. When it has a combined-mode encryption algorithm, it either does not have
952             // integrity algorithm or only has one NONE value integrity algorithm. When the
953             // negotiated ChildSaProposal has a normal encryption algorithm, it either does not have
954             // integrity algorithm or has one integrity algorithm with any supported value.
955 
956             mSaProposal = createChildResult.negotiatedProposal;
957             mChildCipher = IkeCipher.create(mSaProposal.getEncryptionTransforms()[0]);
958             if (mSaProposal.getIntegrityTransforms().length != 0
959                     && mSaProposal.getIntegrityTransforms()[0].id
960                             != SaProposal.INTEGRITY_ALGORITHM_NONE) {
961                 mChildIntegrity = IkeMacIntegrity.create(mSaProposal.getIntegrityTransforms()[0]);
962             }
963 
964             mLocalTs = createChildResult.initTs;
965             mRemoteTs = createChildResult.respTs;
966         }
967 
buildChildSessionConfigFromResp( CreateChildResult createChildResult, List<IkePayload> respPayloads)968         private ChildSessionConfiguration buildChildSessionConfigFromResp(
969                 CreateChildResult createChildResult, List<IkePayload> respPayloads) {
970             IkeConfigPayload configPayload =
971                     IkePayload.getPayloadForTypeInProvidedList(
972                             PAYLOAD_TYPE_CP, IkeConfigPayload.class, respPayloads);
973 
974             if (mChildSessionParams.isTransportMode()
975                     || configPayload == null
976                     || configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) {
977                 if (configPayload != null) {
978                     logw("Unexpected config payload. Config Type: " + configPayload.configType);
979                 }
980 
981                 return new ChildSessionConfiguration(
982                         Arrays.asList(createChildResult.initTs),
983                         Arrays.asList(createChildResult.respTs));
984             } else {
985                 return new ChildSessionConfiguration(
986                         Arrays.asList(createChildResult.initTs),
987                         Arrays.asList(createChildResult.respTs),
988                         configPayload);
989             }
990         }
991 
handleCreationFailAndQuit(int registeredSpi, IkeException exception)992         private void handleCreationFailAndQuit(int registeredSpi, IkeException exception) {
993             if (registeredSpi != SPI_NOT_REGISTERED) {
994                 mChildSmCallback.onChildSaDeleted(registeredSpi);
995             }
996             handleChildFatalError(exception);
997         }
998     }
999 
getIntentIdentifier(int remoteSpi)1000     private String getIntentIdentifier(int remoteSpi) {
1001         return IkeSessionStateMachine.TAG + "_" + mIkeSessionId + "_" + TAG + "_" + remoteSpi;
1002     }
1003 
getIntentIkeSmMsg(int localRequestType, int remoteSpi)1004     private Message getIntentIkeSmMsg(int localRequestType, int remoteSpi) {
1005         Bundle spiBundle = new Bundle();
1006         spiBundle.putInt(BUNDLE_KEY_CHILD_REMOTE_SPI, remoteSpi);
1007 
1008         return mIkeHandler.obtainMessage(
1009                 CMD_ALARM_FIRED, mIkeSessionId, localRequestType, spiBundle);
1010     }
1011 
buildSaLifetimeAlarmSched(int remoteSpi)1012     private SaLifetimeAlarmScheduler buildSaLifetimeAlarmSched(int remoteSpi) {
1013         Message deleteMsg = getIntentIkeSmMsg(CMD_LOCAL_REQUEST_DELETE_CHILD, remoteSpi);
1014         Message rekeyMsg = getIntentIkeSmMsg(CMD_LOCAL_REQUEST_REKEY_CHILD, remoteSpi);
1015 
1016         PendingIntent deleteSaIntent =
1017                 buildIkeAlarmIntent(
1018                         mIkeContext.getContext(),
1019                         ACTION_DELETE_CHILD,
1020                         getIntentIdentifier(remoteSpi),
1021                         deleteMsg);
1022         PendingIntent rekeySaIntent =
1023                 buildIkeAlarmIntent(
1024                         mIkeContext.getContext(),
1025                         ACTION_REKEY_CHILD,
1026                         getIntentIdentifier(remoteSpi),
1027                         rekeyMsg);
1028 
1029         return new SaLifetimeAlarmScheduler(
1030                 new IkeAlarmConfig(
1031                         mIkeContext.getContext(),
1032                         ACTION_DELETE_CHILD,
1033                         mChildSessionParams.getHardLifetimeMsInternal(),
1034                         deleteSaIntent,
1035                         deleteMsg),
1036                 new IkeAlarmConfig(
1037                         mIkeContext.getContext(),
1038                         ACTION_REKEY_CHILD,
1039                         mChildSessionParams.getSoftLifetimeMsInternal(),
1040                         rekeySaIntent,
1041                         rekeyMsg));
1042     }
1043 
1044     /** Initial state of ChildSessionStateMachine. */
1045     class Initial extends CreateChildLocalCreateBase {
1046         List<IkePayload> mRequestPayloads;
1047 
1048         @Override
processStateMessage(Message message)1049         public boolean processStateMessage(Message message) {
1050             switch (message.what) {
1051                 case CMD_HANDLE_FIRST_CHILD_EXCHANGE:
1052                     FirstChildNegotiationData childNegotiationData =
1053                             (FirstChildNegotiationData) message.obj;
1054                     mRequestPayloads = childNegotiationData.requestPayloads;
1055                     List<IkePayload> respPayloads = childNegotiationData.responsePayloads;
1056 
1057                     // Negotiate Child SA. The exchangeType has been validated in
1058                     // IkeSessionStateMachine. Won't validate it again here.
1059                     validateAndBuildChild(
1060                             mRequestPayloads,
1061                             respPayloads,
1062                             EXCHANGE_TYPE_IKE_AUTH,
1063                             EXCHANGE_TYPE_IKE_AUTH,
1064                             childNegotiationData.registeredSpi);
1065 
1066                     return HANDLED;
1067                 case CMD_LOCAL_REQUEST_CREATE_CHILD:
1068                     transitionTo(mCreateChildLocalCreate);
1069                     return HANDLED;
1070                 case CMD_LOCAL_REQUEST_DELETE_CHILD:
1071                     // This may happen when creation has been rescheduled to be after deletion.
1072                     executeUserCallback(
1073                             () -> {
1074                                 mUserCallback.onClosed();
1075                             });
1076 
1077                     recordMetricsEvent_sessionTerminated(null);
1078                     quitSessionNow();
1079                     return HANDLED;
1080                 case CMD_FORCE_TRANSITION:
1081                     transitionTo((State) message.obj);
1082                     return HANDLED;
1083                 default:
1084                     return NOT_HANDLED;
1085             }
1086         }
1087 
1088         @Override
exitState()1089         public void exitState() {
1090             CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
1091         }
1092 
1093         @Override
getMetricsStateCode()1094         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1095             return IkeMetrics.IKE_STATE_CHILD_INITIAL;
1096         }
1097     }
1098 
1099     /**
1100      * CreateChildLocalCreate represents the state where Child Session initiates the Create Child
1101      * exchange.
1102      */
1103     class CreateChildLocalCreate extends CreateChildLocalCreateBase {
1104         private List<IkePayload> mRequestPayloads;
1105 
1106         @Override
enterState()1107         public void enterState() {
1108             try {
1109                 mRequestPayloads =
1110                         CreateChildSaHelper.getInitChildCreateReqPayloads(
1111                                 mIkeContext.getRandomnessFactory(),
1112                                 mIpSecSpiGenerator,
1113                                 mLocalAddress,
1114                                 mChildSessionParams,
1115                                 false /*isFirstChildSa*/);
1116 
1117                 final ConfigAttribute[] configAttributes =
1118                         CreateChildSaHelper.getConfigAttributes(mChildSessionParams);
1119                 if (configAttributes.length > 0) {
1120                     mRequestPayloads.add(
1121                             new IkeConfigPayload(
1122                                     false /*isReply*/, Arrays.asList(configAttributes)));
1123                 }
1124 
1125                 mChildSmCallback.onOutboundPayloadsReady(
1126                         EXCHANGE_TYPE_CREATE_CHILD_SA,
1127                         false /*isResp*/,
1128                         mRequestPayloads,
1129                         ChildSessionStateMachine.this);
1130             } catch (SpiUnavailableException | ResourceUnavailableException e) {
1131                 // Fail to assign SPI
1132                 handleChildFatalError(e);
1133             }
1134         }
1135 
1136         @Override
processStateMessage(Message message)1137         public boolean processStateMessage(Message message) {
1138             switch (message.what) {
1139                 case CMD_HANDLE_RECEIVED_RESPONSE:
1140                     ReceivedCreateResponse rcvResp = (ReceivedCreateResponse) message.obj;
1141                     CreateChildResult createChildResult =
1142                             CreateChildSaHelper.validateAndNegotiateInitChild(
1143                                     mRequestPayloads,
1144                                     rcvResp.responsePayloads,
1145                                     rcvResp.exchangeType,
1146                                     EXCHANGE_TYPE_CREATE_CHILD_SA,
1147                                     mChildSessionParams.isTransportMode(),
1148                                     mIpSecSpiGenerator,
1149                                     mRemoteAddress);
1150 
1151                     // If the response includes the error notification for TEMPORARY_FAILURE, retry
1152                     // creating the Child.
1153                     if (isTemporaryFailure(createChildResult)) {
1154                         transitionTo(mInitial);
1155 
1156                         mChildSmCallback.scheduleRetryLocalRequest(
1157                                 mLocalRequestFactory.getChildLocalRequest(
1158                                         CMD_LOCAL_REQUEST_CREATE_CHILD,
1159                                         mUserCallback,
1160                                         mChildSessionParams));
1161                         return HANDLED;
1162                     }
1163 
1164                     validateAndBuildChild(
1165                             mRequestPayloads,
1166                             rcvResp.responsePayloads,
1167                             rcvResp.registeredSpi,
1168                             createChildResult);
1169                     return HANDLED;
1170                 default:
1171                     return NOT_HANDLED;
1172             }
1173         }
1174 
1175         @Override
exitState()1176         public void exitState() {
1177             CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
1178         }
1179 
1180         @Override
getMetricsStateCode()1181         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1182             return IkeMetrics.IKE_STATE_CHILD_CREATE_LOCAL_CREATE;
1183         }
1184 
isTemporaryFailure(CreateChildResult createChildResult)1185         private boolean isTemporaryFailure(CreateChildResult createChildResult) {
1186             if (createChildResult.status != CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY) {
1187                 return false;
1188             }
1189             return createChildResult.exception instanceof TemporaryFailureException;
1190         }
1191     }
1192 
1193     /**
1194      * Idle represents a state when there is no ongoing IKE exchange affecting established Child SA.
1195      */
1196     class Idle extends ExceptionHandler {
1197         @Override
enterState()1198         public void enterState() {
1199             maybeNotifyIkeSessionStateMachine();
1200         }
1201 
maybeNotifyIkeSessionStateMachine()1202         protected void maybeNotifyIkeSessionStateMachine() {
1203             mChildSmCallback.onProcedureFinished(ChildSessionStateMachine.this);
1204         }
1205 
1206         @Override
processStateMessage(Message message)1207         public boolean processStateMessage(Message message) {
1208             switch (message.what) {
1209                 case CMD_LOCAL_REQUEST_DELETE_CHILD:
1210                     transitionTo(mDeleteChildLocalDelete);
1211                     return HANDLED;
1212                 case CMD_LOCAL_REQUEST_REKEY_CHILD:
1213                     transitionTo(mRekeyChildLocalCreate);
1214                     return HANDLED;
1215                 case CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE:
1216                     transitionTo(mMobikeRekeyChildLocalCreate);
1217                     return HANDLED;
1218                 case CMD_HANDLE_RECEIVED_REQUEST:
1219                     ReceivedRequest req = (ReceivedRequest) message.obj;
1220                     switch (req.exchangeSubtype) {
1221                         case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD:
1222                             deferMessage(message);
1223                             transitionTo(mDeleteChildRemoteDelete);
1224                             return HANDLED;
1225                         case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD:
1226                             deferMessage(message);
1227                             transitionTo(mRekeyChildRemoteCreate);
1228                             return HANDLED;
1229                         default:
1230                             return NOT_HANDLED;
1231                     }
1232                 case CMD_FORCE_TRANSITION: // Testing command
1233                     transitionTo((State) message.obj);
1234                     return HANDLED;
1235                 default:
1236                     return NOT_HANDLED;
1237             }
1238         }
1239 
1240         @Override
getMetricsStateCode()1241         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1242             return IkeMetrics.IKE_STATE_CHILD_IDLE;
1243         }
1244     }
1245 
1246     /**
1247      * This class is for handling the case when the previous procedure was finished by a new request
1248      *
1249      * <p>This state is the destination state when Child Session receives a new procedure request in
1250      * Rekey Delete. When entering this state, Child Session will process the deferred request as
1251      * Idle state does but will not notify IKE Session that Child Session has finished all the
1252      * procedures. It prevents IKE Session from going back to Idle state when its Child Session is
1253      * still busy.
1254      */
1255     class IdleWithDeferredRequest extends Idle {
1256         @Override
maybeNotifyIkeSessionStateMachine()1257         public void maybeNotifyIkeSessionStateMachine() {
1258             // Do not notify IkeSessionStateMachine because Child Session needs to process the
1259             // deferred request and start a new procedure
1260         }
1261 
1262         @Override
getMetricsStateCode()1263         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1264             return IkeMetrics.IKE_STATE_CHILD_IDLE_WITH_DEFERRED_REQUEST;
1265         }
1266     }
1267 
1268     /**
1269      * This class represents the state that Child Session was closed by the remote while waiting for
1270      * a response.
1271      *
1272      * <p>This state is the destination state when Child Session receives a Delete request while
1273      * waitng for a Rekey Create response. When that happens, Child Session should close all IPsec
1274      * SAs and notify the user immediately to prevent security risk. Child Session also needs to
1275      * continue waiting for the response and keep its parent IKE Session retransmitting the request,
1276      * as required by the IKE spec.
1277      */
1278     private class ClosedAndAwaitResponse extends ExceptionHandler {
1279         @Override
processStateMessage(Message message)1280         public boolean processStateMessage(Message message) {
1281             switch (message.what) {
1282                 case CMD_HANDLE_RECEIVED_RESPONSE:
1283                     // Do not need to verify the response since the Child Session is already closed
1284 
1285                     // Metrics not recorded, since already closed. Metrics recorded at the same
1286                     // time that user callbacks are fired.
1287                     quitSessionNow();
1288                     return HANDLED;
1289                 default:
1290                     return NOT_HANDLED;
1291             }
1292         }
1293 
1294         @Override
getMetricsStateCode()1295         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1296             return IkeMetrics.IKE_STATE_CHILD_CLOSE_AND_AWAIT_RESPONSE;
1297         }
1298     }
1299 
1300     /**
1301      * DeleteResponderBase represents all states after Child Session is established
1302      *
1303      * <p>All post-init states share common functionality of being able to respond to Delete Child
1304      * requests.
1305      */
1306     private abstract class DeleteResponderBase extends ExceptionHandler {
1307         /**
1308          * Check if the payload list has a Delete Payload that includes the remote SPI of the input
1309          * ChildSaRecord.
1310          */
hasRemoteChildSpiForDelete( List<IkePayload> payloads, ChildSaRecord expectedRecord)1311         protected boolean hasRemoteChildSpiForDelete(
1312                 List<IkePayload> payloads, ChildSaRecord expectedRecord) {
1313             List<IkeDeletePayload> delPayloads =
1314                     IkePayload.getPayloadListForTypeInProvidedList(
1315                             PAYLOAD_TYPE_DELETE, IkeDeletePayload.class, payloads);
1316 
1317             for (IkeDeletePayload delPayload : delPayloads) {
1318                 for (int spi : delPayload.spisToDelete) {
1319                     if (spi == expectedRecord.getRemoteSpi()) return true;
1320                 }
1321             }
1322             return false;
1323         }
1324 
1325         /**
1326          * Build and send payload list that has a Delete Payload that includes the local SPI of the
1327          * input ChildSaRecord.
1328          */
sendDeleteChild(ChildSaRecord childSaRecord, boolean isResp)1329         protected void sendDeleteChild(ChildSaRecord childSaRecord, boolean isResp) {
1330             List<IkePayload> outIkePayloads = new ArrayList<>(1);
1331             outIkePayloads.add(new IkeDeletePayload(new int[] {childSaRecord.getLocalSpi()}));
1332 
1333             mChildSmCallback.onOutboundPayloadsReady(
1334                     EXCHANGE_TYPE_INFORMATIONAL,
1335                     isResp,
1336                     outIkePayloads,
1337                     ChildSessionStateMachine.this);
1338         }
1339 
1340         /**
1341          * Helper method for responding to a session deletion request
1342          *
1343          * <p>Note that this method expects that the session is keyed on the mCurrentChildSaRecord
1344          * and closing this Child SA indicates that the remote wishes to end the session as a whole.
1345          * As such, this should not be used in rekey cases where there is any ambiguity as to which
1346          * Child SA the session is reliant upon.
1347          *
1348          * <p>Note that this method will also quit the state machine
1349          */
handleDeleteSessionRequest(List<IkePayload> payloads)1350         protected void handleDeleteSessionRequest(List<IkePayload> payloads) {
1351             if (!hasRemoteChildSpiForDelete(payloads, mCurrentChildSaRecord)) {
1352                 cleanUpAndQuit(
1353                         new IllegalStateException(
1354                                 "Found no remote SPI for mCurrentChildSaRecord in a Delete Child"
1355                                         + " request."));
1356             } else {
1357                 sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/);
1358                 closeSessionAndNotifyUser(true /* quitStateMachine */);
1359             }
1360         }
1361 
closeSessionAndNotifyUser(boolean quitStateMachine)1362         protected void closeSessionAndNotifyUser(boolean quitStateMachine) {
1363             OnIpSecSaPairDeletedRunnable delRunnable =
1364                     new OnIpSecSaPairDeletedRunnable(mCurrentChildSaRecord);
1365             executeUserCallback(
1366                     () -> {
1367                         delRunnable.run();
1368                         mUserCallback.onClosed();
1369                     });
1370             recordMetricsEvent_sessionTerminated(null);
1371 
1372             mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi());
1373             mCurrentChildSaRecord.close();
1374             mCurrentChildSaRecord = null;
1375 
1376             if (quitStateMachine) {
1377                 quitSessionNow();
1378             }
1379         }
1380     }
1381 
1382     /**
1383      * DeleteBase abstracts deletion handling for all states initiating and responding to a Delete
1384      * Child exchange
1385      *
1386      * <p>All subclasses of this state share common functionality that a deletion request is sent,
1387      * and the response is received.
1388      */
1389     private abstract class DeleteBase extends DeleteResponderBase {
1390         /** Validate payload types in Delete Child response. */
validateDeleteRespPayloadAndExchangeType( List<IkePayload> respPayloads, @ExchangeType int exchangeType)1391         protected void validateDeleteRespPayloadAndExchangeType(
1392                 List<IkePayload> respPayloads, @ExchangeType int exchangeType)
1393                 throws IkeProtocolException {
1394 
1395             if (exchangeType != EXCHANGE_TYPE_INFORMATIONAL) {
1396                 throw new InvalidSyntaxException(
1397                         "Unexpected exchange type in Delete Child response: " + exchangeType);
1398             }
1399 
1400             for (IkePayload payload : respPayloads) {
1401                 handlePayload:
1402                 switch (payload.payloadType) {
1403                     case PAYLOAD_TYPE_DELETE:
1404                         // A Delete Payload is only required when it is not simultaneous deletion.
1405                         // Included Child SPIs are verified in the subclass to make sure the remote
1406                         // side is deleting the right SAs.
1407                         break handlePayload;
1408                     case PAYLOAD_TYPE_NOTIFY:
1409                         IkeNotifyPayload notify = (IkeNotifyPayload) payload;
1410                         if (!notify.isErrorNotify()) {
1411                             logw(
1412                                     "Unexpected or unknown status notification in Delete Child"
1413                                             + " response: "
1414                                             + notify.notifyType);
1415                             break handlePayload;
1416                         }
1417 
1418                         throw notify.validateAndBuildIkeException();
1419                     default:
1420                         logw(
1421                                 "Unexpected payload type in Delete Child response: "
1422                                         + payload.payloadType);
1423                 }
1424             }
1425         }
1426     }
1427 
1428     /**
1429      * DeleteChildLocalDelete represents the state where Child Session initiates the Delete Child
1430      * exchange.
1431      */
1432     class DeleteChildLocalDelete extends DeleteBase {
1433         private boolean mSimulDeleteDetected = false;
1434 
1435         @Override
enterState()1436         public void enterState() {
1437             mSimulDeleteDetected = false;
1438             sendDeleteChild(mCurrentChildSaRecord, false /*isResp*/);
1439         }
1440 
1441         @Override
processStateMessage(Message message)1442         public boolean processStateMessage(Message message) {
1443             switch (message.what) {
1444                 case CMD_HANDLE_RECEIVED_RESPONSE:
1445                     try {
1446                         ReceivedResponse resp = (ReceivedResponse) message.obj;
1447                         validateDeleteRespPayloadAndExchangeType(
1448                                 resp.responsePayloads, resp.exchangeType);
1449 
1450                         boolean currentSaSpiFound =
1451                                 hasRemoteChildSpiForDelete(
1452                                         resp.responsePayloads, mCurrentChildSaRecord);
1453                         if (!currentSaSpiFound && !mSimulDeleteDetected) {
1454                             throw new InvalidSyntaxException(
1455                                     "Found no remote SPI in received Delete response.");
1456                         } else if (currentSaSpiFound && mSimulDeleteDetected) {
1457                             // As required by the RFC 7296, in simultaneous delete case, the remote
1458                             // side MUST NOT include SPI of mCurrentChildSaRecord. However, to
1459                             // provide better interoperatibility, IKE library will keep IKE Session
1460                             // alive and continue the deleting process.
1461                             logw(
1462                                     "Found remote SPI in the Delete response in a simultaneous"
1463                                             + " deletion case");
1464                         }
1465 
1466                         closeSessionAndNotifyUser(true /* quitStateMachine */);
1467                     } catch (IkeProtocolException e) {
1468                         // Shut down Child Session and notify users the error.
1469                         handleChildFatalError(e);
1470                     }
1471                     return HANDLED;
1472                 case CMD_HANDLE_RECEIVED_REQUEST:
1473                     ReceivedRequest req = (ReceivedRequest) message.obj;
1474                     switch (req.exchangeSubtype) {
1475                         case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD:
1476                             // It has been verified in IkeSessionStateMachine that the incoming
1477                             // request can be ONLY for mCurrentChildSaRecord at this point.
1478                             if (!hasRemoteChildSpiForDelete(
1479                                     req.requestPayloads, mCurrentChildSaRecord)) {
1480                                 // Program error
1481                                 cleanUpAndQuit(
1482                                         new IllegalStateException(
1483                                                 "Found no remote SPI for mCurrentChildSaRecord in"
1484                                                         + " a Delete request"));
1485 
1486                             } else {
1487                                 mChildSmCallback.onOutboundPayloadsReady(
1488                                         EXCHANGE_TYPE_INFORMATIONAL,
1489                                         true /*isResp*/,
1490                                         new LinkedList<>(),
1491                                         ChildSessionStateMachine.this);
1492                                 mSimulDeleteDetected = true;
1493                             }
1494                             return HANDLED;
1495                         case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD:
1496                             replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE);
1497                             return HANDLED;
1498                         default:
1499                             cleanUpAndQuit(
1500                                     new IllegalStateException(
1501                                             "Invalid exchange subtype for Child Session: "
1502                                                     + req.exchangeSubtype));
1503                             return HANDLED;
1504                     }
1505                 default:
1506                     return NOT_HANDLED;
1507             }
1508         }
1509 
1510         @Override
getMetricsStateCode()1511         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1512             return IkeMetrics.IKE_STATE_CHILD_DELETE_LOCAL_DELETE;
1513         }
1514     }
1515 
1516     /**
1517      * DeleteChildRemoteDelete represents the state where Child Session receives the Delete Child
1518      * request.
1519      */
1520     class DeleteChildRemoteDelete extends DeleteResponderBase {
1521         @Override
processStateMessage(Message message)1522         public boolean processStateMessage(Message message) {
1523             switch (message.what) {
1524                 case CMD_HANDLE_RECEIVED_REQUEST:
1525                     ReceivedRequest req = (ReceivedRequest) message.obj;
1526                     if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) {
1527                         handleDeleteSessionRequest(req.requestPayloads);
1528                         return HANDLED;
1529                     }
1530                     return NOT_HANDLED;
1531                 default:
1532                     return NOT_HANDLED;
1533             }
1534         }
1535 
1536         @Override
getMetricsStateCode()1537         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1538             return IkeMetrics.IKE_STATE_CHILD_DELETE_REMOTE_DELETE;
1539         }
1540     }
1541 
1542     /**
1543      * RekeyChildLocalCreate represents the state where Child Session initiates the Rekey Child
1544      * exchange.
1545      *
1546      * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have
1547      * different Traffic Selectors and algorithms than the old one."
1548      */
1549     class RekeyChildLocalCreate extends DeleteResponderBase {
1550         private List<IkePayload> mRequestPayloads;
1551 
1552         @Override
enterState()1553         public void enterState() {
1554             try {
1555                 ChildSaProposal saProposal = mSaProposal;
1556                 if (mIsFirstChild) {
1557                     saProposal = addDhGroupsFromChildSessionParamsIfAbsent();
1558                 }
1559 
1560                 // Build request with negotiated proposal and TS.
1561                 mRequestPayloads =
1562                         CreateChildSaHelper.getRekeyChildCreateReqPayloads(
1563                                 mIkeContext.getRandomnessFactory(),
1564                                 mIpSecSpiGenerator,
1565                                 mLocalAddress,
1566                                 saProposal,
1567                                 mLocalTs,
1568                                 mRemoteTs,
1569                                 mCurrentChildSaRecord.getLocalSpi(),
1570                                 mChildSessionParams.isTransportMode());
1571                 mChildSmCallback.onOutboundPayloadsReady(
1572                         EXCHANGE_TYPE_CREATE_CHILD_SA,
1573                         false /*isResp*/,
1574                         mRequestPayloads,
1575                         ChildSessionStateMachine.this);
1576             } catch (SpiUnavailableException | ResourceUnavailableException e) {
1577                 loge("Fail to assign Child SPI. Schedule a retry for rekey Child");
1578                 mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
1579                 transitionTo(mIdle);
1580             }
1581         }
1582 
1583         @Override
processStateMessage(Message message)1584         public boolean processStateMessage(Message message) {
1585             switch (message.what) {
1586                 case CMD_HANDLE_RECEIVED_REQUEST:
1587                     ReceivedRequest req = (ReceivedRequest) message.obj;
1588 
1589                     if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) {
1590                         // Handle Delete request, notify users and do state transition to continue
1591                         // waiting for the response
1592                         sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/);
1593                         closeSessionAndNotifyUser(false /* quitStateMachine */);
1594                         transitionTo(mClosedAndAwaitResponse);
1595                     } else {
1596                         replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE);
1597                     }
1598                     return HANDLED;
1599                 case CMD_HANDLE_RECEIVED_RESPONSE:
1600                     ReceivedCreateResponse resp = (ReceivedCreateResponse) message.obj;
1601                     CreateChildResult createChildResult =
1602                             CreateChildSaHelper.validateAndNegotiateRekeyChildResp(
1603                                     mRequestPayloads,
1604                                     resp.responsePayloads,
1605                                     resp.exchangeType,
1606                                     EXCHANGE_TYPE_CREATE_CHILD_SA,
1607                                     mChildSessionParams.isTransportMode(),
1608                                     mCurrentChildSaRecord,
1609                                     mIpSecSpiGenerator,
1610                                     mRemoteAddress);
1611 
1612                     switch (createChildResult.status) {
1613                         case CREATE_STATUS_OK:
1614                             try {
1615                                 // Do not need to update TS because they are not changed.
1616                                 mSaProposal = createChildResult.negotiatedProposal;
1617 
1618                                 mLocalInitNewChildSaRecord =
1619                                         ChildSaRecord.makeChildSaRecord(
1620                                                 mIkeContext.getContext(),
1621                                                 mRequestPayloads,
1622                                                 resp.responsePayloads,
1623                                                 createChildResult.initSpi,
1624                                                 createChildResult.respSpi,
1625                                                 mLocalAddress,
1626                                                 mRemoteAddress,
1627                                                 mUdpEncapSocket,
1628                                                 mIkePrf,
1629                                                 mChildIntegrity,
1630                                                 mChildCipher,
1631                                                 mSkD,
1632                                                 mChildSessionParams.isTransportMode(),
1633                                                 true /*isLocalInit*/,
1634                                                 buildSaLifetimeAlarmSched(
1635                                                         createChildResult.respSpi.getSpi()));
1636 
1637                                 List<Integer> integrityAlgorithms =
1638                                         mSaProposal.getIntegrityAlgorithms();
1639                                 List<Integer> dhGroups = mSaProposal.getDhGroups();
1640 
1641                                 recordMetricsEvent_SaNegotiation(
1642                                         dhGroups.isEmpty()
1643                                                 ? IkeMetrics.DH_GROUP_UNSPECIFIED
1644                                                 : dhGroups.get(0),
1645                                         mSaProposal.getEncryptionTransforms()[0].id,
1646                                         mSaProposal.getEncryptionTransforms()[0]
1647                                                 .getSpecifiedKeyLength(),
1648                                         integrityAlgorithms.isEmpty()
1649                                                 ? IkeMetrics.INTEGRITY_ALGORITHM_NONE
1650                                                 : integrityAlgorithms.get(0),
1651                                         IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED,
1652                                         null);
1653 
1654                                 notifyCallerForLocalChildSaRekey();
1655 
1656                                 transitionTo(mRekeyChildLocalDelete);
1657                             } catch (GeneralSecurityException
1658                                     | ResourceUnavailableException
1659                                     | SpiUnavailableException
1660                                     | IOException e) {
1661                                 // #makeChildSaRecord failed
1662                                 handleProcessRespOrSaCreationFailAndQuit(resp.registeredSpi, e);
1663                             } finally {
1664                                 // In the successful case the transform in ChildSaRecord has taken
1665                                 // ownership of the SPI (in IpSecService), and will keep it alive.
1666                                 createChildResult.initSpi.close();
1667                                 createChildResult.respSpi.close();
1668                             }
1669                             break;
1670                         case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
1671                             handleProcessRespOrSaCreationFailAndQuit(
1672                                     resp.registeredSpi, createChildResult.exception);
1673                             break;
1674                         case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY:
1675                             handleErrorNotify(createChildResult.exception);
1676                             transitionTo(mIdle);
1677                             break;
1678                         default:
1679                             cleanUpAndQuit(
1680                                     new IllegalStateException(
1681                                             "Unrecognized status: " + createChildResult.status));
1682                     }
1683                     return HANDLED;
1684                 default:
1685                     return NOT_HANDLED;
1686             }
1687         }
1688 
notifyCallerForLocalChildSaRekey()1689         protected void notifyCallerForLocalChildSaRekey() {
1690             OnIpSecSaPairCreatedRunnable createRunnable =
1691                     new OnIpSecSaPairCreatedRunnable(mLocalInitNewChildSaRecord);
1692             executeUserCallback(createRunnable);
1693         }
1694 
handleProcessRespOrSaCreationFailAndQuit( int registeredSpi, Exception exception)1695         protected void handleProcessRespOrSaCreationFailAndQuit(
1696                 int registeredSpi, Exception exception) {
1697             // We don't retry rekey if failure was caused by invalid response or SA creation error.
1698             // Reason is there is no way to notify the remote side the old SA is still alive but the
1699             // new one has failed. Sending delete request for new SA indicates the rekey has
1700             // finished and the new SA has died.
1701 
1702             // TODO: Initiate deletion on newly created SA
1703             if (registeredSpi != SPI_NOT_REGISTERED) {
1704                 mChildSmCallback.onChildSaDeleted(registeredSpi);
1705             }
1706             handleChildFatalError(exception);
1707         }
1708 
handleErrorNotify(Exception exception)1709         protected void handleErrorNotify(Exception exception) {
1710             loge("Received error notification for rekey Child. Schedule a retry");
1711             mCurrentChildSaRecord.rescheduleRekey(RETRY_INTERVAL_MS);
1712         }
1713 
1714         @Override
exitState()1715         public void exitState() {
1716             CreateChildSaHelper.releaseSpiResources(mRequestPayloads);
1717         }
1718 
1719         @Override
getMetricsStateCode()1720         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1721             return IkeMetrics.IKE_STATE_CHILD_REKEY_LOCAL_CREATE;
1722         }
1723     }
1724 
1725     /**
1726      * MobikeRekeyChildLocalCreate represents the state where Child Session initiates the Rekey
1727      * Child exchange for MOBIKE-enabled IKE Sessions.
1728      *
1729      * <p>MobikeRekeyChildLocalCreate behaves similarly to RekeyChildLocalCreate except:
1730      *
1731      * <ul>
1732      *   <li>It notifies the caller of Child SA creation via {@link
1733      *       ChildSessionCallback#onIpSecTransformsMigrated(android.net.IpSecTransform,
1734      *       android.net.IpSecTransform)}. .
1735      *   <li>It tears down IKE SA immediately if any error occurs. In this case the origin Child SA
1736      *       has lost connectivity due to the changed IP addresses, and renewing the Child SA by
1737      *       MOBIKE rekey has failed. IKE module will handle this failure by tearing down the IKE
1738      *       Session and notifying the caller. In this way, the caller can immediately re-establish
1739      *       the IKE session if needed.
1740      * </ul>
1741      *
1742      * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have
1743      * different Traffic Selectors and algorithms than the old one."
1744      */
1745     class MobikeRekeyChildLocalCreate extends RekeyChildLocalCreate {
1746         @Override
notifyCallerForLocalChildSaRekey()1747         protected void notifyCallerForLocalChildSaRekey() {
1748             IpSecTransform inTransform = mLocalInitNewChildSaRecord.getInboundIpSecTransform();
1749             IpSecTransform outTransform = mLocalInitNewChildSaRecord.getOutboundIpSecTransform();
1750             executeUserCallback(
1751                     () -> mUserCallback.onIpSecTransformsMigrated(inTransform, outTransform));
1752         }
1753 
1754         @Override
handleProcessRespOrSaCreationFailAndQuit( int registeredSpi, Exception exception)1755         protected void handleProcessRespOrSaCreationFailAndQuit(
1756                 int registeredSpi, Exception exception) {
1757             sendDeleteIkeRequest();
1758 
1759             recordMetricsEvent_sessionTerminated(wrapAsIkeException(exception));
1760             mChildSmCallback.onFatalIkeSessionError(exception);
1761         }
1762 
1763         @Override
handleErrorNotify(Exception exception)1764         protected void handleErrorNotify(Exception exception) {
1765             loge("Received error notification for rekey Child. Tear down IKE SA");
1766             sendDeleteIkeRequest();
1767 
1768             recordMetricsEvent_sessionTerminated(wrapAsIkeException(exception));
1769             mChildSmCallback.onFatalIkeSessionError(exception);
1770         }
1771 
1772         @Override
getMetricsStateCode()1773         protected @IkeMetrics.IkeState int getMetricsStateCode() {
1774             return IkeMetrics.IKE_STATE_CHILD_MOBIKE_REKEY_LOCAL_CREATE;
1775         }
1776     }
1777 
addDhGroupsFromChildSessionParamsIfAbsent()1778     private ChildSaProposal addDhGroupsFromChildSessionParamsIfAbsent() {
1779         // DH groups are excluded for the first child. Add dh groups from child session params in
1780         // this case.
1781         if (mSaProposal.getDhGroups().size() != 0) {
1782             return mSaProposal;
1783         }
1784 
1785         Set<DhGroupTransform> dhGroupSet = new LinkedHashSet<>();
1786         for (SaProposal saProposal : mChildSessionParams.getSaProposals()) {
1787             if (!mSaProposal.isNegotiatedFromExceptDhGroup(saProposal)) continue;
1788             dhGroupSet.addAll(Arrays.asList(saProposal.getDhGroupTransforms()));
1789         }
1790 
1791         DhGroupTransform[] dhGroups = new DhGroupTransform[dhGroupSet.size()];
1792         dhGroupSet.toArray(dhGroups);
1793 
1794         return new ChildSaProposal(
1795                 mSaProposal.getEncryptionTransforms(),
1796                 mSaProposal.getIntegrityTransforms(),
1797                 dhGroups,
1798                 mSaProposal.getEsnTransforms());
1799     }
1800 
1801     /**
1802      * RekeyChildRemoteCreate represents the state where Child Session receives a Rekey Child
1803      * request.
1804      *
1805      * <p>As indicated in RFC 7296 section 2.8, "when rekeying, the new Child SA SHOULD NOT have
1806      * different Traffic Selectors and algorithms than the old one."
1807      *
1808      * <p>Errors in this exchange with no specific protocol error code will all be classified to use
1809      * NO_PROPOSAL_CHOSEN. The reason that we don't use NO_ADDITIONAL_SAS is because it indicates
1810      * "responder is unwilling to accept any more Child SAs on this IKE SA.", according to RFC 7296.
1811      * Sending this error may mislead the remote peer.
1812      */
1813     class RekeyChildRemoteCreate extends ExceptionHandler {
1814         @Override
processStateMessage(Message message)1815         public boolean processStateMessage(Message message) {
1816             switch (message.what) {
1817                 case CMD_HANDLE_RECEIVED_REQUEST:
1818                     ReceivedRequest req = (ReceivedRequest) message.obj;
1819 
1820                     if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_REKEY_CHILD) {
1821                         handleCreateChildRequest(req);
1822                         return HANDLED;
1823                     }
1824 
1825                     return NOT_HANDLED;
1826                 default:
1827                     return NOT_HANDLED;
1828             }
1829         }
1830 
handleCreateChildRequest(ReceivedRequest req)1831         private void handleCreateChildRequest(ReceivedRequest req) {
1832             List<IkePayload> reqPayloads = null;
1833             List<IkePayload> respPayloads = null;
1834             try {
1835                 reqPayloads = req.requestPayloads;
1836 
1837                 // Build a rekey response payload list with our previously selected proposal,
1838                 // against which we will validate the received request. It is guaranteed in
1839                 // IkeSessionStateMachine#getIkeExchangeSubType that a SA Payload is included in the
1840                 // inbound request payload list.
1841                 IkeSaPayload reqSaPayload =
1842                         IkePayload.getPayloadForTypeInProvidedList(
1843                                 PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads);
1844 
1845                 IkeKePayload reqKePayload =
1846                         IkePayload.getPayloadForTypeInProvidedList(
1847                                 PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads);
1848 
1849                 ChildSaProposal saProposal = mSaProposal;
1850                 if (reqKePayload != null) {
1851                     saProposal =
1852                             reqSaPayload.getNegotiatedChildProposalWithDh(
1853                                     mSaProposal,
1854                                     mChildSessionParams.getChildSaProposals(),
1855                                     reqKePayload.dhGroup,
1856                                     mIkeDhGroup);
1857                 }
1858 
1859 
1860                 byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(saProposal);
1861 
1862                 respPayloads =
1863                         CreateChildSaHelper.getRekeyChildCreateRespPayloads(
1864                                 mIkeContext.getRandomnessFactory(),
1865                                 mIpSecSpiGenerator,
1866                                 mLocalAddress,
1867                                 respProposalNumber,
1868                                 saProposal,
1869                                 mLocalTs,
1870                                 mRemoteTs,
1871                                 mCurrentChildSaRecord.getLocalSpi(),
1872                                 mChildSessionParams.isTransportMode());
1873             } catch (NoValidProposalChosenException | InvalidKeException e) {
1874                 handleCreationFailureAndBackToIdle(e);
1875                 return;
1876             } catch (SpiUnavailableException | ResourceUnavailableException e) {
1877                 handleCreationFailureAndBackToIdle(
1878                         new NoValidProposalChosenException("Fail to assign inbound SPI", e));
1879                 return;
1880             }
1881 
1882             CreateChildResult createChildResult =
1883                     CreateChildSaHelper.validateAndNegotiateRekeyChildRequest(
1884                             reqPayloads,
1885                             respPayloads,
1886                             req.exchangeType /*exchangeType*/,
1887                             EXCHANGE_TYPE_CREATE_CHILD_SA /*expectedExchangeType*/,
1888                             mChildSessionParams.isTransportMode(),
1889                             mIpSecSpiGenerator,
1890                             mRemoteAddress);
1891 
1892             switch (createChildResult.status) {
1893                 case CREATE_STATUS_OK:
1894                     try {
1895                         // Do not need to update TS because they are not changed.
1896                         mSaProposal = createChildResult.negotiatedProposal;
1897 
1898                         mRemoteInitNewChildSaRecord =
1899                                 ChildSaRecord.makeChildSaRecord(
1900                                         mIkeContext.getContext(),
1901                                         reqPayloads,
1902                                         respPayloads,
1903                                         createChildResult.initSpi,
1904                                         createChildResult.respSpi,
1905                                         mLocalAddress,
1906                                         mRemoteAddress,
1907                                         mUdpEncapSocket,
1908                                         mIkePrf,
1909                                         mChildIntegrity,
1910                                         mChildCipher,
1911                                         mSkD,
1912                                         mChildSessionParams.isTransportMode(),
1913                                         false /*isLocalInit*/,
1914                                         buildSaLifetimeAlarmSched(
1915                                                 createChildResult.initSpi.getSpi()));
1916 
1917                         mChildSmCallback.onChildSaCreated(
1918                                 mRemoteInitNewChildSaRecord.getRemoteSpi(),
1919                                 ChildSessionStateMachine.this);
1920 
1921                         // To avoid traffic loss, outbound transform should only be applied once
1922                         // the remote has (implicitly) acknowledged our response via the
1923                         // delete-old-SA request. This will be performed in the finishRekey()
1924                         // method.
1925                         IpSecTransform inTransform =
1926                                 mRemoteInitNewChildSaRecord.getInboundIpSecTransform();
1927                         executeUserCallback(
1928                                 () -> {
1929                                     mUserCallback.onIpSecTransformCreated(
1930                                             inTransform, IpSecManager.DIRECTION_IN);
1931                                 });
1932 
1933                         mChildSmCallback.onOutboundPayloadsReady(
1934                                 EXCHANGE_TYPE_CREATE_CHILD_SA,
1935                                 true /*isResp*/,
1936                                 respPayloads,
1937                                 ChildSessionStateMachine.this);
1938 
1939                         List<Integer> integrityAlgorithms = mSaProposal.getIntegrityAlgorithms();
1940                         List<Integer> dhGroups = mSaProposal.getDhGroups();
1941 
1942                         recordMetricsEvent_SaNegotiation(
1943                                 dhGroups.isEmpty()
1944                                         ? IkeMetrics.DH_GROUP_UNSPECIFIED
1945                                         : dhGroups.get(0),
1946                                 mSaProposal.getEncryptionTransforms()[0].id,
1947                                 mSaProposal.getEncryptionTransforms()[0].getSpecifiedKeyLength(),
1948                                 integrityAlgorithms.isEmpty()
1949                                         ? IkeMetrics.INTEGRITY_ALGORITHM_NONE
1950                                         : integrityAlgorithms.get(0),
1951                                 IkeMetrics.PSEUDORANDOM_FUNCTION_UNSPECIFIED,
1952                                 null);
1953 
1954                         transitionTo(mRekeyChildRemoteDelete);
1955                     } catch (GeneralSecurityException
1956                             | ResourceUnavailableException
1957                             | SpiUnavailableException
1958                             | IOException e) {
1959                         // #makeChildSaRecord failed.
1960                         handleCreationFailureAndBackToIdle(
1961                                 new NoValidProposalChosenException(
1962                                         "Error in Child SA creation", e));
1963                     } finally {
1964                         // In the successful case the transform in ChildSaRecord has taken ownership
1965                         // of the SPI (in IpSecService), and will keep it alive.
1966                         createChildResult.initSpi.close();
1967                         createChildResult.respSpi.close();
1968                     }
1969                     break;
1970                 case CREATE_STATUS_CHILD_ERROR_INVALID_MSG:
1971                     IkeException error = createChildResult.exception;
1972                     if (error instanceof IkeProtocolException) {
1973                         handleCreationFailureAndBackToIdle((IkeProtocolException) error);
1974                     } else {
1975                         handleCreationFailureAndBackToIdle(
1976                                 new NoValidProposalChosenException(
1977                                         "Error in validating Create Child request", error));
1978                     }
1979                     break;
1980                 case CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY:
1981                     cleanUpAndQuit(
1982                             new IllegalStateException(
1983                                     "Unexpected processing status in Create Child request: "
1984                                             + createChildResult.status));
1985                     break;
1986                 default:
1987                     cleanUpAndQuit(
1988                             new IllegalStateException(
1989                                     "Unrecognized status: " + createChildResult.status));
1990             }
1991         }
1992 
handleCreationFailureAndBackToIdle(IkeProtocolException e)1993         private void handleCreationFailureAndBackToIdle(IkeProtocolException e) {
1994             loge("Received invalid Rekey Child request. Reject with error notification", e);
1995 
1996             ArrayList<IkePayload> payloads = new ArrayList<>(1);
1997             payloads.add(e.buildNotifyPayload());
1998             mChildSmCallback.onOutboundPayloadsReady(
1999                     EXCHANGE_TYPE_CREATE_CHILD_SA,
2000                     true /*isResp*/,
2001                     payloads,
2002                     ChildSessionStateMachine.this);
2003 
2004             transitionTo(mIdle);
2005         }
2006 
2007         @Override
getMetricsStateCode()2008         protected @IkeMetrics.IkeState int getMetricsStateCode() {
2009             return IkeMetrics.IKE_STATE_CHILD_REKEY_REMOTE_CREATE;
2010         }
2011     }
2012 
2013     /**
2014      * RekeyChildDeleteBase represents common behaviours of deleting stage during rekeying Child SA.
2015      */
2016     abstract class RekeyChildDeleteBase extends DeleteBase {
2017         @Override
processStateMessage(Message message)2018         public boolean processStateMessage(Message message) {
2019             switch (message.what) {
2020                 case CMD_HANDLE_RECEIVED_REQUEST:
2021                     try {
2022                         if (isOnNewSa((ReceivedRequest) message.obj)) {
2023                             finishRekey();
2024                             deferMessage(message);
2025                             transitionTo(mIdleWithDeferredRequest);
2026                             return HANDLED;
2027                         }
2028                         return NOT_HANDLED;
2029                     } catch (IllegalStateException e) {
2030                         cleanUpAndQuit(e);
2031                         return HANDLED;
2032                     }
2033                 default:
2034                     return NOT_HANDLED;
2035             }
2036         }
2037 
isOnNewSa(ReceivedRequest req)2038         private boolean isOnNewSa(ReceivedRequest req) {
2039             switch (req.exchangeSubtype) {
2040                 case IKE_EXCHANGE_SUBTYPE_DELETE_CHILD:
2041                     return hasRemoteChildSpiForDelete(req.requestPayloads, mChildSaRecordSurviving);
2042                 case IKE_EXCHANGE_SUBTYPE_REKEY_CHILD:
2043                     return CreateChildSaHelper.hasRemoteChildSpiForRekey(
2044                             req.requestPayloads, mChildSaRecordSurviving);
2045                 default:
2046                     throw new IllegalStateException(
2047                             "Invalid exchange subtype for Child Session: " + req.exchangeSubtype);
2048             }
2049         }
2050 
2051         // Rekey timer for old SA will be cancelled as part of the closing of the SA.
finishRekey()2052         protected void finishRekey() {
2053             OnIpSecSaPairDeletedRunnable delRunnable =
2054                     new OnIpSecSaPairDeletedRunnable(mCurrentChildSaRecord);
2055             executeUserCallback(delRunnable);
2056 
2057             mChildSmCallback.onChildSaDeleted(mCurrentChildSaRecord.getRemoteSpi());
2058             mCurrentChildSaRecord.close();
2059 
2060             mCurrentChildSaRecord = mChildSaRecordSurviving;
2061 
2062             mLocalInitNewChildSaRecord = null;
2063             mRemoteInitNewChildSaRecord = null;
2064             mChildSaRecordSurviving = null;
2065         }
2066     }
2067 
2068     /**
2069      * RekeyChildLocalDelete represents the deleting stage of a locally-initiated Rekey Child
2070      * procedure.
2071      */
2072     class RekeyChildLocalDelete extends RekeyChildDeleteBase {
2073         private boolean mSimulDeleteDetected;
2074 
2075         @Override
enterState()2076         public void enterState() {
2077             mSimulDeleteDetected = false;
2078             mChildSaRecordSurviving = mLocalInitNewChildSaRecord;
2079             sendDeleteChild(mCurrentChildSaRecord, false /*isResp*/);
2080         }
2081 
2082         @Override
processStateMessage(Message message)2083         public boolean processStateMessage(Message message) {
2084             if (super.processStateMessage(message) == HANDLED) {
2085                 return HANDLED;
2086             }
2087 
2088             switch (message.what) {
2089                 case CMD_HANDLE_RECEIVED_REQUEST:
2090                     ReceivedRequest req = (ReceivedRequest) message.obj;
2091 
2092                     if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) {
2093                         // Reply with empty message during simultaneous deleting and keep waiting
2094                         // for Delete response.
2095                         mChildSmCallback.onOutboundPayloadsReady(
2096                                 EXCHANGE_TYPE_INFORMATIONAL,
2097                                 true /*isResp*/,
2098                                 new ArrayList<>(),
2099                                 ChildSessionStateMachine.this);
2100                         mSimulDeleteDetected = true;
2101                     } else {
2102                         replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE);
2103                     }
2104                     return HANDLED;
2105                 case CMD_HANDLE_RECEIVED_RESPONSE:
2106                     try {
2107                         ReceivedResponse resp = (ReceivedResponse) message.obj;
2108                         validateDeleteRespPayloadAndExchangeType(
2109                                 resp.responsePayloads, resp.exchangeType);
2110 
2111                         boolean currentSaSpiFound =
2112                                 hasRemoteChildSpiForDelete(
2113                                         resp.responsePayloads, mCurrentChildSaRecord);
2114                         if (!mSimulDeleteDetected && !currentSaSpiFound) {
2115                             loge(
2116                                     "Found no remote SPI for current SA in received Delete"
2117                                         + " response. Shutting down old SA and finishing rekey.");
2118                         }
2119                     } catch (IkeProtocolException e) {
2120                         loge(
2121                                 "Received Delete response with invalid syntax or error"
2122                                     + " notifications. Shutting down old SA and finishing rekey.",
2123                                 e);
2124                     }
2125                     finishRekey();
2126                     transitionTo(mIdle);
2127                     return HANDLED;
2128                 default:
2129                     return NOT_HANDLED;
2130             }
2131         }
2132 
2133         @Override
getMetricsStateCode()2134         protected @IkeMetrics.IkeState int getMetricsStateCode() {
2135             return IkeMetrics.IKE_STATE_CHILD_REKEY_LOCAL_DELETE;
2136         }
2137     }
2138 
2139     /**
2140      * RekeyChildRemoteDelete represents the deleting stage of a remotely-initiated Rekey Child
2141      * procedure.
2142      */
2143     class RekeyChildRemoteDelete extends RekeyChildDeleteBase {
2144         @Override
enterState()2145         public void enterState() {
2146             mChildSaRecordSurviving = mRemoteInitNewChildSaRecord;
2147             sendMessageDelayed(TIMEOUT_REKEY_REMOTE_DELETE, REKEY_DELETE_TIMEOUT_MS);
2148         }
2149 
2150         @Override
processStateMessage(Message message)2151         public boolean processStateMessage(Message message) {
2152             if (super.processStateMessage(message) == HANDLED) {
2153                 return HANDLED;
2154             }
2155 
2156             switch (message.what) {
2157                 case CMD_HANDLE_RECEIVED_REQUEST:
2158                     ReceivedRequest req = (ReceivedRequest) message.obj;
2159 
2160                     if (req.exchangeSubtype == IKE_EXCHANGE_SUBTYPE_DELETE_CHILD) {
2161                         handleDeleteRequest(req.requestPayloads);
2162 
2163                     } else {
2164                         replyErrorNotification(ERROR_TYPE_TEMPORARY_FAILURE);
2165                     }
2166                     return HANDLED;
2167                 case TIMEOUT_REKEY_REMOTE_DELETE:
2168                     // Receiving this signal means the remote side has received the outbound
2169                     // Rekey-Create response since no retransmissions were received during the
2170                     // waiting time. IKE library will assume the remote side has set up the new
2171                     // Child SA and finish the rekey procedure. Users should be warned there is
2172                     // a risk that the remote side failed to set up the new Child SA and all
2173                     // outbound IPsec traffic protected by new Child SA will be dropped.
2174 
2175                     // TODO:Consider finishing rekey procedure if the IKE Session receives a new
2176                     // request. Since window size is one, receiving a new request indicates the
2177                     // remote side has received the outbound Rekey-Create response
2178 
2179                     finishRekey();
2180                     transitionTo(mIdle);
2181                     return HANDLED;
2182                 default:
2183                     return NOT_HANDLED;
2184             }
2185         }
2186 
handleDeleteRequest(List<IkePayload> payloads)2187         private void handleDeleteRequest(List<IkePayload> payloads) {
2188             if (!hasRemoteChildSpiForDelete(payloads, mCurrentChildSaRecord)) {
2189                 // Request received on incorrect SA
2190                 cleanUpAndQuit(
2191                         new IllegalStateException(
2192                                 "Found no remote SPI for current SA in received Delete"
2193                                         + " response."));
2194             } else {
2195                 sendDeleteChild(mCurrentChildSaRecord, true /*isResp*/);
2196                 finishRekey();
2197                 transitionTo(mIdle);
2198             }
2199         }
2200 
2201         @Override
finishRekey()2202         protected void finishRekey() {
2203             IpSecTransform outTransform = mRemoteInitNewChildSaRecord.getOutboundIpSecTransform();
2204             executeUserCallback(
2205                     () -> {
2206                         mUserCallback.onIpSecTransformCreated(
2207                                 outTransform, IpSecManager.DIRECTION_OUT);
2208                     });
2209 
2210             super.finishRekey();
2211         }
2212 
2213         @Override
exitState()2214         public void exitState() {
2215             removeMessages(TIMEOUT_REKEY_REMOTE_DELETE);
2216         }
2217 
2218         @Override
getMetricsStateCode()2219         protected @IkeMetrics.IkeState int getMetricsStateCode() {
2220             return IkeMetrics.IKE_STATE_CHILD_REKEY_REMOTE_DELETE;
2221         }
2222     }
2223 
2224     @Override
getMetricsSessionType()2225     protected @IkeMetrics.IkeSessionType int getMetricsSessionType() {
2226         return IkeMetrics.IKE_SESSION_TYPE_CHILD;
2227     }
2228 
2229     /**
2230      * Package private helper class to generate IKE SA creation payloads, in both request and
2231      * response directions.
2232      */
2233     static class CreateChildSaHelper {
2234         /** Create payload list for creating the initial Child SA for this Child Session. */
getInitChildCreateReqPayloads( RandomnessFactory randomFactory, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress, ChildSessionParams childSessionParams, boolean isFirstChildSa)2235         public static List<IkePayload> getInitChildCreateReqPayloads(
2236                 RandomnessFactory randomFactory,
2237                 IpSecSpiGenerator ipSecSpiGenerator,
2238                 InetAddress localAddress,
2239                 ChildSessionParams childSessionParams,
2240                 boolean isFirstChildSa)
2241                 throws SpiUnavailableException, ResourceUnavailableException {
2242 
2243             ChildSaProposal[] saProposals = childSessionParams.getSaProposalsInternal();
2244 
2245             if (isFirstChildSa) {
2246                 for (int i = 0; i < saProposals.length; i++) {
2247                     saProposals[i] =
2248                             childSessionParams.getSaProposalsInternal()[i]
2249                                     .getCopyWithoutDhTransform();
2250                 }
2251             }
2252 
2253             List<IkePayload> payloadList =
2254                     getChildCreatePayloads(
2255                             IkeSaPayload.createChildSaRequestPayload(
2256                                     saProposals, ipSecSpiGenerator, localAddress),
2257                             childSessionParams.getInboundTrafficSelectorsInternal(),
2258                             childSessionParams.getOutboundTrafficSelectorsInternal(),
2259                             childSessionParams.isTransportMode(),
2260                             isFirstChildSa,
2261                             randomFactory);
2262 
2263             return payloadList;
2264         }
2265 
getConfigAttributes(ChildSessionParams params)2266         public static ConfigAttribute[] getConfigAttributes(ChildSessionParams params) {
2267             if (!params.isTransportMode()) {
2268                 return ((TunnelModeChildSessionParams) params).getConfigurationAttributesInternal();
2269             }
2270             return new ConfigAttribute[0];
2271         }
2272 
2273         /** Create payload list as a rekey Child Session request. */
getRekeyChildCreateReqPayloads( RandomnessFactory randomFactory, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress, ChildSaProposal currentProposal, IkeTrafficSelector[] currentLocalTs, IkeTrafficSelector[] currentRemoteTs, int localSpi, boolean isTransport)2274         public static List<IkePayload> getRekeyChildCreateReqPayloads(
2275                 RandomnessFactory randomFactory,
2276                 IpSecSpiGenerator ipSecSpiGenerator,
2277                 InetAddress localAddress,
2278                 ChildSaProposal currentProposal,
2279                 IkeTrafficSelector[] currentLocalTs,
2280                 IkeTrafficSelector[] currentRemoteTs,
2281                 int localSpi,
2282                 boolean isTransport)
2283                 throws SpiUnavailableException, ResourceUnavailableException {
2284             List<IkePayload> payloads =
2285                     getChildCreatePayloads(
2286                             IkeSaPayload.createChildSaRequestPayload(
2287                                     new ChildSaProposal[] {currentProposal},
2288                                     ipSecSpiGenerator,
2289                                     localAddress),
2290                             currentLocalTs,
2291                             currentRemoteTs,
2292                             isTransport,
2293                             false /*isFirstChildSa*/,
2294                             randomFactory);
2295 
2296             payloads.add(
2297                     new IkeNotifyPayload(
2298                             PROTOCOL_ID_ESP, localSpi, NOTIFY_TYPE_REKEY_SA, new byte[0]));
2299             return payloads;
2300         }
2301 
2302         /** Create payload list as a rekey Child Session response. */
getRekeyChildCreateRespPayloads( RandomnessFactory randomFactory, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress, byte proposalNumber, ChildSaProposal currentProposal, IkeTrafficSelector[] currentLocalTs, IkeTrafficSelector[] currentRemoteTs, int localSpi, boolean isTransport)2303         public static List<IkePayload> getRekeyChildCreateRespPayloads(
2304                 RandomnessFactory randomFactory,
2305                 IpSecSpiGenerator ipSecSpiGenerator,
2306                 InetAddress localAddress,
2307                 byte proposalNumber,
2308                 ChildSaProposal currentProposal,
2309                 IkeTrafficSelector[] currentLocalTs,
2310                 IkeTrafficSelector[] currentRemoteTs,
2311                 int localSpi,
2312                 boolean isTransport)
2313                 throws SpiUnavailableException, ResourceUnavailableException {
2314             List<IkePayload> payloads =
2315                     getChildCreatePayloads(
2316                             IkeSaPayload.createChildSaResponsePayload(
2317                                     proposalNumber,
2318                                     currentProposal,
2319                                     ipSecSpiGenerator,
2320                                     localAddress),
2321                             currentRemoteTs /*initTs*/,
2322                             currentLocalTs /*respTs*/,
2323                             isTransport,
2324                             false /*isFirstChildSa*/,
2325                             randomFactory);
2326 
2327             payloads.add(
2328                     new IkeNotifyPayload(
2329                             PROTOCOL_ID_ESP, localSpi, NOTIFY_TYPE_REKEY_SA, new byte[0]));
2330             return payloads;
2331         }
2332 
2333         /** Create payload list for creating a new Child SA. */
getChildCreatePayloads( IkeSaPayload saPayload, IkeTrafficSelector[] initTs, IkeTrafficSelector[] respTs, boolean isTransport, boolean isFirstChildSa, RandomnessFactory randomFactory)2334         private static List<IkePayload> getChildCreatePayloads(
2335                 IkeSaPayload saPayload,
2336                 IkeTrafficSelector[] initTs,
2337                 IkeTrafficSelector[] respTs,
2338                 boolean isTransport,
2339                 boolean isFirstChildSa,
2340                 RandomnessFactory randomFactory)
2341                 throws ResourceUnavailableException {
2342             List<IkePayload> payloadList = new ArrayList<>(5);
2343 
2344             payloadList.add(saPayload);
2345             payloadList.add(new IkeTsPayload(true /*isInitiator*/, initTs));
2346             payloadList.add(new IkeTsPayload(false /*isInitiator*/, respTs));
2347 
2348             if (!isFirstChildSa) {
2349                 payloadList.add(new IkeNoncePayload(randomFactory));
2350             }
2351 
2352             DhGroupTransform[] dhGroups =
2353                     ((ChildProposal) saPayload.proposalList.get(0))
2354                             .saProposal.getDhGroupTransforms();
2355             if (dhGroups.length != 0 && dhGroups[0].id != DH_GROUP_NONE) {
2356                 payloadList.add(
2357                         IkeKePayload.createOutboundKePayload(dhGroups[0].id, randomFactory));
2358             }
2359 
2360             if (isTransport) payloadList.add(new IkeNotifyPayload(NOTIFY_TYPE_USE_TRANSPORT_MODE));
2361 
2362             return payloadList;
2363         }
2364 
2365         /**
2366          * Validate the received response of initial Create Child SA exchange and return the
2367          * negotiation result.
2368          */
validateAndNegotiateInitChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean expectTransport, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2369         public static CreateChildResult validateAndNegotiateInitChild(
2370                 List<IkePayload> reqPayloads,
2371                 List<IkePayload> respPayloads,
2372                 @ExchangeType int exchangeType,
2373                 @ExchangeType int expectedExchangeType,
2374                 boolean expectTransport,
2375                 IpSecSpiGenerator ipSecSpiGenerator,
2376                 InetAddress remoteAddress) {
2377 
2378             return validateAndNegotiateChild(
2379                     reqPayloads,
2380                     respPayloads,
2381                     exchangeType,
2382                     expectedExchangeType,
2383                     true /*isLocalInit*/,
2384                     expectTransport,
2385                     ipSecSpiGenerator,
2386                     remoteAddress);
2387         }
2388 
2389         /**
2390          * Validate the received rekey-create request against locally built response (based on
2391          * previously negotiated Child SA) and return the negotiation result.
2392          */
validateAndNegotiateRekeyChildRequest( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean expectTransport, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2393         public static CreateChildResult validateAndNegotiateRekeyChildRequest(
2394                 List<IkePayload> reqPayloads,
2395                 List<IkePayload> respPayloads,
2396                 @ExchangeType int exchangeType,
2397                 @ExchangeType int expectedExchangeType,
2398                 boolean expectTransport,
2399                 IpSecSpiGenerator ipSecSpiGenerator,
2400                 InetAddress remoteAddress) {
2401 
2402             // It is guaranteed that a Rekey-Notify Payload with remote SPI of current Child SA is
2403             // included in the reqPayloads. So we won't validate it again here.
2404             return validateAndNegotiateChild(
2405                     reqPayloads,
2406                     respPayloads,
2407                     exchangeType,
2408                     expectedExchangeType,
2409                     false /*isLocalInit*/,
2410                     expectTransport,
2411                     ipSecSpiGenerator,
2412                     remoteAddress);
2413         }
2414 
2415         /**
2416          * Validate the received rekey-create response against locally built request and previously
2417          * negotiated Child SA, and return the negotiation result.
2418          */
validateAndNegotiateRekeyChildResp( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean expectTransport, ChildSaRecord expectedChildRecord, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2419         public static CreateChildResult validateAndNegotiateRekeyChildResp(
2420                 List<IkePayload> reqPayloads,
2421                 List<IkePayload> respPayloads,
2422                 @ExchangeType int exchangeType,
2423                 @ExchangeType int expectedExchangeType,
2424                 boolean expectTransport,
2425                 ChildSaRecord expectedChildRecord,
2426                 IpSecSpiGenerator ipSecSpiGenerator,
2427                 InetAddress remoteAddress) {
2428             // Validate rest of payloads and negotiate Child SA.
2429             CreateChildResult childResult =
2430                     validateAndNegotiateChild(
2431                             reqPayloads,
2432                             respPayloads,
2433                             exchangeType,
2434                             expectedExchangeType,
2435                             true /*isLocalInit*/,
2436                             expectTransport,
2437                             ipSecSpiGenerator,
2438                             remoteAddress);
2439 
2440             // TODO: Validate new Child SA does not have different Traffic Selectors
2441 
2442             return childResult;
2443         }
2444 
2445         /**
2446          * Check if SPI of Child SA that is expected to be rekeyed is included in the provided
2447          * payload list.
2448          */
hasRemoteChildSpiForRekey( List<IkePayload> payloads, ChildSaRecord expectedRecord)2449         public static boolean hasRemoteChildSpiForRekey(
2450                 List<IkePayload> payloads, ChildSaRecord expectedRecord) {
2451             List<IkeNotifyPayload> notifyPayloads =
2452                     IkePayload.getPayloadListForTypeInProvidedList(
2453                             IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class, payloads);
2454 
2455             boolean hasExpectedRekeyNotify = false;
2456             for (IkeNotifyPayload notifyPayload : notifyPayloads) {
2457                 if (notifyPayload.notifyType == NOTIFY_TYPE_REKEY_SA
2458                         && notifyPayload.spi == expectedRecord.getRemoteSpi()) {
2459                     hasExpectedRekeyNotify = true;
2460                     break;
2461                 }
2462             }
2463 
2464             return hasExpectedRekeyNotify;
2465         }
2466 
releaseSpiResources(List<IkePayload> reqPayloads)2467         public static void releaseSpiResources(List<IkePayload> reqPayloads) {
2468             if (reqPayloads == null) {
2469                 return;
2470             }
2471 
2472             IkeSaPayload saPayload =
2473                     IkePayload.getPayloadForTypeInProvidedList(
2474                             IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads);
2475             if (saPayload != null) {
2476                 saPayload.releaseChildSpiResourcesIfExists();
2477             }
2478         }
2479 
2480         /** Validate the received payload list and negotiate Child SA. */
validateAndNegotiateChild( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType, boolean isLocalInit, boolean expectTransport, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)2481         private static CreateChildResult validateAndNegotiateChild(
2482                 List<IkePayload> reqPayloads,
2483                 List<IkePayload> respPayloads,
2484                 @ExchangeType int exchangeType,
2485                 @ExchangeType int expectedExchangeType,
2486                 boolean isLocalInit,
2487                 boolean expectTransport,
2488                 IpSecSpiGenerator ipSecSpiGenerator,
2489                 InetAddress remoteAddress) {
2490             List<IkePayload> inboundPayloads = isLocalInit ? respPayloads : reqPayloads;
2491 
2492             try {
2493                 validatePayloadAndExchangeType(
2494                         inboundPayloads,
2495                         isLocalInit /*isResp*/,
2496                         exchangeType,
2497                         expectedExchangeType);
2498             } catch (InvalidSyntaxException e) {
2499                 return new CreateChildResult(CREATE_STATUS_CHILD_ERROR_INVALID_MSG, e);
2500             }
2501 
2502             List<IkeNotifyPayload> notifyPayloads =
2503                     IkePayload.getPayloadListForTypeInProvidedList(
2504                             IkePayload.PAYLOAD_TYPE_NOTIFY,
2505                             IkeNotifyPayload.class,
2506                             inboundPayloads);
2507 
2508             boolean hasTransportNotify = false;
2509             for (IkeNotifyPayload notify : notifyPayloads) {
2510                 if (notify.isErrorNotify()) {
2511                     try {
2512                         IkeProtocolException exception = notify.validateAndBuildIkeException();
2513                         if (isLocalInit) {
2514                             return new CreateChildResult(
2515                                     CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY, exception);
2516                         } else {
2517                             logw("Received unexpected error notification: " + notify.notifyType);
2518                         }
2519                     } catch (InvalidSyntaxException e) {
2520                         return new CreateChildResult(CREATE_STATUS_CHILD_ERROR_INVALID_MSG, e);
2521                     }
2522                 }
2523 
2524                 switch (notify.notifyType) {
2525                     case IkeNotifyPayload.NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE:
2526                         // TODO: Store it as part of negotiation results that can be retrieved
2527                         // by users.
2528                         break;
2529                     case IkeNotifyPayload.NOTIFY_TYPE_IPCOMP_SUPPORTED:
2530                         // Ignore
2531                         break;
2532                     case IkeNotifyPayload.NOTIFY_TYPE_USE_TRANSPORT_MODE:
2533                         hasTransportNotify = true;
2534                         break;
2535                     case IkeNotifyPayload.NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED:
2536                         // Ignore
2537                         break;
2538                     case IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA:
2539                         // Handled in Rekey State. Ignore here.
2540                         break;
2541                     default:
2542                         // Unknown and unexpected status notifications are ignored as per RFC7296.
2543                         logw(
2544                                 "Received unknown or unexpected status notifications with notify"
2545                                         + " type: "
2546                                         + notify.notifyType);
2547                 }
2548             }
2549 
2550             Pair<ChildProposal, ChildProposal> childProposalPair = null;
2551             try {
2552                 IkeSaPayload reqSaPayload =
2553                         IkePayload.getPayloadForTypeInProvidedList(
2554                                 IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads);
2555                 IkeSaPayload respSaPayload =
2556                         IkePayload.getPayloadForTypeInProvidedList(
2557                                 IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class, respPayloads);
2558 
2559                 // This method either throws exception or returns non-null pair that contains two
2560                 // valid {@link ChildProposal} both with a {@link SecurityParameterIndex} allocated
2561                 // inside.
2562                 childProposalPair =
2563                         IkeSaPayload.getVerifiedNegotiatedChildProposalPair(
2564                                 reqSaPayload, respSaPayload, ipSecSpiGenerator, remoteAddress);
2565                 ChildSaProposal saProposal = childProposalPair.second.saProposal;
2566 
2567                 validateKePayloads(inboundPayloads, isLocalInit /*isResp*/, saProposal);
2568 
2569                 if (expectTransport != hasTransportNotify) {
2570                     throw new NoValidProposalChosenException(
2571                             "Failed the negotiation on Child SA mode (conflicting modes chosen).");
2572                 }
2573 
2574                 Pair<IkeTrafficSelector[], IkeTrafficSelector[]> tsPair =
2575                         validateAndGetNegotiatedTsPair(reqPayloads, respPayloads);
2576 
2577                 return new CreateChildResult(
2578                         childProposalPair.first.getChildSpiResource(),
2579                         childProposalPair.second.getChildSpiResource(),
2580                         saProposal,
2581                         tsPair.first,
2582                         tsPair.second);
2583             } catch (IkeProtocolException
2584                     | ResourceUnavailableException
2585                     | SpiUnavailableException e) {
2586                 if (childProposalPair != null) {
2587                     childProposalPair.first.getChildSpiResource().close();
2588                     childProposalPair.second.getChildSpiResource().close();
2589                 }
2590 
2591                 if (e instanceof InvalidSyntaxException) {
2592                     return new CreateChildResult(
2593                             CREATE_STATUS_CHILD_ERROR_INVALID_MSG, (InvalidSyntaxException) e);
2594                 } else if (e instanceof IkeProtocolException) {
2595                     return new CreateChildResult(
2596                             CREATE_STATUS_CHILD_ERROR_INVALID_MSG,
2597                             new InvalidSyntaxException(
2598                                     "Processing error in received Create Child response", e));
2599                 } else {
2600                     return new CreateChildResult(
2601                             CREATE_STATUS_CHILD_ERROR_INVALID_MSG, wrapAsIkeException(e));
2602                 }
2603             }
2604         }
2605 
2606         // Validate syntax to make sure all necessary payloads exist and exchange type is correct.
validatePayloadAndExchangeType( List<IkePayload> inboundPayloads, boolean isResp, @ExchangeType int exchangeType, @ExchangeType int expectedExchangeType)2607         private static void validatePayloadAndExchangeType(
2608                 List<IkePayload> inboundPayloads,
2609                 boolean isResp,
2610                 @ExchangeType int exchangeType,
2611                 @ExchangeType int expectedExchangeType)
2612                 throws InvalidSyntaxException {
2613             boolean hasSaPayload = false;
2614             boolean hasKePayload = false;
2615             boolean hasNoncePayload = false;
2616             boolean hasTsInitPayload = false;
2617             boolean hasTsRespPayload = false;
2618             boolean hasErrorNotify = false;
2619 
2620             for (IkePayload payload : inboundPayloads) {
2621                 switch (payload.payloadType) {
2622                     case PAYLOAD_TYPE_SA:
2623                         hasSaPayload = true;
2624                         break;
2625                     case PAYLOAD_TYPE_KE:
2626                         // Could not decide if KE Payload MUST or MUST NOT be included until SA
2627                         // negotiation is done.
2628                         hasKePayload = true;
2629                         break;
2630                     case PAYLOAD_TYPE_NONCE:
2631                         hasNoncePayload = true;
2632                         break;
2633                     case PAYLOAD_TYPE_TS_INITIATOR:
2634                         hasTsInitPayload = true;
2635                         break;
2636                     case PAYLOAD_TYPE_TS_RESPONDER:
2637                         hasTsRespPayload = true;
2638                         break;
2639                     case PAYLOAD_TYPE_NOTIFY:
2640                         if (((IkeNotifyPayload) payload).isErrorNotify()) hasErrorNotify = true;
2641                         // Do not have enough context to handle all notifications. Handle them
2642                         // together in higher layer.
2643                         break;
2644                     case PAYLOAD_TYPE_CP:
2645                         // Handled in child creation state. Note Child Session can only handle
2646                         // Config Payload in initial creation and can only handle a Config Reply.
2647                         // For interoperability, Config Payloads received in rekey creation
2648                         // or with other config types will be ignored.
2649                         break;
2650                     default:
2651                         logw(
2652                                 "Received unexpected payload in Create Child SA message. Payload"
2653                                         + " type: "
2654                                         + payload.payloadType);
2655                 }
2656             }
2657 
2658             // Do not need to check exchange type of a request because it has been already verified
2659             // in IkeSessionStateMachine
2660             if (isResp
2661                     && exchangeType != expectedExchangeType
2662                     && exchangeType != EXCHANGE_TYPE_INFORMATIONAL) {
2663                 throw new InvalidSyntaxException("Received invalid exchange type: " + exchangeType);
2664             }
2665 
2666             if (exchangeType == EXCHANGE_TYPE_INFORMATIONAL
2667                     && (hasSaPayload
2668                             || hasKePayload
2669                             || hasNoncePayload
2670                             || hasTsInitPayload
2671                             || hasTsRespPayload)) {
2672                 logw(
2673                         "Unexpected payload found in an INFORMATIONAL message: SA, KE, Nonce,"
2674                                 + " TS-Initiator or TS-Responder");
2675             }
2676 
2677             if (isResp
2678                     && !hasErrorNotify
2679                     && (!hasSaPayload
2680                             || !hasNoncePayload
2681                             || !hasTsInitPayload
2682                             || !hasTsRespPayload)) {
2683                 throw new InvalidSyntaxException(
2684                         "SA, Nonce, TS-Initiator or TS-Responder missing.");
2685             }
2686         }
2687 
2688         private static Pair<IkeTrafficSelector[], IkeTrafficSelector[]>
validateAndGetNegotiatedTsPair( List<IkePayload> reqPayloads, List<IkePayload> respPayloads)2689                 validateAndGetNegotiatedTsPair(
2690                         List<IkePayload> reqPayloads, List<IkePayload> respPayloads)
2691                         throws TsUnacceptableException {
2692             IkeTrafficSelector[] initTs =
2693                     validateAndGetNegotiatedTs(reqPayloads, respPayloads, true /*isInitTs*/);
2694             IkeTrafficSelector[] respTs =
2695                     validateAndGetNegotiatedTs(reqPayloads, respPayloads, false /*isInitTs*/);
2696 
2697             return new Pair<IkeTrafficSelector[], IkeTrafficSelector[]>(initTs, respTs);
2698         }
2699 
validateAndGetNegotiatedTs( List<IkePayload> reqPayloads, List<IkePayload> respPayloads, boolean isInitTs)2700         private static IkeTrafficSelector[] validateAndGetNegotiatedTs(
2701                 List<IkePayload> reqPayloads, List<IkePayload> respPayloads, boolean isInitTs)
2702                 throws TsUnacceptableException {
2703             int tsType = isInitTs ? PAYLOAD_TYPE_TS_INITIATOR : PAYLOAD_TYPE_TS_RESPONDER;
2704             IkeTsPayload reqPayload =
2705                     IkePayload.getPayloadForTypeInProvidedList(
2706                             tsType, IkeTsPayload.class, reqPayloads);
2707             IkeTsPayload respPayload =
2708                     IkePayload.getPayloadForTypeInProvidedList(
2709                             tsType, IkeTsPayload.class, respPayloads);
2710 
2711             if (!reqPayload.contains(respPayload)) {
2712                 throw new TsUnacceptableException();
2713             }
2714 
2715             // It is guaranteed by decoding inbound TS Payload and constructing outbound TS Payload
2716             // that each TS Payload has at least one IkeTrafficSelector.
2717             return respPayload.trafficSelectors;
2718         }
2719 
2720         @VisibleForTesting
validateKePayloads( List<IkePayload> inboundPayloads, boolean isResp, ChildSaProposal negotiatedProposal)2721         static void validateKePayloads(
2722                 List<IkePayload> inboundPayloads,
2723                 boolean isResp,
2724                 ChildSaProposal negotiatedProposal)
2725                 throws IkeProtocolException {
2726             DhGroupTransform[] dhTransforms = negotiatedProposal.getDhGroupTransforms();
2727 
2728             if (dhTransforms.length > 1) {
2729                 throw new IllegalArgumentException(
2730                         "Found multiple DH Group Transforms in the negotiated SA proposal");
2731             }
2732             boolean expectKePayload =
2733                     dhTransforms.length == 1 && dhTransforms[0].id != DH_GROUP_NONE;
2734 
2735             IkeKePayload kePayload =
2736                     IkePayload.getPayloadForTypeInProvidedList(
2737                             PAYLOAD_TYPE_KE, IkeKePayload.class, inboundPayloads);
2738 
2739             if (expectKePayload && (kePayload == null || dhTransforms[0].id != kePayload.dhGroup)) {
2740                 if (isResp) {
2741                     throw new InvalidSyntaxException(
2742                             "KE Payload missing or has mismatched DH Group with the negotiated"
2743                                     + " proposal.");
2744                 } else {
2745                     throw new InvalidKeException(dhTransforms[0].id);
2746                 }
2747 
2748             } else if (!expectKePayload && kePayload != null && isResp) {
2749                 // It is valid when the remote request proposed multiple DH Groups with a KE
2750                 // payload, and the responder chose DH_GROUP_NONE.
2751                 throw new InvalidSyntaxException("Received unexpected KE Payload.");
2752             }
2753         }
2754 
logw(String s)2755         private static void logw(String s) {
2756             getIkeLog().w(TAG, s);
2757         }
2758     }
2759 
2760     @Retention(RetentionPolicy.SOURCE)
2761     @IntDef({
2762         CREATE_STATUS_OK,
2763         CREATE_STATUS_CHILD_ERROR_INVALID_MSG,
2764         CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY
2765     })
2766     @interface CreateStatus {}
2767 
2768     /** The Child SA negotiation succeeds. */
2769     private static final int CREATE_STATUS_OK = 0;
2770     /** The inbound message is invalid in Child negotiation but is non-fatal for IKE Session. */
2771     private static final int CREATE_STATUS_CHILD_ERROR_INVALID_MSG = 1;
2772     /** The inbound message includes error notification that failed the Child negotiation. */
2773     private static final int CREATE_STATUS_CHILD_ERROR_RCV_NOTIFY = 2;
2774 
2775     private static class CreateChildResult {
2776         @CreateStatus public final int status;
2777         public final SecurityParameterIndex initSpi;
2778         public final SecurityParameterIndex respSpi;
2779         public final ChildSaProposal negotiatedProposal;
2780         public final IkeTrafficSelector[] initTs;
2781         public final IkeTrafficSelector[] respTs;
2782         public final IkeException exception;
2783 
CreateChildResult( @reateStatus int status, SecurityParameterIndex initSpi, SecurityParameterIndex respSpi, ChildSaProposal negotiatedProposal, IkeTrafficSelector[] initTs, IkeTrafficSelector[] respTs, IkeException exception)2784         private CreateChildResult(
2785                 @CreateStatus int status,
2786                 SecurityParameterIndex initSpi,
2787                 SecurityParameterIndex respSpi,
2788                 ChildSaProposal negotiatedProposal,
2789                 IkeTrafficSelector[] initTs,
2790                 IkeTrafficSelector[] respTs,
2791                 IkeException exception) {
2792             this.status = status;
2793             this.initSpi = initSpi;
2794             this.respSpi = respSpi;
2795             this.negotiatedProposal = negotiatedProposal;
2796             this.initTs = initTs;
2797             this.respTs = respTs;
2798             this.exception = exception;
2799         }
2800 
2801         /* Construct a CreateChildResult instance for a successful case. */
CreateChildResult( SecurityParameterIndex initSpi, SecurityParameterIndex respSpi, ChildSaProposal negotiatedProposal, IkeTrafficSelector[] initTs, IkeTrafficSelector[] respTs)2802         CreateChildResult(
2803                 SecurityParameterIndex initSpi,
2804                 SecurityParameterIndex respSpi,
2805                 ChildSaProposal negotiatedProposal,
2806                 IkeTrafficSelector[] initTs,
2807                 IkeTrafficSelector[] respTs) {
2808             this(
2809                     CREATE_STATUS_OK,
2810                     initSpi,
2811                     respSpi,
2812                     negotiatedProposal,
2813                     initTs,
2814                     respTs,
2815                     null /*exception*/);
2816         }
2817 
2818         /** Construct a CreateChildResult instance for an error case. */
CreateChildResult(@reateStatus int status, IkeException exception)2819         CreateChildResult(@CreateStatus int status, IkeException exception) {
2820             this(
2821                     status,
2822                     null /*initSpi*/,
2823                     null /*respSpi*/,
2824                     null /*negotiatedProposal*/,
2825                     null /*initTs*/,
2826                     null /*respTs*/,
2827                     exception);
2828         }
2829     }
2830 }
2831