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