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