• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.ipsec.ike;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.SuppressLint;
22 import android.annotation.SystemApi;
23 import android.net.InetAddresses;
24 import android.net.vcn.util.PersistableBundleUtils;
25 import android.os.PersistableBundle;
26 
27 import java.net.InetAddress;
28 import java.util.Arrays;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.concurrent.TimeUnit;
33 
34 /**
35  * ChildSessionParams is an abstract class that represents proposed configurations for negotiating a
36  * Child Session.
37  *
38  * <p>Note that references to negotiated configurations will be held, and the same parameters will
39  * be reused during rekey. This includes SA Proposals, lifetimes and traffic selectors.
40  *
41  * <p>IKE library will send out KE payload only if user has configured one or more DH groups. The KE
42  * payload in a request will use the first DH group from the first user provided SA proposal (or the
43  * peer selected SA proposal if it's a rekey request). The KE payload in a response will depend on
44  * the SA proposal negotiation result.
45  *
46  * <p>When requesting the first Child Session in IKE AUTH, IKE library will not propose any DH group
47  * even if user has configured it, as per RFC 7296. When rekeying this child session, IKE library
48  * will accept DH groups that are configured in its ChildSessionParams. If after rekeying user needs
49  * to have the same DH group as that of the IKE Session, then they need to explicitly set the same
50  * DH Group in ChildSessionParams.
51  *
52  * <p>@see {@link TunnelModeChildSessionParams} and {@link TransportModeChildSessionParams}
53  */
54 public abstract class ChildSessionParams {
55     /** @hide */
56     protected static final int CHILD_HARD_LIFETIME_SEC_MINIMUM = 300; // 5 minutes
57     /** @hide */
58     protected static final int CHILD_HARD_LIFETIME_SEC_MAXIMUM = 14400; // 4 hours
59     /** @hide */
60     protected static final int CHILD_HARD_LIFETIME_SEC_DEFAULT = 7200; // 2 hours
61 
62     /** @hide */
63     protected static final int CHILD_SOFT_LIFETIME_SEC_MINIMUM = 120; // 2 minutes
64     /** @hide */
65     protected static final int CHILD_SOFT_LIFETIME_SEC_DEFAULT = 3600; // 1 hour
66 
67     /** @hide */
68     protected static final int CHILD_LIFETIME_MARGIN_SEC_MINIMUM =
69             (int) TimeUnit.MINUTES.toSeconds(1L);
70 
71     @NonNull private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV4;
72     @NonNull private static final IkeTrafficSelector DEFAULT_TRAFFIC_SELECTOR_IPV6;
73 
74     static {
75         DEFAULT_TRAFFIC_SELECTOR_IPV4 =
76                 buildDefaultTrafficSelector(
77                         IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE);
78         DEFAULT_TRAFFIC_SELECTOR_IPV6 =
79                 buildDefaultTrafficSelector(
80                         IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE);
81     }
82 
83     private static final String IS_TRANPORT_KEY = "mIsTransport";
84     /** @hide */
85     protected static final String INBOUND_TS_KEY = "mInboundTrafficSelectors";
86     /** @hide */
87     protected static final String OUTBOUND_TS_KEY = "mOutboundTrafficSelectors";
88     /** @hide */
89     protected static final String SA_PROPOSALS_KEY = "mSaProposals";
90     /** @hide */
91     protected static final String HARD_LIFETIME_SEC_KEY = "mHardLifetimeSec";
92     /** @hide */
93     protected static final String SOFT_LIFETIME_SEC_KEY = "mSoftLifetimeSec";
94 
95     @NonNull private final IkeTrafficSelector[] mInboundTrafficSelectors;
96     @NonNull private final IkeTrafficSelector[] mOutboundTrafficSelectors;
97     @NonNull private final ChildSaProposal[] mSaProposals;
98 
99     private final int mHardLifetimeSec;
100     private final int mSoftLifetimeSec;
101 
102     private final boolean mIsTransport;
103 
104     /** @hide */
ChildSessionParams( IkeTrafficSelector[] inboundTs, IkeTrafficSelector[] outboundTs, ChildSaProposal[] proposals, int hardLifetimeSec, int softLifetimeSec, boolean isTransport)105     protected ChildSessionParams(
106             IkeTrafficSelector[] inboundTs,
107             IkeTrafficSelector[] outboundTs,
108             ChildSaProposal[] proposals,
109             int hardLifetimeSec,
110             int softLifetimeSec,
111             boolean isTransport) {
112         mInboundTrafficSelectors = inboundTs;
113         mOutboundTrafficSelectors = outboundTs;
114         mSaProposals = proposals;
115         mHardLifetimeSec = hardLifetimeSec;
116         mSoftLifetimeSec = softLifetimeSec;
117         mIsTransport = isTransport;
118     }
119 
120     /**
121      * Constructs this object by deserializing a PersistableBundle
122      *
123      * @hide
124      */
125     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)126     public static ChildSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
127         Objects.requireNonNull(in, "PersistableBundle is null");
128 
129         if (in.getBoolean(IS_TRANPORT_KEY)) {
130             return TransportModeChildSessionParams.fromPersistableBundle(in);
131         } else {
132             return TunnelModeChildSessionParams.fromPersistableBundle(in);
133         }
134     }
135 
136     /**
137      * Serializes this object to a PersistableBundle
138      *
139      * @hide
140      */
141     @NonNull
toPersistableBundle()142     public PersistableBundle toPersistableBundle() {
143         final PersistableBundle result = new PersistableBundle();
144 
145         result.putBoolean(IS_TRANPORT_KEY, mIsTransport);
146 
147         PersistableBundle saProposalBundle =
148                 PersistableBundleUtils.fromList(
149                         Arrays.asList(mSaProposals), ChildSaProposal::toPersistableBundle);
150         result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
151 
152         PersistableBundle inTsBundle =
153                 PersistableBundleUtils.fromList(
154                         Arrays.asList(mInboundTrafficSelectors),
155                         IkeTrafficSelector::toPersistableBundle);
156         result.putPersistableBundle(INBOUND_TS_KEY, inTsBundle);
157 
158         PersistableBundle outTsBundle =
159                 PersistableBundleUtils.fromList(
160                         Arrays.asList(mOutboundTrafficSelectors),
161                         IkeTrafficSelector::toPersistableBundle);
162         result.putPersistableBundle(OUTBOUND_TS_KEY, outTsBundle);
163 
164         result.putInt(HARD_LIFETIME_SEC_KEY, mHardLifetimeSec);
165         result.putInt(SOFT_LIFETIME_SEC_KEY, mSoftLifetimeSec);
166         return result;
167     }
168 
169     /** @hide */
getProposalsFromPersistableBundle(PersistableBundle in)170     protected static List<ChildSaProposal> getProposalsFromPersistableBundle(PersistableBundle in) {
171         PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
172         Objects.requireNonNull(proposalBundle, "Value for key " + SA_PROPOSALS_KEY + " was null");
173         return PersistableBundleUtils.toList(
174                 proposalBundle, ChildSaProposal::fromPersistableBundle);
175     }
176 
177     /** @hide */
getTsFromPersistableBundle( PersistableBundle in, String key)178     protected static List<IkeTrafficSelector> getTsFromPersistableBundle(
179             PersistableBundle in, String key) {
180         PersistableBundle tsBundle = in.getPersistableBundle(key);
181         Objects.requireNonNull(tsBundle, "Value for key " + key + " was null");
182         return PersistableBundleUtils.toList(tsBundle, IkeTrafficSelector::fromPersistableBundle);
183     }
184 
185     /**
186      * Retrieves configured inbound traffic selectors
187      *
188      * <p>@see {@link
189      * TunnelModeChildSessionParams.Builder#addInboundTrafficSelectors(IkeTrafficSelector)} or
190      * {@link
191      * TransportModeChildSessionParams.Builder#addInboundTrafficSelectors(IkeTrafficSelector)}
192      */
193     @NonNull
getInboundTrafficSelectors()194     public List<IkeTrafficSelector> getInboundTrafficSelectors() {
195         return Arrays.asList(mInboundTrafficSelectors);
196     }
197 
198     /**
199      * Retrieves configured outbound traffic selectors
200      *
201      * <p>@see {@link
202      * TunnelModeChildSessionParams.Builder#addOutboundTrafficSelectors(IkeTrafficSelector)} or
203      * {@link
204      * TransportModeChildSessionParams.Builder#addOutboundTrafficSelectors(IkeTrafficSelector)}
205      */
206     @NonNull
getOutboundTrafficSelectors()207     public List<IkeTrafficSelector> getOutboundTrafficSelectors() {
208         return Arrays.asList(mOutboundTrafficSelectors);
209     }
210 
211     /**
212      * Retrieves all ChildSaProposals configured
213      *
214      * @deprecated Callers should use {@link #getChildSaProposals()}. This method is deprecated
215      *     because its name does not match the return type,
216      * @hide
217      */
218     @Deprecated
219     @SystemApi
220     @NonNull
getSaProposals()221     public List<ChildSaProposal> getSaProposals() {
222         return getChildSaProposals();
223     }
224 
225     /** Retrieves all ChildSaProposals configured */
226     @NonNull
getChildSaProposals()227     public List<ChildSaProposal> getChildSaProposals() {
228         return Arrays.asList(mSaProposals);
229     }
230 
231     /** Retrieves hard lifetime in seconds */
232     // Use "second" because smaller unit won't make sense to describe a rekey interval.
233     @SuppressLint("MethodNameUnits")
234     @IntRange(from = CHILD_HARD_LIFETIME_SEC_MINIMUM, to = CHILD_HARD_LIFETIME_SEC_MAXIMUM)
getHardLifetimeSeconds()235     public int getHardLifetimeSeconds() {
236         return mHardLifetimeSec;
237     }
238 
239     /** Retrieves soft lifetime in seconds */
240     // Use "second" because smaller unit won't make sense to describe a rekey interval.
241     @SuppressLint("MethodNameUnits")
242     @IntRange(from = CHILD_SOFT_LIFETIME_SEC_MINIMUM, to = CHILD_HARD_LIFETIME_SEC_MAXIMUM)
getSoftLifetimeSeconds()243     public int getSoftLifetimeSeconds() {
244         return mSoftLifetimeSec;
245     }
246 
247     /** @hide */
getInboundTrafficSelectorsInternal()248     public IkeTrafficSelector[] getInboundTrafficSelectorsInternal() {
249         return Arrays.copyOf(mInboundTrafficSelectors, mInboundTrafficSelectors.length);
250     }
251 
252     /** @hide */
getOutboundTrafficSelectorsInternal()253     public IkeTrafficSelector[] getOutboundTrafficSelectorsInternal() {
254         return Arrays.copyOf(mOutboundTrafficSelectors, mOutboundTrafficSelectors.length);
255     }
256 
257     /** @hide */
getSaProposalsInternal()258     public ChildSaProposal[] getSaProposalsInternal() {
259         return Arrays.copyOf(mSaProposals, mSaProposals.length);
260     }
261 
262     /** @hide */
getHardLifetimeMsInternal()263     public long getHardLifetimeMsInternal() {
264         return TimeUnit.SECONDS.toMillis((long) mHardLifetimeSec);
265     }
266 
267     /** @hide */
getSoftLifetimeMsInternal()268     public long getSoftLifetimeMsInternal() {
269         return TimeUnit.SECONDS.toMillis((long) mSoftLifetimeSec);
270     }
271 
272     /** @hide */
isTransportMode()273     public boolean isTransportMode() {
274         return mIsTransport;
275     }
276 
277     @Override
hashCode()278     public int hashCode() {
279         return Objects.hash(
280                 Arrays.hashCode(mInboundTrafficSelectors),
281                 Arrays.hashCode(mOutboundTrafficSelectors),
282                 Arrays.hashCode(mSaProposals),
283                 mHardLifetimeSec,
284                 mSoftLifetimeSec,
285                 mIsTransport);
286     }
287 
288     @Override
equals(Object o)289     public boolean equals(Object o) {
290         if (!(o instanceof ChildSessionParams)) {
291             return false;
292         }
293 
294         ChildSessionParams other = (ChildSessionParams) o;
295 
296         return Arrays.equals(mInboundTrafficSelectors, other.mInboundTrafficSelectors)
297                 && Arrays.equals(mOutboundTrafficSelectors, other.mOutboundTrafficSelectors)
298                 && Arrays.equals(mSaProposals, other.mSaProposals)
299                 && mHardLifetimeSec == other.mHardLifetimeSec
300                 && mSoftLifetimeSec == other.mSoftLifetimeSec
301                 && mIsTransport == other.mIsTransport;
302     }
303 
304     /**
305      * This class represents common information for Child Session Parameters Builders.
306      *
307      * @hide
308      */
309     protected abstract static class Builder {
310         @NonNull protected final List<IkeTrafficSelector> mInboundTsList = new LinkedList<>();
311         @NonNull protected final List<IkeTrafficSelector> mOutboundTsList = new LinkedList<>();
312         @NonNull protected final List<SaProposal> mSaProposalList = new LinkedList<>();
313 
314         protected int mHardLifetimeSec = CHILD_HARD_LIFETIME_SEC_DEFAULT;
315         protected int mSoftLifetimeSec = CHILD_SOFT_LIFETIME_SEC_DEFAULT;
316 
317         /** Package private constructor */
Builder()318         Builder() {}
319 
320         /** Package private constructor */
Builder(@onNull ChildSessionParams childParams)321         Builder(@NonNull ChildSessionParams childParams) {
322             Objects.requireNonNull(childParams, "childParams was null");
323 
324             mInboundTsList.addAll(childParams.getInboundTrafficSelectors());
325             mOutboundTsList.addAll(childParams.getOutboundTrafficSelectors());
326             mSaProposalList.addAll(childParams.getSaProposals());
327             mHardLifetimeSec = childParams.getHardLifetimeSeconds();
328             mSoftLifetimeSec = childParams.getSoftLifetimeSeconds();
329         }
330 
addProposal(@onNull ChildSaProposal proposal)331         protected void addProposal(@NonNull ChildSaProposal proposal) {
332             mSaProposalList.add(proposal);
333         }
334 
addInboundTs(@onNull IkeTrafficSelector trafficSelector)335         protected void addInboundTs(@NonNull IkeTrafficSelector trafficSelector) {
336             mInboundTsList.add(trafficSelector);
337         }
338 
addOutboundTs(@onNull IkeTrafficSelector trafficSelector)339         protected void addOutboundTs(@NonNull IkeTrafficSelector trafficSelector) {
340             mOutboundTsList.add(trafficSelector);
341         }
342 
validateAndSetLifetime(int hardLifetimeSec, int softLifetimeSec)343         protected void validateAndSetLifetime(int hardLifetimeSec, int softLifetimeSec) {
344             if (hardLifetimeSec < CHILD_HARD_LIFETIME_SEC_MINIMUM
345                     || hardLifetimeSec > CHILD_HARD_LIFETIME_SEC_MAXIMUM
346                     || softLifetimeSec < CHILD_SOFT_LIFETIME_SEC_MINIMUM
347                     || hardLifetimeSec - softLifetimeSec < CHILD_LIFETIME_MARGIN_SEC_MINIMUM) {
348                 throw new IllegalArgumentException("Invalid lifetime value");
349             }
350         }
351 
validateOrThrow()352         protected void validateOrThrow() {
353             if (mSaProposalList.isEmpty()) {
354                 throw new IllegalArgumentException(
355                         "ChildSessionParams requires at least one Child SA proposal.");
356             }
357         }
358 
addDefaultTsIfNotConfigured()359         protected void addDefaultTsIfNotConfigured() {
360             if (mInboundTsList.isEmpty()) {
361                 mInboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4);
362                 mInboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV6);
363             }
364 
365             if (mOutboundTsList.isEmpty()) {
366                 mOutboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV4);
367                 mOutboundTsList.add(DEFAULT_TRAFFIC_SELECTOR_IPV6);
368             }
369         }
370     }
371 
buildDefaultTrafficSelector( @keTrafficSelector.TrafficSelectorType int tsType)372     private static IkeTrafficSelector buildDefaultTrafficSelector(
373             @IkeTrafficSelector.TrafficSelectorType int tsType) {
374         int startPort = IkeTrafficSelector.PORT_NUMBER_MIN;
375         int endPort = IkeTrafficSelector.PORT_NUMBER_MAX;
376         InetAddress startAddress = null;
377         InetAddress endAddress = null;
378         switch (tsType) {
379             case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV4_ADDR_RANGE:
380                 startAddress = InetAddresses.parseNumericAddress("0.0.0.0");
381                 endAddress = InetAddresses.parseNumericAddress("255.255.255.255");
382                 break;
383             case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE:
384                 startAddress = InetAddresses.parseNumericAddress("::");
385                 endAddress =
386                         InetAddresses.parseNumericAddress(
387                                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
388                 break;
389             default:
390                 throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType);
391         }
392 
393         return new IkeTrafficSelector(tsType, startPort, endPort, startAddress, endAddress);
394     }
395 }
396