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