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