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