2417 lines
97 KiB
Java
2417 lines
97 KiB
Java
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.net.ipsec.ike;
|
|
|
|
import static android.system.OsConstants.AF_INET;
|
|
import static android.system.OsConstants.AF_INET6;
|
|
|
|
import static com.android.internal.net.ipsec.ike.utils.IkeCertUtils.certificateFromByteArray;
|
|
import static com.android.internal.net.ipsec.ike.utils.IkeCertUtils.privateKeyFromByteArray;
|
|
|
|
import android.annotation.FlaggedApi;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.IntRange;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SuppressLint;
|
|
import android.annotation.SystemApi;
|
|
import android.content.Context;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.Network;
|
|
import android.net.eap.EapSessionConfig;
|
|
import android.net.ipsec.ike.ike3gpp.Ike3gppExtension;
|
|
import android.os.PersistableBundle;
|
|
import android.util.SparseArray;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute;
|
|
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Pcscf;
|
|
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Pcscf;
|
|
import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.IkeConfigAttribute;
|
|
import com.android.internal.net.ipsec.ike.message.IkePayload;
|
|
import com.android.modules.utils.build.SdkLevel;
|
|
import com.android.server.vcn.util.PersistableBundleUtils;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.net.Inet4Address;
|
|
import java.net.Inet6Address;
|
|
import java.net.InetAddress;
|
|
import java.security.PrivateKey;
|
|
import java.security.cert.CertificateEncodingException;
|
|
import java.security.cert.TrustAnchor;
|
|
import java.security.cert.X509Certificate;
|
|
import java.security.interfaces.RSAKey;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.Set;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* IkeSessionParams contains all user provided configurations for negotiating an {@link IkeSession}.
|
|
*
|
|
* <p>Note that all negotiated configurations will be reused during rekey including SA Proposal and
|
|
* lifetime.
|
|
*/
|
|
public final class IkeSessionParams {
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({IKE_AUTH_METHOD_PSK, IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, IKE_AUTH_METHOD_EAP})
|
|
public @interface IkeAuthMethod {}
|
|
|
|
// Constants to describe user configured authentication methods.
|
|
/** @hide */
|
|
public static final int IKE_AUTH_METHOD_PSK = 1;
|
|
/** @hide */
|
|
public static final int IKE_AUTH_METHOD_PUB_KEY_SIGNATURE = 2;
|
|
/** @hide */
|
|
public static final int IKE_AUTH_METHOD_EAP = 3;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({AUTH_DIRECTION_LOCAL, AUTH_DIRECTION_REMOTE, AUTH_DIRECTION_BOTH})
|
|
public @interface AuthDirection {}
|
|
|
|
// Constants to describe which side (local and/or remote) the authentication configuration will
|
|
// be used.
|
|
/** @hide */
|
|
public static final int AUTH_DIRECTION_LOCAL = 1;
|
|
/** @hide */
|
|
public static final int AUTH_DIRECTION_REMOTE = 2;
|
|
/** @hide */
|
|
public static final int AUTH_DIRECTION_BOTH = 3;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({
|
|
IKE_OPTION_ACCEPT_ANY_REMOTE_ID,
|
|
IKE_OPTION_EAP_ONLY_AUTH,
|
|
IKE_OPTION_MOBIKE,
|
|
IKE_OPTION_FORCE_PORT_4500,
|
|
IKE_OPTION_INITIAL_CONTACT,
|
|
IKE_OPTION_REKEY_MOBILITY,
|
|
IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION,
|
|
IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES,
|
|
IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF
|
|
})
|
|
public @interface IkeOption {}
|
|
|
|
/**
|
|
* If set, the IKE library will accept any remote (server) identity, even if it does not match
|
|
* the configured remote identity
|
|
*
|
|
* <p>See {@link Builder#setRemoteIdentification(IkeIdentification)}
|
|
*/
|
|
public static final int IKE_OPTION_ACCEPT_ANY_REMOTE_ID = 0;
|
|
/**
|
|
* If set, and EAP has been configured as the authentication method, the IKE library will
|
|
* request that the remote (also) use an EAP-only authentication flow.
|
|
*
|
|
* <p>@see {@link Builder#setAuthEap(X509Certificate, EapSessionConfig)}
|
|
*/
|
|
public static final int IKE_OPTION_EAP_ONLY_AUTH = 1;
|
|
|
|
/**
|
|
* If set, the IKE Session will attempt to handle IP address changes using RFC4555 MOBIKE.
|
|
*
|
|
* <p>Upon IP address changes (including Network changes), the IKE session will initiate an RFC
|
|
* 4555 MOBIKE procedure, migrating both this IKE Session and associated IPsec Transforms to the
|
|
* new local and remote address pair.
|
|
*
|
|
* <p>The IKE library will first attempt to enable MOBIKE to handle the changes of underlying
|
|
* network and addresses. For callers targeting SDK {@link android.os.Build.VERSION_CODES#S_V2}
|
|
* and earlier, this option will implicitly enable the support for rekey-based mobility, and
|
|
* thus if the server does not support MOBIKE, the IKE Session will try migration by rekeying
|
|
* all associated IPsec SAs. This rekey-based mobility feature is not best-practice and has
|
|
* technical issues; accordingly, it will no longer be enabled for callers targeting SDK {@link
|
|
* android.os.Build.VERSION_CODES#TIRAMISU} and above.
|
|
*
|
|
* <p>Checking whether or not MOBIKE is supported by both the IKE library and the server in an
|
|
* IKE Session is done via {@link IkeSessionConfiguration#isIkeExtensionEnabled(int)}.
|
|
*
|
|
* <p>It is recommended that IKE_OPTION_MOBIKE be enabled unless precluded for compatibility
|
|
* reasons.
|
|
*
|
|
* <p>If this option is set for an IKE Session, Transport-mode SAs will not be allowed in that
|
|
* Session.
|
|
*
|
|
* <p>Callers that need to perform migration of IPsec transforms and tunnels MUST implement
|
|
* migration specific methods in {@link IkeSessionCallback} and {@link ChildSessionCallback}.
|
|
*/
|
|
public static final int IKE_OPTION_MOBIKE = 2;
|
|
|
|
/**
|
|
* Configures the IKE session to always send to port 4500.
|
|
*
|
|
* <p>If set, the IKE Session will be initiated and maintained exclusively using
|
|
* destination port 4500, regardless of the presence of NAT. Otherwise, the IKE Session will
|
|
* be initiated on destination port 500; then, if either a NAT is detected or both MOBIKE
|
|
* and NAT-T are supported by the peer, it will proceed on port 4500.
|
|
*/
|
|
public static final int IKE_OPTION_FORCE_PORT_4500 = 3;
|
|
|
|
/**
|
|
* If set, the IKE library will send INITIAL_CONTACT notification to the peers.
|
|
*
|
|
* <p>If this option is set, the INITIAL_CONTACT notification payload is sent in IKE_AUTH. The
|
|
* client can use this option to assert to the peer that this IKE SA is the only IKE SA
|
|
* currently active between the authenticated identities.
|
|
*
|
|
* <p>@see "https://tools.ietf.org/html/rfc7296#section-2.4" RFC 7296, Internet Key Exchange
|
|
* Protocol Version 2 (IKEv2)
|
|
*
|
|
* <p>@see {@link Builder#addIkeOption(int)}
|
|
*/
|
|
public static final int IKE_OPTION_INITIAL_CONTACT = 4;
|
|
|
|
/**
|
|
* If set, the IKE Session will attempt to handle IP address changes by rekeying with new
|
|
* addresses.
|
|
*
|
|
* <p>Upon IP address changes (including Network changes), the IKE session will initiate a
|
|
* standard rekey Child procedure using the new local address to replace the existing associated
|
|
* IPsec transforms with new transforms tied to the new addresses. At the same time the IKE
|
|
* library will notify the remote of the address change and implicitly migrate itself to the new
|
|
* address.
|
|
*
|
|
* <p>This capability is NOT negotiated; it is the responsibility of the caller to ensure that
|
|
* the remote supports rekey-based mobility. Failure to do so may lead to increased disruption
|
|
* during mobility events.
|
|
*
|
|
* <p>This option may be set together with {@link #IKE_OPTION_MOBIKE} as a fallback. If both
|
|
* {@link #IKE_OPTION_MOBIKE} and {@link #IKE_OPTION_REKEY_MOBILITY} are set:
|
|
*
|
|
* <ul>
|
|
* <li>If the server has indicated MOBIKE support, MOBIKE will be used for mobility
|
|
* <li>Otherwise, Rekey will be used for mobility
|
|
* </ul>
|
|
*
|
|
* <p>For callers targeting SDK {@link android.os.Build.VERSION_CODES#S_V2} or earlier, setting
|
|
* {@link #IKE_OPTION_MOBIKE} will implicitly set {@link #IKE_OPTION_REKEY_MOBILITY}.
|
|
*
|
|
* <p>If this option is set for an IKE Session, Transport-mode SAs will not be allowed in that
|
|
* Session.
|
|
*
|
|
* <p>Callers that need to perform migration of IPsec transforms and tunnels MUST implement
|
|
* migration specific methods in {@link IkeSessionCallback} and {@link ChildSessionCallback}.
|
|
*
|
|
* @see {@link IKE_OPTION_MOBIKE}
|
|
* @see {@link IkeSession#setNetwork(Network)}
|
|
* @hide
|
|
*/
|
|
@SystemApi public static final int IKE_OPTION_REKEY_MOBILITY = 5;
|
|
|
|
/**
|
|
* If set, IKE Session will automatically select address families.
|
|
*
|
|
* <p>IP address families often have different performance characteristics on any given network.
|
|
* For example, IPv6 ESP may not be hardware-accelerated by middleboxes, or completely
|
|
* black-holed. This option allows the IKE session to automatically select based on the IP
|
|
* address family it perceives as the most likely to work well.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION = 6;
|
|
|
|
/**
|
|
* If set, the IKE session will select the NATT keepalive timers automatically.
|
|
*
|
|
* <p>NATT keepalive timers will be selected and adjusted based on the underlying network
|
|
* configurations, and updated as underlying network configurations change.
|
|
*
|
|
* @hide
|
|
*/
|
|
public static final int IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES = 7;
|
|
|
|
/**
|
|
* If set, the IKE session will start the NATT keepalive with a power optimization flag.
|
|
*
|
|
* <p>IKE session will start the keepalive with {@link SocketKeepalive#FLAG_AUTOMATIC_ON_OFF}.
|
|
* The system will automatically disable keepalives when no TCP connections are open on the
|
|
* network that is associated with the IKE session.
|
|
*
|
|
* <p>For callers relying on long-lived UDP port mappings through the IPsec layer, this flag
|
|
* should never be used since the keepalive may be stopped unexpectedly.
|
|
*
|
|
* <p>This option applies to only hardware keepalive. When keepalive switches to software
|
|
* keepalive because of errors on hardware keepalive, this option may be ignored.
|
|
*
|
|
* @hide
|
|
*/
|
|
// TODO(b/269200616): Move software keepalive mechanism to other place with the required
|
|
// permission to get TCP socket status via netlink commands to also get benefit from this
|
|
// option.
|
|
@SystemApi
|
|
public static final int IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF = 8;
|
|
|
|
private static final int MIN_IKE_OPTION = IKE_OPTION_ACCEPT_ANY_REMOTE_ID;
|
|
private static final int MAX_IKE_OPTION = IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF;
|
|
|
|
/**
|
|
* Automatically choose the IP version for ESP packets.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int ESP_IP_VERSION_AUTO = 0;
|
|
|
|
/**
|
|
* Use IPv4 for ESP packets.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int ESP_IP_VERSION_IPV4 = 4;
|
|
|
|
/**
|
|
* Use IPv6 for ESP packets.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int ESP_IP_VERSION_IPV6 = 6;
|
|
|
|
// IP version to store in mEspIpVersion.
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({
|
|
ESP_IP_VERSION_AUTO,
|
|
ESP_IP_VERSION_IPV4,
|
|
ESP_IP_VERSION_IPV6,
|
|
})
|
|
public @interface EspIpVersion {}
|
|
|
|
/**
|
|
* Automatically choose the encapsulation type for ESP packets.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int ESP_ENCAP_TYPE_AUTO = 0;
|
|
|
|
/**
|
|
* Do not encapsulate ESP packets in transport layer protocol.
|
|
*
|
|
* Under this encapsulation type, the IKE Session will send NAT detection only when it is
|
|
* performing mobility update from an environment with a NAT, as an attempt to stop using
|
|
* UDP encapsulation for the ESP packets. If IKE Session still detects a NAT in this case,
|
|
* the IKE Session will be terminated.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int ESP_ENCAP_TYPE_NONE = -1;
|
|
|
|
/**
|
|
* Encapsulate ESP packets in UDP.
|
|
*
|
|
* Under this encapsulation type, the IKE Session will send NAT detection and fake a local
|
|
* NAT. In this case the IKE Session will always encapsulate ESP packets in UDP as long as
|
|
* the server also supports NAT traversal.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int ESP_ENCAP_TYPE_UDP = 17;
|
|
|
|
// Encap type to store in mEspEncapType.
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({
|
|
ESP_ENCAP_TYPE_AUTO,
|
|
ESP_ENCAP_TYPE_NONE,
|
|
ESP_ENCAP_TYPE_UDP,
|
|
})
|
|
public @interface EspEncapType {}
|
|
|
|
/**
|
|
* Automatically choose the keepalive interval.
|
|
*
|
|
* This constant can be passed to
|
|
* {@link com.android.internal.net.ipsec.ike.IkeSessionStateMachine#setNetwork} to signify
|
|
* that the keepalive delay should be deduced automatically from the underlying network.
|
|
*
|
|
* @see #getNattKeepAliveDelaySeconds
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
public static final int NATT_KEEPALIVE_INTERVAL_AUTO = -1;
|
|
|
|
/**
|
|
* Setting timer to this value will disable the Dead Peer Detection(DPD).
|
|
*
|
|
* <p>@see {@link Builder#setDpdDelaySeconds}
|
|
*/
|
|
@FlaggedApi("com.android.ipsec.flags.dpd_disable_api")
|
|
public static final int IKE_DPD_DELAY_SEC_DISABLED = Integer.MAX_VALUE;
|
|
|
|
/** @hide */
|
|
public static final SparseArray<String> IP_VERSION_TO_STR;
|
|
|
|
static {
|
|
IP_VERSION_TO_STR = new SparseArray<>();
|
|
IP_VERSION_TO_STR.put(ESP_IP_VERSION_AUTO, "AUTO");
|
|
IP_VERSION_TO_STR.put(ESP_IP_VERSION_IPV4, "IPV4");
|
|
IP_VERSION_TO_STR.put(ESP_IP_VERSION_IPV6, "IPV6");
|
|
}
|
|
|
|
/** @hide */
|
|
public static final SparseArray<String> ENCAP_TYPE_TO_STR;
|
|
|
|
static {
|
|
ENCAP_TYPE_TO_STR = new SparseArray<>();
|
|
ENCAP_TYPE_TO_STR.put(ESP_ENCAP_TYPE_NONE, "NONE");
|
|
ENCAP_TYPE_TO_STR.put(ESP_ENCAP_TYPE_AUTO, "AUTO");
|
|
ENCAP_TYPE_TO_STR.put(ESP_ENCAP_TYPE_UDP, "UDP");
|
|
}
|
|
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_HARD_LIFETIME_SEC_MINIMUM = 300; // 5 minutes
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_HARD_LIFETIME_SEC_MAXIMUM = 86400; // 24 hours
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_HARD_LIFETIME_SEC_DEFAULT = 14400; // 4 hours
|
|
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_SOFT_LIFETIME_SEC_MINIMUM = 120; // 2 minutes
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_SOFT_LIFETIME_SEC_DEFAULT = 7200; // 2 hours
|
|
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
static final int IKE_LIFETIME_MARGIN_SEC_MINIMUM = (int) TimeUnit.MINUTES.toSeconds(1L);
|
|
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_DPD_DELAY_SEC_MIN = 20;
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_DPD_DELAY_SEC_MAX = 1800; // 30 minutes
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_DPD_DELAY_SEC_DEFAULT = 120; // 2 minutes
|
|
|
|
/** @hide */
|
|
@VisibleForTesting public static final int IKE_NATT_KEEPALIVE_DELAY_SEC_MIN = 10;
|
|
/** @hide */
|
|
@VisibleForTesting public static final int IKE_NATT_KEEPALIVE_DELAY_SEC_MAX = 3600;
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_NATT_KEEPALIVE_DELAY_SEC_DEFAULT = 10;
|
|
|
|
/** @hide */
|
|
@VisibleForTesting static final int DSCP_MIN = 0;
|
|
/** @hide */
|
|
@VisibleForTesting static final int DSCP_MAX = 63;
|
|
/** @hide */
|
|
@VisibleForTesting static final int DSCP_DEFAULT = 0;
|
|
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_RETRANS_TIMEOUT_MS_MIN = 500;
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
static final int IKE_RETRANS_TIMEOUT_MS_MAX = (int) TimeUnit.MINUTES.toMillis(30L);
|
|
/** @hide */
|
|
@VisibleForTesting static final int IKE_RETRANS_MAX_ATTEMPTS_MAX = 10;
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
static final int[] IKE_RETRANS_TIMEOUT_MS_LIST_DEFAULT =
|
|
new int[] {500, 1000, 2000, 4000, 8000};
|
|
|
|
/** @hide */
|
|
@VisibleForTesting static final int LIVENESS_RETRANS_TIMEOUT_MS_MIN = 500;
|
|
/** @hide */
|
|
@VisibleForTesting static final int LIVENESS_RETRANS_TIMEOUT_MS_MAX = 30000;
|
|
/** @hide */
|
|
@VisibleForTesting static final int LIVENESS_RETRANS_TIMEOUT_MS_TOTAL = 30000;
|
|
/** @hide */
|
|
@VisibleForTesting static final int LIVENESS_RETRANS_MAX_ATTEMPTS_MAX = 10;
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
static final int[] LIVENESS_RETRANS_TIMEOUT_MS_LIST_DEFAULT =
|
|
new int[] {500, 1000, 2000, 4000, 8000};
|
|
|
|
private static final String SERVER_HOST_NAME_KEY = "mServerHostname";
|
|
private static final String SA_PROPOSALS_KEY = "mSaProposals";
|
|
private static final String LOCAL_ID_KEY = "mLocalIdentification";
|
|
private static final String REMOTE_ID_KEY = "mRemoteIdentification";
|
|
private static final String LOCAL_AUTH_KEY = "mLocalAuthConfig";
|
|
private static final String REMOTE_AUTH_KEY = "mRemoteAuthConfig";
|
|
private static final String CONFIG_ATTRIBUTES_KEY = "mConfigRequests";
|
|
private static final String RETRANS_TIMEOUTS_KEY = "mRetransTimeoutMsList";
|
|
private static final String LIVENESS_RETRANS_TIMEOUTS_KEY = "mLivenessRetransTimeoutMsList";
|
|
private static final String IKE_OPTIONS_KEY = "mIkeOptions";
|
|
private static final String HARD_LIFETIME_SEC_KEY = "mHardLifetimeSec";
|
|
private static final String SOFT_LIFETIME_SEC_KEY = "mSoftLifetimeSec";
|
|
private static final String DPD_DELAY_SEC_KEY = "mDpdDelaySec";
|
|
private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "mNattKeepaliveDelaySec";
|
|
private static final String DSCP_KEY = "mDscp";
|
|
private static final String IS_IKE_FRAGMENT_SUPPORTED_KEY = "mIsIkeFragmentationSupported";
|
|
private static final String IP_VERSION_KEY = "mIpVersion";
|
|
private static final String ENCAP_TYPE_KEY = "mEncapType";
|
|
|
|
@NonNull private final String mServerHostname;
|
|
|
|
// @see #getNetwork for reasons of changing the annotation from @NonNull to @Nullable in SDK S.
|
|
// Do not include mDefaultOrConfiguredNetwork in #hashCode or #equal because when it represents
|
|
// configured network, it always has the same value as mCallerConfiguredNetwork. When it
|
|
// represents a default network it can only reflects the device status at the IkeSessionParams
|
|
// creation time. Since the actually default network may change after IkeSessionParams is
|
|
// constructed, depending on mDefaultOrConfiguredNetwork in #hashCode and #equal to decide
|
|
// if this object equals to another object does not make sense.
|
|
@Nullable private final Network mDefaultOrConfiguredNetwork;
|
|
|
|
@Nullable private final Network mCallerConfiguredNetwork;
|
|
|
|
@NonNull private final IkeSaProposal[] mSaProposals;
|
|
|
|
@NonNull private final IkeIdentification mLocalIdentification;
|
|
@NonNull private final IkeIdentification mRemoteIdentification;
|
|
|
|
@NonNull private final IkeAuthConfig mLocalAuthConfig;
|
|
@NonNull private final IkeAuthConfig mRemoteAuthConfig;
|
|
|
|
@NonNull private final IkeConfigAttribute[] mConfigRequests;
|
|
|
|
@NonNull private final int[] mRetransTimeoutMsList;
|
|
@NonNull private final int[] mLivenessRetransTimeoutMsList;
|
|
|
|
@Nullable private final Ike3gppExtension mIke3gppExtension;
|
|
|
|
private final long mIkeOptions;
|
|
|
|
private final int mHardLifetimeSec;
|
|
private final int mSoftLifetimeSec;
|
|
|
|
private final int mDpdDelaySec;
|
|
private final int mNattKeepaliveDelaySec;
|
|
private final int mDscp;
|
|
@EspIpVersion private final int mIpVersion;
|
|
@EspEncapType private final int mEncapType;
|
|
|
|
private final boolean mIsIkeFragmentationSupported;
|
|
|
|
private IkeSessionParams(
|
|
@NonNull String serverHostname,
|
|
@NonNull Network defaultOrConfiguredNetwork,
|
|
@NonNull Network callerConfiguredNetwork,
|
|
@NonNull IkeSaProposal[] proposals,
|
|
@NonNull IkeIdentification localIdentification,
|
|
@NonNull IkeIdentification remoteIdentification,
|
|
@NonNull IkeAuthConfig localAuthConfig,
|
|
@NonNull IkeAuthConfig remoteAuthConfig,
|
|
@NonNull IkeConfigAttribute[] configRequests,
|
|
@NonNull int[] retransTimeoutMsList,
|
|
@NonNull int[] livenessRetransTimeoutMsList,
|
|
@Nullable Ike3gppExtension ike3gppExtension,
|
|
long ikeOptions,
|
|
int hardLifetimeSec,
|
|
int softLifetimeSec,
|
|
int dpdDelaySec,
|
|
int nattKeepaliveDelaySec,
|
|
int dscp,
|
|
@EspIpVersion int espIpVersion,
|
|
@EspEncapType int espEncapType,
|
|
boolean isIkeFragmentationSupported) {
|
|
mServerHostname = serverHostname;
|
|
mDefaultOrConfiguredNetwork = defaultOrConfiguredNetwork;
|
|
mCallerConfiguredNetwork = callerConfiguredNetwork;
|
|
|
|
mSaProposals = proposals;
|
|
|
|
mLocalIdentification = localIdentification;
|
|
mRemoteIdentification = remoteIdentification;
|
|
|
|
mLocalAuthConfig = localAuthConfig;
|
|
mRemoteAuthConfig = remoteAuthConfig;
|
|
|
|
mConfigRequests = configRequests;
|
|
|
|
mRetransTimeoutMsList = retransTimeoutMsList;
|
|
mLivenessRetransTimeoutMsList = livenessRetransTimeoutMsList;
|
|
|
|
mIke3gppExtension = ike3gppExtension;
|
|
|
|
mIkeOptions = ikeOptions;
|
|
|
|
mHardLifetimeSec = hardLifetimeSec;
|
|
mSoftLifetimeSec = softLifetimeSec;
|
|
|
|
mDpdDelaySec = dpdDelaySec;
|
|
mNattKeepaliveDelaySec = nattKeepaliveDelaySec;
|
|
mDscp = dscp;
|
|
|
|
mIpVersion = espIpVersion;
|
|
mEncapType = espEncapType;
|
|
|
|
mIsIkeFragmentationSupported = isIkeFragmentationSupported;
|
|
}
|
|
|
|
private static void validateIkeOptionOrThrow(@IkeOption int ikeOption) {
|
|
if (ikeOption < MIN_IKE_OPTION || ikeOption > MAX_IKE_OPTION) {
|
|
throw new IllegalArgumentException("Invalid IKE Option: " + ikeOption);
|
|
}
|
|
}
|
|
|
|
private static long getOptionBitValue(int ikeOption) {
|
|
return 1 << ikeOption;
|
|
}
|
|
|
|
/**
|
|
* Constructs this object by deserializing a PersistableBundle
|
|
*
|
|
* <p>Constructed IkeSessionParams is guaranteed to be valid, as checked by the
|
|
* IkeSessionParams.Builder
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public static IkeSessionParams fromPersistableBundle(@NonNull PersistableBundle in) {
|
|
Objects.requireNonNull(in, "PersistableBundle is null");
|
|
|
|
IkeSessionParams.Builder builder = new IkeSessionParams.Builder();
|
|
|
|
builder.setServerHostname(in.getString(SERVER_HOST_NAME_KEY));
|
|
|
|
PersistableBundle proposalBundle = in.getPersistableBundle(SA_PROPOSALS_KEY);
|
|
Objects.requireNonNull(in, "SA Proposals is null");
|
|
List<IkeSaProposal> saProposals =
|
|
PersistableBundleUtils.toList(proposalBundle, IkeSaProposal::fromPersistableBundle);
|
|
for (IkeSaProposal proposal : saProposals) {
|
|
builder.addSaProposal(proposal);
|
|
}
|
|
|
|
builder.setLocalIdentification(
|
|
IkeIdentification.fromPersistableBundle(in.getPersistableBundle(LOCAL_ID_KEY)));
|
|
builder.setRemoteIdentification(
|
|
IkeIdentification.fromPersistableBundle(in.getPersistableBundle(REMOTE_ID_KEY)));
|
|
builder.setAuth(
|
|
IkeAuthConfig.fromPersistableBundle(in.getPersistableBundle(LOCAL_AUTH_KEY)),
|
|
IkeAuthConfig.fromPersistableBundle(in.getPersistableBundle(REMOTE_AUTH_KEY)));
|
|
|
|
PersistableBundle configBundle = in.getPersistableBundle(CONFIG_ATTRIBUTES_KEY);
|
|
Objects.requireNonNull(configBundle, "configBundle is null");
|
|
List<ConfigAttribute> configList =
|
|
PersistableBundleUtils.toList(configBundle, ConfigAttribute::fromPersistableBundle);
|
|
for (ConfigAttribute configAttribute : configList) {
|
|
builder.addConfigRequest((IkeConfigAttribute) configAttribute);
|
|
}
|
|
|
|
builder.setRetransmissionTimeoutsMillis(in.getIntArray(RETRANS_TIMEOUTS_KEY));
|
|
builder.setLivenessRetransmissionTimeoutsMillis(
|
|
in.getIntArray(LIVENESS_RETRANS_TIMEOUTS_KEY));
|
|
|
|
long ikeOptions = in.getLong(IKE_OPTIONS_KEY);
|
|
for (int option = MIN_IKE_OPTION; option <= MAX_IKE_OPTION; option++) {
|
|
if (hasIkeOption(ikeOptions, option)) {
|
|
builder.addIkeOptionInternal(option);
|
|
} else {
|
|
builder.removeIkeOption(option);
|
|
}
|
|
}
|
|
|
|
builder.setLifetimeSeconds(
|
|
in.getInt(HARD_LIFETIME_SEC_KEY), in.getInt(SOFT_LIFETIME_SEC_KEY));
|
|
builder.setDpdDelaySeconds(in.getInt(DPD_DELAY_SEC_KEY));
|
|
builder.setNattKeepAliveDelaySeconds(in.getInt(NATT_KEEPALIVE_DELAY_SEC_KEY));
|
|
|
|
builder.setIpVersion(in.getInt(IP_VERSION_KEY));
|
|
builder.setEncapType(in.getInt(ENCAP_TYPE_KEY));
|
|
|
|
// Fragmentation policy is not configurable. IkeSessionParams will always be constructed to
|
|
// support fragmentation.
|
|
if (!in.getBoolean(IS_IKE_FRAGMENT_SUPPORTED_KEY)) {
|
|
throw new IllegalArgumentException("Invalid fragmentation policy");
|
|
}
|
|
|
|
return builder.build();
|
|
}
|
|
/**
|
|
* Serializes this object to a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public PersistableBundle toPersistableBundle() {
|
|
if (mCallerConfiguredNetwork != null || mIke3gppExtension != null) {
|
|
throw new IllegalStateException(
|
|
"Cannot convert a IkeSessionParams with a caller configured network or with"
|
|
+ " 3GPP extension enabled");
|
|
}
|
|
final PersistableBundle result = new PersistableBundle();
|
|
|
|
result.putString(SERVER_HOST_NAME_KEY, mServerHostname);
|
|
|
|
PersistableBundle saProposalBundle =
|
|
PersistableBundleUtils.fromList(
|
|
Arrays.asList(mSaProposals), IkeSaProposal::toPersistableBundle);
|
|
result.putPersistableBundle(SA_PROPOSALS_KEY, saProposalBundle);
|
|
|
|
result.putPersistableBundle(LOCAL_ID_KEY, mLocalIdentification.toPersistableBundle());
|
|
result.putPersistableBundle(REMOTE_ID_KEY, mRemoteIdentification.toPersistableBundle());
|
|
result.putPersistableBundle(LOCAL_AUTH_KEY, mLocalAuthConfig.toPersistableBundle());
|
|
result.putPersistableBundle(REMOTE_AUTH_KEY, mRemoteAuthConfig.toPersistableBundle());
|
|
|
|
PersistableBundle configAttributeBundle =
|
|
PersistableBundleUtils.fromList(
|
|
Arrays.asList(mConfigRequests), ConfigAttribute::toPersistableBundle);
|
|
result.putPersistableBundle(CONFIG_ATTRIBUTES_KEY, configAttributeBundle);
|
|
|
|
result.putIntArray(RETRANS_TIMEOUTS_KEY, mRetransTimeoutMsList);
|
|
result.putIntArray(LIVENESS_RETRANS_TIMEOUTS_KEY, mLivenessRetransTimeoutMsList);
|
|
result.putLong(IKE_OPTIONS_KEY, mIkeOptions);
|
|
result.putInt(HARD_LIFETIME_SEC_KEY, mHardLifetimeSec);
|
|
result.putInt(SOFT_LIFETIME_SEC_KEY, mSoftLifetimeSec);
|
|
result.putInt(DPD_DELAY_SEC_KEY, mDpdDelaySec);
|
|
result.putInt(NATT_KEEPALIVE_DELAY_SEC_KEY, mNattKeepaliveDelaySec);
|
|
result.putInt(DSCP_KEY, mDscp);
|
|
result.putBoolean(IS_IKE_FRAGMENT_SUPPORTED_KEY, mIsIkeFragmentationSupported);
|
|
result.putInt(IP_VERSION_KEY, mIpVersion);
|
|
result.putInt(ENCAP_TYPE_KEY, mEncapType);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the configured server hostname
|
|
*
|
|
* <p>The configured server hostname will be resolved during IKE Session creation.
|
|
*/
|
|
@NonNull
|
|
public String getServerHostname() {
|
|
return mServerHostname;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the configured {@link Network}, or null if was not set
|
|
*
|
|
* <p>This getter is for internal use. Not matter {@link Builder#Builder(Context)} or {@link
|
|
* Builder#Builder()} is used, this method will always return null if no Network was set by the
|
|
* caller.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Nullable
|
|
public Network getConfiguredNetwork() {
|
|
return mCallerConfiguredNetwork;
|
|
}
|
|
|
|
// This method was first released as a @NonNull System APi and has been changed to @Nullable
|
|
// since Android S. This method needs to be @Nullable because a new Builder constructor {@link
|
|
// Builder#Builder() was added in Android S, and by using the new constructor the return value
|
|
// of this method will be null if no network was set.
|
|
// For apps that are using a null-safe language, making this method @Nullable will break
|
|
// compilation, and apps need to update their code. For apps that are not using null-safe
|
|
// language, making this change will not break the backwards compatibility because for any app
|
|
// that uses the deprecated constructor {@link Builder#Builder(Context)}, the return value of
|
|
// this method is still guaranteed to be non-null.
|
|
/**
|
|
* Retrieves the configured {@link Network}, or null if was not set.
|
|
*
|
|
* <p>@see {@link Builder#setNetwork(Network)}
|
|
*/
|
|
@Nullable
|
|
public Network getNetwork() {
|
|
return mDefaultOrConfiguredNetwork;
|
|
}
|
|
|
|
/**
|
|
* Retrieves all IkeSaProposals configured
|
|
*
|
|
* @deprecated Callers should use {@link #getIkeSaProposals()}. This method is deprecated
|
|
* because its name does not match the return type.
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@SystemApi
|
|
@NonNull
|
|
public List<IkeSaProposal> getSaProposals() {
|
|
return getIkeSaProposals();
|
|
}
|
|
|
|
/** Retrieves all IkeSaProposals configured */
|
|
@NonNull
|
|
public List<IkeSaProposal> getIkeSaProposals() {
|
|
return Arrays.asList(mSaProposals);
|
|
}
|
|
|
|
/** @hide */
|
|
public IkeSaProposal[] getSaProposalsInternal() {
|
|
return mSaProposals;
|
|
}
|
|
|
|
/** Retrieves the local (client) identity */
|
|
@NonNull
|
|
public IkeIdentification getLocalIdentification() {
|
|
return mLocalIdentification;
|
|
}
|
|
|
|
/** Retrieves the required remote (server) identity */
|
|
@NonNull
|
|
public IkeIdentification getRemoteIdentification() {
|
|
return mRemoteIdentification;
|
|
}
|
|
|
|
/** Retrieves the local (client) authentication configuration */
|
|
@NonNull
|
|
public IkeAuthConfig getLocalAuthConfig() {
|
|
return mLocalAuthConfig;
|
|
}
|
|
|
|
/** Retrieves the remote (server) authentication configuration */
|
|
@NonNull
|
|
public IkeAuthConfig getRemoteAuthConfig() {
|
|
return mRemoteAuthConfig;
|
|
}
|
|
|
|
/** Retrieves hard lifetime in seconds */
|
|
// Use "second" because smaller unit won't make sense to describe a rekey interval.
|
|
@SuppressLint("MethodNameUnits")
|
|
@IntRange(from = IKE_HARD_LIFETIME_SEC_MINIMUM, to = IKE_HARD_LIFETIME_SEC_MAXIMUM)
|
|
public int getHardLifetimeSeconds() {
|
|
return mHardLifetimeSec;
|
|
}
|
|
|
|
/** Retrieves soft lifetime in seconds */
|
|
// Use "second" because smaller unit does not make sense to a rekey interval.
|
|
@SuppressLint("MethodNameUnits")
|
|
@IntRange(from = IKE_SOFT_LIFETIME_SEC_MINIMUM, to = IKE_HARD_LIFETIME_SEC_MAXIMUM)
|
|
public int getSoftLifetimeSeconds() {
|
|
return mSoftLifetimeSec;
|
|
}
|
|
|
|
/** Retrieves the Dead Peer Detection(DPD) delay in seconds */
|
|
// Use "second" because smaller unit does not make sense to a DPD delay.
|
|
@SuppressLint("MethodNameUnits")
|
|
@IntRange(from = IKE_DPD_DELAY_SEC_MIN)
|
|
public int getDpdDelaySeconds() {
|
|
return mDpdDelaySec;
|
|
}
|
|
|
|
/** Retrieves the Network Address Translation Traversal (NATT) keepalive delay in seconds */
|
|
// Use "second" because smaller unit does not make sense for a NATT Keepalive delay.
|
|
@SuppressLint("MethodNameUnits")
|
|
@IntRange(from = IKE_NATT_KEEPALIVE_DELAY_SEC_MIN, to = IKE_NATT_KEEPALIVE_DELAY_SEC_MAX)
|
|
public int getNattKeepAliveDelaySeconds() {
|
|
return mNattKeepaliveDelaySec;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the DSCP field of IKE packets.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@IntRange(from = DSCP_MIN, to = DSCP_MAX)
|
|
public int getDscp() {
|
|
return mDscp;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the IP version.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
@EspIpVersion public int getIpVersion() {
|
|
return mIpVersion;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the encap type.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
@EspEncapType public int getEncapType() {
|
|
return mEncapType;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the relative retransmission timeout list in milliseconds
|
|
*
|
|
* <p>@see {@link Builder#setRetransmissionTimeoutsMillis(int[])}
|
|
*/
|
|
@NonNull
|
|
public int[] getRetransmissionTimeoutsMillis() {
|
|
return mRetransTimeoutMsList;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the relative retransmission timeout list for configuring on-demand liveness checks
|
|
* in milliseconds.
|
|
*
|
|
* <p>The on-demand liveness check uses the returned list of liveness retransmission timeouts
|
|
* set from {@link Builder#setLivenessRetransmissionTimeoutsMillis} or uses the default value of
|
|
* {0.5s, 1s, 2s, 4s, 8s} if no override is defined.
|
|
*
|
|
* <p>@see {@link Builder#setLivenessRetransmissionTimeoutsMillis} for more information about
|
|
* how the list is structured.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi("com.android.ipsec.flags.liveness_check_api")
|
|
@NonNull
|
|
public int[] getLivenessRetransmissionTimeoutsMillis() {
|
|
return mLivenessRetransTimeoutMsList;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the configured Ike3gppExtension, or null if it was not set.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@Nullable
|
|
public Ike3gppExtension getIke3gppExtension() {
|
|
return mIke3gppExtension;
|
|
}
|
|
|
|
private static boolean hasIkeOption(long ikeOptionsRecord, @IkeOption int ikeOption) {
|
|
validateIkeOptionOrThrow(ikeOption);
|
|
return (ikeOptionsRecord & getOptionBitValue(ikeOption)) != 0;
|
|
}
|
|
|
|
/**
|
|
* Checks if the given IKE Session negotiation option is set
|
|
*
|
|
* @param ikeOption the option to check.
|
|
* @throws IllegalArgumentException if the provided option is invalid.
|
|
*/
|
|
public boolean hasIkeOption(@IkeOption int ikeOption) {
|
|
return hasIkeOption(mIkeOptions, ikeOption);
|
|
}
|
|
|
|
/**
|
|
* Return all the enabled IKE Options
|
|
*
|
|
* @return A Set of enabled IKE options that have been added using {@link
|
|
* Builder#addIkeOption(int)}
|
|
*/
|
|
@FlaggedApi("com.android.ipsec.flags.enabled_ike_options_api")
|
|
@NonNull
|
|
@IkeOption
|
|
public Set<Integer> getIkeOptions() {
|
|
final Set<Integer> result = new HashSet<>();
|
|
|
|
long ikeOptionBits = mIkeOptions;
|
|
int optionValue = 0;
|
|
while (ikeOptionBits > 0) {
|
|
if ((ikeOptionBits & 1) == 1) {
|
|
result.add(optionValue);
|
|
}
|
|
ikeOptionBits >>>= 1;
|
|
optionValue++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** @hide */
|
|
public long getHardLifetimeMsInternal() {
|
|
return TimeUnit.SECONDS.toMillis((long) mHardLifetimeSec);
|
|
}
|
|
|
|
/** @hide */
|
|
public long getSoftLifetimeMsInternal() {
|
|
return TimeUnit.SECONDS.toMillis((long) mSoftLifetimeSec);
|
|
}
|
|
|
|
/** @hide */
|
|
public boolean isIkeFragmentationSupported() {
|
|
return mIsIkeFragmentationSupported;
|
|
}
|
|
|
|
/** @hide */
|
|
public IkeConfigAttribute[] getConfigurationAttributesInternal() {
|
|
return mConfigRequests;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the list of Configuration Requests
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@NonNull
|
|
public List<IkeConfigRequest> getConfigurationRequests() {
|
|
return Collections.unmodifiableList(Arrays.asList(mConfigRequests));
|
|
}
|
|
|
|
/** @hide */
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(
|
|
mServerHostname,
|
|
mCallerConfiguredNetwork,
|
|
Arrays.hashCode(mSaProposals),
|
|
mLocalIdentification,
|
|
mRemoteIdentification,
|
|
mLocalAuthConfig,
|
|
mRemoteAuthConfig,
|
|
mIke3gppExtension,
|
|
Arrays.hashCode(mConfigRequests),
|
|
Arrays.hashCode(mRetransTimeoutMsList),
|
|
Arrays.hashCode(mLivenessRetransTimeoutMsList),
|
|
mIkeOptions,
|
|
mHardLifetimeSec,
|
|
mSoftLifetimeSec,
|
|
mDpdDelaySec,
|
|
mNattKeepaliveDelaySec,
|
|
mDscp,
|
|
mIsIkeFragmentationSupported,
|
|
mIpVersion,
|
|
mEncapType);
|
|
}
|
|
|
|
/** @hide */
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!(o instanceof IkeSessionParams)) {
|
|
return false;
|
|
}
|
|
|
|
IkeSessionParams other = (IkeSessionParams) o;
|
|
|
|
return mServerHostname.equals(other.mServerHostname)
|
|
&& Objects.equals(mCallerConfiguredNetwork, other.mCallerConfiguredNetwork)
|
|
&& Arrays.equals(mSaProposals, other.mSaProposals)
|
|
&& mLocalIdentification.equals(other.mLocalIdentification)
|
|
&& mRemoteIdentification.equals(other.mRemoteIdentification)
|
|
&& mLocalAuthConfig.equals(other.mLocalAuthConfig)
|
|
&& mRemoteAuthConfig.equals(other.mRemoteAuthConfig)
|
|
&& Objects.equals(mIke3gppExtension, other.mIke3gppExtension)
|
|
&& Arrays.equals(mConfigRequests, other.mConfigRequests)
|
|
&& Arrays.equals(mRetransTimeoutMsList, other.mRetransTimeoutMsList)
|
|
&& Arrays.equals(mLivenessRetransTimeoutMsList, other.mLivenessRetransTimeoutMsList)
|
|
&& mIkeOptions == other.mIkeOptions
|
|
&& mHardLifetimeSec == other.mHardLifetimeSec
|
|
&& mSoftLifetimeSec == other.mSoftLifetimeSec
|
|
&& mDpdDelaySec == other.mDpdDelaySec
|
|
&& mNattKeepaliveDelaySec == other.mNattKeepaliveDelaySec
|
|
&& mDscp == other.mDscp
|
|
&& mIsIkeFragmentationSupported == other.mIsIkeFragmentationSupported
|
|
&& mIpVersion == other.mIpVersion
|
|
&& mEncapType == other.mEncapType;
|
|
}
|
|
|
|
/**
|
|
* Represents an IKE session configuration request type
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public interface IkeConfigRequest {}
|
|
|
|
/**
|
|
* Represents an IPv4 P_CSCF request
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public interface ConfigRequestIpv4PcscfServer extends IkeConfigRequest {
|
|
/**
|
|
* Retrieves the requested IPv4 P_CSCF server address
|
|
*
|
|
* @return The requested P_CSCF server address, or null if no specific P_CSCF server was
|
|
* requested
|
|
*/
|
|
@Nullable
|
|
Inet4Address getAddress();
|
|
}
|
|
|
|
/**
|
|
* Represents an IPv6 P_CSCF request
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public interface ConfigRequestIpv6PcscfServer extends IkeConfigRequest {
|
|
/**
|
|
* Retrieves the requested IPv6 P_CSCF server address
|
|
*
|
|
* @return The requested P_CSCF server address, or null if no specific P_CSCF server was
|
|
* requested
|
|
*/
|
|
@Nullable
|
|
Inet6Address getAddress();
|
|
}
|
|
|
|
/** This class contains common information of an IKEv2 authentication configuration. */
|
|
public abstract static class IkeAuthConfig {
|
|
private static final String AUTH_METHOD_KEY = "mAuthMethod";
|
|
private static final String AUTH_DIRECTION_KEY = "mAuthDirection";
|
|
/** @hide */
|
|
@IkeAuthMethod public final int mAuthMethod;
|
|
/** @hide */
|
|
@AuthDirection public final int mAuthDirection;
|
|
|
|
/** @hide */
|
|
IkeAuthConfig(@IkeAuthMethod int authMethod, @AuthDirection int authDirection) {
|
|
mAuthMethod = authMethod;
|
|
mAuthDirection = authDirection;
|
|
}
|
|
|
|
/**
|
|
* Constructs this object by deserializing a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public static IkeAuthConfig fromPersistableBundle(PersistableBundle in) {
|
|
Objects.requireNonNull(in, "PersistableBundle is null");
|
|
|
|
int authMethod = in.getInt(AUTH_METHOD_KEY);
|
|
switch (authMethod) {
|
|
case IKE_AUTH_METHOD_PSK:
|
|
return IkeAuthPskConfig.fromPersistableBundle(in);
|
|
case IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
|
|
switch (in.getInt(AUTH_DIRECTION_KEY)) {
|
|
case AUTH_DIRECTION_LOCAL:
|
|
return IkeAuthDigitalSignLocalConfig.fromPersistableBundle(in);
|
|
case AUTH_DIRECTION_REMOTE:
|
|
return IkeAuthDigitalSignRemoteConfig.fromPersistableBundle(in);
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
"Digital-signature-based auth configuration with invalid"
|
|
+ " direction: "
|
|
+ in.getInt(AUTH_DIRECTION_KEY));
|
|
}
|
|
case IKE_AUTH_METHOD_EAP:
|
|
return IkeAuthEapConfig.fromPersistableBundle(in);
|
|
default:
|
|
throw new IllegalArgumentException("Invalid Auth Method: " + authMethod);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Serializes this object to a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
protected PersistableBundle toPersistableBundle() {
|
|
final PersistableBundle result = new PersistableBundle();
|
|
|
|
result.putInt(AUTH_METHOD_KEY, mAuthMethod);
|
|
result.putInt(AUTH_DIRECTION_KEY, mAuthDirection);
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(mAuthMethod, mAuthDirection);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!(o instanceof IkeAuthConfig)) {
|
|
return false;
|
|
}
|
|
|
|
IkeAuthConfig other = (IkeAuthConfig) o;
|
|
|
|
return mAuthMethod == other.mAuthMethod && mAuthDirection == other.mAuthDirection;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents the configuration to support IKEv2 pre-shared-key-based authentication
|
|
* of local or remote side.
|
|
*/
|
|
public static class IkeAuthPskConfig extends IkeAuthConfig {
|
|
private static final String PSK_KEY = "mPsk";
|
|
/** @hide */
|
|
@NonNull public final byte[] mPsk;
|
|
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
IkeAuthPskConfig(byte[] psk) {
|
|
super(IKE_AUTH_METHOD_PSK, AUTH_DIRECTION_BOTH);
|
|
mPsk = psk;
|
|
}
|
|
|
|
/**
|
|
* Constructs this object by deserializing a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public static IkeAuthPskConfig fromPersistableBundle(@NonNull PersistableBundle in) {
|
|
Objects.requireNonNull(in, "PersistableBundle is null");
|
|
|
|
PersistableBundle pskBundle = in.getPersistableBundle(PSK_KEY);
|
|
Objects.requireNonNull(in, "PSK bundle is null");
|
|
|
|
return new IkeAuthPskConfig(PersistableBundleUtils.toByteArray(pskBundle));
|
|
}
|
|
|
|
/**
|
|
* Serializes this object to a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@Override
|
|
@NonNull
|
|
public PersistableBundle toPersistableBundle() {
|
|
final PersistableBundle result = super.toPersistableBundle();
|
|
|
|
result.putPersistableBundle(PSK_KEY, PersistableBundleUtils.fromByteArray(mPsk));
|
|
return result;
|
|
}
|
|
|
|
/** Retrieves the pre-shared key */
|
|
@NonNull
|
|
public byte[] getPsk() {
|
|
return Arrays.copyOf(mPsk, mPsk.length);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(super.hashCode(), Arrays.hashCode(mPsk));
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!super.equals(o) || !(o instanceof IkeAuthPskConfig)) {
|
|
return false;
|
|
}
|
|
|
|
return Arrays.equals(mPsk, ((IkeAuthPskConfig) o).mPsk);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents the configuration to support IKEv2 public-key-signature-based
|
|
* authentication of the remote side.
|
|
*/
|
|
public static class IkeAuthDigitalSignRemoteConfig extends IkeAuthConfig {
|
|
private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
|
|
/** @hide */
|
|
@Nullable public final TrustAnchor mTrustAnchor;
|
|
|
|
/**
|
|
* If a certificate is provided, it MUST be the root CA used by the remote (server), or
|
|
* authentication will fail. If no certificate is provided, any root CA in the system's
|
|
* truststore is considered acceptable.
|
|
*
|
|
* @hide
|
|
*/
|
|
@VisibleForTesting
|
|
IkeAuthDigitalSignRemoteConfig(@Nullable X509Certificate caCert) {
|
|
super(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, AUTH_DIRECTION_REMOTE);
|
|
if (caCert == null) {
|
|
mTrustAnchor = null;
|
|
} else {
|
|
// The name constraints extension, defined in RFC 5280, indicates a name space
|
|
// within which all subject names in subsequent certificates in a certification path
|
|
// MUST be located.
|
|
mTrustAnchor = new TrustAnchor(caCert, null /*nameConstraints*/);
|
|
|
|
// TODO: Investigate if we need to support the name constraints extension.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructs this object by deserializing a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public static IkeAuthDigitalSignRemoteConfig fromPersistableBundle(
|
|
@NonNull PersistableBundle in) {
|
|
Objects.requireNonNull(in, "PersistableBundle is null");
|
|
|
|
PersistableBundle trustCertBundle = in.getPersistableBundle(TRUST_CERT_KEY);
|
|
|
|
X509Certificate caCert = null;
|
|
if (trustCertBundle != null) {
|
|
byte[] encodedCert = PersistableBundleUtils.toByteArray(trustCertBundle);
|
|
caCert = certificateFromByteArray(encodedCert);
|
|
}
|
|
|
|
return new IkeAuthDigitalSignRemoteConfig(caCert);
|
|
}
|
|
|
|
/**
|
|
* Serializes this object to a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@Override
|
|
@NonNull
|
|
public PersistableBundle toPersistableBundle() {
|
|
final PersistableBundle result = super.toPersistableBundle();
|
|
|
|
try {
|
|
if (mTrustAnchor != null) {
|
|
result.putPersistableBundle(
|
|
TRUST_CERT_KEY,
|
|
PersistableBundleUtils.fromByteArray(
|
|
mTrustAnchor.getTrustedCert().getEncoded()));
|
|
}
|
|
|
|
} catch (CertificateEncodingException e) {
|
|
throw new IllegalArgumentException("Fail to encode the certificate");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Retrieves the provided CA certificate for validating the remote certificate(s) */
|
|
@Nullable
|
|
public X509Certificate getRemoteCaCert() {
|
|
if (mTrustAnchor == null) return null;
|
|
return mTrustAnchor.getTrustedCert();
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
// Use #getTrustedCert() because TrustAnchor does not override #hashCode()
|
|
return Objects.hash(
|
|
super.hashCode(),
|
|
(mTrustAnchor == null) ? null : mTrustAnchor.getTrustedCert());
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!super.equals(o) || !(o instanceof IkeAuthDigitalSignRemoteConfig)) {
|
|
return false;
|
|
}
|
|
|
|
IkeAuthDigitalSignRemoteConfig other = (IkeAuthDigitalSignRemoteConfig) o;
|
|
|
|
if (mTrustAnchor == null && other.mTrustAnchor == null) {
|
|
return true;
|
|
}
|
|
|
|
// Compare #getTrustedCert() because TrustAnchor does not override #equals(Object)
|
|
return mTrustAnchor != null
|
|
&& other.mTrustAnchor != null
|
|
&& Objects.equals(
|
|
mTrustAnchor.getTrustedCert(), other.mTrustAnchor.getTrustedCert());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents the configuration to support IKEv2 public-key-signature-based
|
|
* authentication of the local side.
|
|
*/
|
|
public static class IkeAuthDigitalSignLocalConfig extends IkeAuthConfig {
|
|
private static final String END_CERT_KEY = "mEndCert";
|
|
private static final String INTERMEDIATE_CERTS_KEY = "mIntermediateCerts";
|
|
private static final String PRIVATE_KEY_KEY = "mPrivateKey";
|
|
/** @hide */
|
|
@NonNull public final X509Certificate mEndCert;
|
|
|
|
/** @hide */
|
|
@NonNull public final List<X509Certificate> mIntermediateCerts;
|
|
|
|
/** @hide */
|
|
@NonNull public final PrivateKey mPrivateKey;
|
|
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
IkeAuthDigitalSignLocalConfig(
|
|
@NonNull X509Certificate clientEndCert,
|
|
@NonNull List<X509Certificate> clientIntermediateCerts,
|
|
@NonNull PrivateKey privateKey) {
|
|
super(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE, AUTH_DIRECTION_LOCAL);
|
|
mEndCert = clientEndCert;
|
|
mIntermediateCerts = clientIntermediateCerts;
|
|
mPrivateKey = privateKey;
|
|
}
|
|
|
|
/**
|
|
* Constructs this object by deserializing a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public static IkeAuthDigitalSignLocalConfig fromPersistableBundle(
|
|
@NonNull PersistableBundle in) {
|
|
Objects.requireNonNull(in, "PersistableBundle is null");
|
|
|
|
PersistableBundle endCertBundle = in.getPersistableBundle(END_CERT_KEY);
|
|
Objects.requireNonNull(endCertBundle, "End cert not provided");
|
|
byte[] encodedCert = PersistableBundleUtils.toByteArray(endCertBundle);
|
|
X509Certificate endCert = certificateFromByteArray(encodedCert);
|
|
|
|
PersistableBundle certsBundle = in.getPersistableBundle(INTERMEDIATE_CERTS_KEY);
|
|
Objects.requireNonNull(certsBundle, "Intermediate certs not provided");
|
|
List<byte[]> encodedCertList =
|
|
PersistableBundleUtils.toList(certsBundle, PersistableBundleUtils::toByteArray);
|
|
List<X509Certificate> certList = new ArrayList<>(encodedCertList.size());
|
|
for (byte[] encoded : encodedCertList) {
|
|
certList.add(certificateFromByteArray(encoded));
|
|
}
|
|
|
|
PersistableBundle privateKeyBundle = in.getPersistableBundle(PRIVATE_KEY_KEY);
|
|
Objects.requireNonNull(privateKeyBundle, "PrivateKey bundle is null");
|
|
PrivateKey privateKey =
|
|
privateKeyFromByteArray(PersistableBundleUtils.toByteArray(privateKeyBundle));
|
|
Objects.requireNonNull(privateKeyBundle, "PrivateKey is null");
|
|
|
|
return new IkeAuthDigitalSignLocalConfig(endCert, certList, privateKey);
|
|
}
|
|
|
|
/**
|
|
* Serializes this object to a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@Override
|
|
@NonNull
|
|
public PersistableBundle toPersistableBundle() {
|
|
final PersistableBundle result = super.toPersistableBundle();
|
|
|
|
try {
|
|
result.putPersistableBundle(
|
|
END_CERT_KEY, PersistableBundleUtils.fromByteArray(mEndCert.getEncoded()));
|
|
|
|
List<byte[]> encodedCertList = new ArrayList<>(mIntermediateCerts.size());
|
|
for (X509Certificate cert : mIntermediateCerts) {
|
|
encodedCertList.add(cert.getEncoded());
|
|
}
|
|
PersistableBundle certsBundle =
|
|
PersistableBundleUtils.fromList(
|
|
encodedCertList, PersistableBundleUtils::fromByteArray);
|
|
result.putPersistableBundle(INTERMEDIATE_CERTS_KEY, certsBundle);
|
|
} catch (CertificateEncodingException e) {
|
|
throw new IllegalArgumentException("Fail to encode certificate");
|
|
}
|
|
|
|
// TODO: b/170670506 Consider putting PrivateKey in Android KeyStore
|
|
result.putPersistableBundle(
|
|
PRIVATE_KEY_KEY,
|
|
PersistableBundleUtils.fromByteArray(mPrivateKey.getEncoded()));
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Retrieves the client end certificate */
|
|
@NonNull
|
|
public X509Certificate getClientEndCertificate() {
|
|
return mEndCert;
|
|
}
|
|
|
|
/** Retrieves the intermediate certificates */
|
|
@NonNull
|
|
public List<X509Certificate> getIntermediateCertificates() {
|
|
return mIntermediateCerts;
|
|
}
|
|
|
|
/** Retrieves the private key */
|
|
@NonNull
|
|
public PrivateKey getPrivateKey() {
|
|
return mPrivateKey;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(super.hashCode(), mEndCert, mIntermediateCerts, mPrivateKey);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!super.equals(o) || !(o instanceof IkeAuthDigitalSignLocalConfig)) {
|
|
return false;
|
|
}
|
|
|
|
IkeAuthDigitalSignLocalConfig other = (IkeAuthDigitalSignLocalConfig) o;
|
|
|
|
return mEndCert.equals(other.mEndCert)
|
|
&& mIntermediateCerts.equals(other.mIntermediateCerts)
|
|
&& mPrivateKey.equals(other.mPrivateKey);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents the configuration to support EAP authentication of the local side.
|
|
*
|
|
* <p>@see {@link IkeSessionParams.Builder#setAuthEap(X509Certificate, EapSessionConfig)}
|
|
*/
|
|
public static class IkeAuthEapConfig extends IkeAuthConfig {
|
|
private static final String EAP_CONFIG_KEY = "mEapConfig";
|
|
|
|
/** @hide */
|
|
@NonNull public final EapSessionConfig mEapConfig;
|
|
|
|
/** @hide */
|
|
@VisibleForTesting
|
|
IkeAuthEapConfig(EapSessionConfig eapConfig) {
|
|
super(IKE_AUTH_METHOD_EAP, AUTH_DIRECTION_LOCAL);
|
|
|
|
mEapConfig = eapConfig;
|
|
}
|
|
|
|
/**
|
|
* Constructs this object by deserializing a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
public static IkeAuthEapConfig fromPersistableBundle(@NonNull PersistableBundle in) {
|
|
Objects.requireNonNull(in, "PersistableBundle null");
|
|
|
|
PersistableBundle eapBundle = in.getPersistableBundle(EAP_CONFIG_KEY);
|
|
Objects.requireNonNull(in, "EAP Config bundle is null");
|
|
|
|
EapSessionConfig eapConfig = EapSessionConfig.fromPersistableBundle(eapBundle);
|
|
Objects.requireNonNull(eapConfig, "EAP Config is null");
|
|
|
|
return new IkeAuthEapConfig(eapConfig);
|
|
}
|
|
|
|
/**
|
|
* Serializes this object to a PersistableBundle
|
|
*
|
|
* @hide
|
|
*/
|
|
@Override
|
|
@NonNull
|
|
public PersistableBundle toPersistableBundle() {
|
|
final PersistableBundle result = super.toPersistableBundle();
|
|
result.putPersistableBundle(EAP_CONFIG_KEY, mEapConfig.toPersistableBundle());
|
|
return result;
|
|
}
|
|
|
|
/** Retrieves EAP configuration */
|
|
@NonNull
|
|
public EapSessionConfig getEapConfig() {
|
|
return mEapConfig;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(super.hashCode(), mEapConfig);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (!super.equals(o) || !(o instanceof IkeAuthEapConfig)) {
|
|
return false;
|
|
}
|
|
|
|
return mEapConfig.equals(((IkeAuthEapConfig) o).mEapConfig);
|
|
}
|
|
}
|
|
|
|
/** This class can be used to incrementally construct a {@link IkeSessionParams}. */
|
|
public static final class Builder {
|
|
// This field has changed from @NonNull to @Nullable since Android S. It has to be @Nullable
|
|
// because the new constructor #Builder() will not need and will not able to get a
|
|
// ConnectivityManager instance anymore. Making it @Nullable does not break the backwards
|
|
// compatibility because if apps use the old constructor #Builder(Context), the Builder and
|
|
// the IkeSessionParams built from it will still work in the old way. @see #Builder(Context)
|
|
@Nullable private ConnectivityManager mConnectivityManager;
|
|
|
|
@NonNull private final List<IkeSaProposal> mSaProposalList = new LinkedList<>();
|
|
@NonNull private final List<IkeConfigAttribute> mConfigRequestList = new ArrayList<>();
|
|
|
|
@NonNull
|
|
private int[] mRetransTimeoutMsList =
|
|
Arrays.copyOf(
|
|
IKE_RETRANS_TIMEOUT_MS_LIST_DEFAULT,
|
|
IKE_RETRANS_TIMEOUT_MS_LIST_DEFAULT.length);
|
|
|
|
@NonNull
|
|
private int[] mLivenessRetransTimeoutMsList =
|
|
Arrays.copyOf(
|
|
LIVENESS_RETRANS_TIMEOUT_MS_LIST_DEFAULT,
|
|
LIVENESS_RETRANS_TIMEOUT_MS_LIST_DEFAULT.length);
|
|
|
|
@NonNull private String mServerHostname;
|
|
@Nullable private Network mCallerConfiguredNetwork;
|
|
|
|
@Nullable private IkeIdentification mLocalIdentification;
|
|
@Nullable private IkeIdentification mRemoteIdentification;
|
|
|
|
@Nullable private IkeAuthConfig mLocalAuthConfig;
|
|
@Nullable private IkeAuthConfig mRemoteAuthConfig;
|
|
|
|
@Nullable private Ike3gppExtension mIke3gppExtension;
|
|
|
|
private long mIkeOptions = 0;
|
|
|
|
private int mHardLifetimeSec = IKE_HARD_LIFETIME_SEC_DEFAULT;
|
|
private int mSoftLifetimeSec = IKE_SOFT_LIFETIME_SEC_DEFAULT;
|
|
|
|
private int mDpdDelaySec = IKE_DPD_DELAY_SEC_DEFAULT;
|
|
private int mNattKeepaliveDelaySec = IKE_NATT_KEEPALIVE_DELAY_SEC_DEFAULT;
|
|
private int mDscp = DSCP_DEFAULT;
|
|
private final boolean mIsIkeFragmentationSupported = true;
|
|
|
|
@EspIpVersion private int mIpVersion = ESP_IP_VERSION_AUTO;
|
|
@EspEncapType private int mEncapType = ESP_ENCAP_TYPE_AUTO;
|
|
|
|
/**
|
|
* Construct Builder
|
|
*
|
|
* <p>This constructor is deprecated since Android S. Apps that use this constructor can
|
|
* still expect {@link #build()} to throw if no configured or default network was found. But
|
|
* apps that use {@link #Builder()} MUST NOT expect that behavior anymore.
|
|
*
|
|
* <p>For a caller that used this constructor and did not set any Network, {@link
|
|
* IkeSessionParams#getNetwork()} will return the default Network resolved in {@link
|
|
* IkeSessionParams.Builder#build()}. This return value is only informational because if
|
|
* MOBIKE is enabled, IKE Session may switch to a different default Network.
|
|
*
|
|
* @param context a valid {@link Context} instance.
|
|
* @deprecated Callers should use {@link #Builder()}.This method is deprecated because it is
|
|
* unnecessary to try resolving a default network or to validate network is connected
|
|
* before {@link IkeSession} starts the setup process.
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@SystemApi
|
|
public Builder(@NonNull Context context) {
|
|
this((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
|
|
}
|
|
|
|
/**
|
|
* Construct Builder
|
|
*/
|
|
public Builder() {}
|
|
|
|
/** @hide */
|
|
// TODO: b/178389011 This constructor should be removed when #Builder(Context) can be safely
|
|
// removed. See #Builder(Context) for reasons.
|
|
@VisibleForTesting
|
|
public Builder(ConnectivityManager connectManager) {
|
|
mConnectivityManager = connectManager;
|
|
}
|
|
|
|
/**
|
|
* Construct Builder from the {@link IkeSessionParams} object.
|
|
*
|
|
* @param ikeSessionParams the object this Builder will be constructed with.
|
|
*/
|
|
public Builder(@NonNull IkeSessionParams ikeSessionParams) {
|
|
mSaProposalList.addAll(ikeSessionParams.getSaProposals());
|
|
mConfigRequestList.addAll(Arrays.asList(ikeSessionParams.mConfigRequests));
|
|
|
|
int[] retransmissionTimeouts = ikeSessionParams.getRetransmissionTimeoutsMillis();
|
|
mRetransTimeoutMsList =
|
|
Arrays.copyOf(retransmissionTimeouts, retransmissionTimeouts.length);
|
|
|
|
int[] livenessretransmissionTimeouts = ikeSessionParams.mLivenessRetransTimeoutMsList;
|
|
if (livenessretransmissionTimeouts != null) {
|
|
mLivenessRetransTimeoutMsList =
|
|
Arrays.copyOf(
|
|
livenessretransmissionTimeouts,
|
|
livenessretransmissionTimeouts.length);
|
|
}
|
|
|
|
mServerHostname = ikeSessionParams.getServerHostname();
|
|
mCallerConfiguredNetwork = ikeSessionParams.getConfiguredNetwork();
|
|
mLocalIdentification = ikeSessionParams.getLocalIdentification();
|
|
mRemoteIdentification = ikeSessionParams.getRemoteIdentification();
|
|
mLocalAuthConfig = ikeSessionParams.getLocalAuthConfig();
|
|
mRemoteAuthConfig = ikeSessionParams.getRemoteAuthConfig();
|
|
|
|
mIke3gppExtension = ikeSessionParams.getIke3gppExtension();
|
|
|
|
mHardLifetimeSec = ikeSessionParams.getHardLifetimeSeconds();
|
|
mSoftLifetimeSec = ikeSessionParams.getSoftLifetimeSeconds();
|
|
mDpdDelaySec = ikeSessionParams.getDpdDelaySeconds();
|
|
mNattKeepaliveDelaySec = ikeSessionParams.getNattKeepAliveDelaySeconds();
|
|
mDscp = ikeSessionParams.getDscp();
|
|
mIpVersion = ikeSessionParams.getIpVersion();
|
|
mEncapType = ikeSessionParams.getEncapType();
|
|
|
|
mIkeOptions = ikeSessionParams.mIkeOptions;
|
|
|
|
if (!ikeSessionParams.mIsIkeFragmentationSupported) {
|
|
throw new IllegalStateException(
|
|
"mIsIkeFragmentationSupported should never be false");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the server hostname for the {@link IkeSessionParams} being built.
|
|
*
|
|
* @param serverHostname the hostname of the IKE server, such as "ike.android.com".
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setServerHostname(@NonNull String serverHostname) {
|
|
Objects.requireNonNull(serverHostname, "Required argument not provided");
|
|
|
|
mServerHostname = serverHostname;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the {@link Network} for the {@link IkeSessionParams} being built.
|
|
*
|
|
* <p>If no {@link Network} is provided, the default Network (as per {@link
|
|
* ConnectivityManager#getActiveNetwork()}) will be used when constructing an {@link
|
|
* IkeSession}.
|
|
*
|
|
* @param network the {@link Network} that IKE Session will use, or {@code null} to clear
|
|
* the previously set {@link Network}
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setNetwork(@Nullable Network network) {
|
|
mCallerConfiguredNetwork = network;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets local IKE identification for the {@link IkeSessionParams} being built.
|
|
*
|
|
* <p>It is not allowed to use KEY ID together with digital-signature-based authentication
|
|
* as per RFC 7296.
|
|
*
|
|
* @param identification the local IKE identification.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setLocalIdentification(@NonNull IkeIdentification identification) {
|
|
if (identification == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
mLocalIdentification = identification;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets remote IKE identification for the {@link IkeSessionParams} being built.
|
|
*
|
|
* @param identification the remote IKE identification.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setRemoteIdentification(@NonNull IkeIdentification identification) {
|
|
if (identification == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
mRemoteIdentification = identification;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Adds an IKE SA proposal to the {@link IkeSessionParams} being built.
|
|
*
|
|
* @param proposal IKE SA proposal.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @deprecated Callers should use {@link #addIkeSaProposal(IkeSaProposal)}. This method is
|
|
* deprecated because its name does not match the input type.
|
|
* @hide
|
|
*/
|
|
@Deprecated
|
|
@SystemApi
|
|
@NonNull
|
|
public Builder addSaProposal(@NonNull IkeSaProposal proposal) {
|
|
return addIkeSaProposal(proposal);
|
|
}
|
|
|
|
/**
|
|
* Adds an IKE SA proposal to the {@link IkeSessionParams} being built.
|
|
*
|
|
* @param proposal IKE SA proposal.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder addIkeSaProposal(@NonNull IkeSaProposal proposal) {
|
|
if (proposal == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
if (proposal.getProtocolId() != IkePayload.PROTOCOL_ID_IKE) {
|
|
throw new IllegalArgumentException(
|
|
"Expected IKE SA Proposal but received Child SA proposal");
|
|
}
|
|
mSaProposalList.add(proposal);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures authentication for IKE Session. Internal use only.
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
private Builder setAuth(IkeAuthConfig local, IkeAuthConfig remote) {
|
|
mLocalAuthConfig = local;
|
|
mRemoteAuthConfig = remote;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures the {@link IkeSession} to use pre-shared-key-based authentication.
|
|
*
|
|
* <p>Both client and server MUST be authenticated using the provided shared key. IKE
|
|
* authentication will fail if the remote peer tries to use other authentication methods.
|
|
*
|
|
* <p>Callers MUST declare only one authentication method. Calling this function will
|
|
* override the previously set authentication configuration.
|
|
*
|
|
* <p>Callers SHOULD NOT use this if any other authentication methods can be used; PSK-based
|
|
* authentication is generally considered insecure.
|
|
*
|
|
* @param sharedKey the shared key.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
// #getLocalAuthConfig and #getRemoveAuthConfig are defined to retrieve
|
|
// authentication configurations
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@NonNull
|
|
public Builder setAuthPsk(@NonNull byte[] sharedKey) {
|
|
if (sharedKey == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
return setAuth(new IkeAuthPskConfig(sharedKey), new IkeAuthPskConfig(sharedKey));
|
|
}
|
|
|
|
/**
|
|
* Configures the {@link IkeSession} to use EAP authentication.
|
|
*
|
|
* <p>Not all EAP methods provide mutual authentication. As such EAP MUST be used in
|
|
* conjunction with a public-key-signature-based authentication of the remote server, unless
|
|
* EAP-Only authentication is enabled.
|
|
*
|
|
* <p>Callers may enable EAP-Only authentication by setting {@link
|
|
* #IKE_OPTION_EAP_ONLY_AUTH}, which will make IKE library request the remote to use
|
|
* EAP-Only authentication. The remote may opt to reject the request, at which point the
|
|
* received certificates and authentication payload WILL be validated with the provided root
|
|
* CA or system's truststore as usual. Only safe EAP methods as listed in RFC 5998 will be
|
|
* accepted for EAP-Only authentication.
|
|
*
|
|
* <p>If {@link #IKE_OPTION_EAP_ONLY_AUTH} is set, callers MUST configure EAP as the
|
|
* authentication method and all EAP methods set in EAP Session configuration MUST be safe
|
|
* methods that are accepted for EAP-Only authentication. Otherwise callers will get an
|
|
* exception when building the {@link IkeSessionParams}
|
|
*
|
|
* <p>Callers MUST declare only one authentication method. Calling this function will
|
|
* override the previously set authentication configuration.
|
|
*
|
|
* @see <a href="https://tools.ietf.org/html/rfc5280">RFC 5280, Internet X.509 Public Key
|
|
* Infrastructure Certificate and Certificate Revocation List (CRL) Profile</a>
|
|
* @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998, An Extension for EAP-Only
|
|
* Authentication in IKEv2</a>
|
|
* @param serverCaCert the CA certificate for validating the received server certificate(s).
|
|
* If a certificate is provided, it MUST be the root CA used by the server, or
|
|
* authentication will fail. If no certificate is provided, any root CA in the system's
|
|
* truststore is considered acceptable.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
// TODO(b/151667921): Consider also supporting configuring EAP method that is not accepted
|
|
// by EAP-Only when {@link #IKE_OPTION_EAP_ONLY_AUTH} is set
|
|
// MissingGetterMatchingBuilder: #getLocalAuthConfig and #getRemoveAuthConfig are defined to
|
|
// retrieve authentication configurations
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@NonNull
|
|
public Builder setAuthEap(
|
|
@Nullable X509Certificate serverCaCert, @NonNull EapSessionConfig eapConfig) {
|
|
if (eapConfig == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
return setAuth(
|
|
new IkeAuthEapConfig(eapConfig),
|
|
new IkeAuthDigitalSignRemoteConfig(serverCaCert));
|
|
}
|
|
|
|
/**
|
|
* Configures the {@link IkeSession} to use public-key-signature-based authentication.
|
|
*
|
|
* <p>The public key included by the client end certificate and the private key used for
|
|
* signing MUST be a matching key pair.
|
|
*
|
|
* <p>The IKE library will use the strongest signature algorithm supported by both sides.
|
|
*
|
|
* <p>Currenly only RSA digital signature is supported.
|
|
*
|
|
* @param serverCaCert the CA certificate for validating the received server certificate(s).
|
|
* If a certificate is provided, it MUST be the root CA used by the server, or
|
|
* authentication will fail. If no certificate is provided, any root CA in the system's
|
|
* truststore is considered acceptable.
|
|
* @param clientEndCert the end certificate for remote server to verify the locally
|
|
* generated signature.
|
|
* @param clientPrivateKey private key to generate outbound digital signature. The {@link
|
|
* PrivateKey} MUST be an instance of {@link RSAKey}.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
// #getLocalAuthConfig and #getRemoveAuthConfig are defined to retrieve
|
|
// authentication configurations
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@NonNull
|
|
public Builder setAuthDigitalSignature(
|
|
@Nullable X509Certificate serverCaCert,
|
|
@NonNull X509Certificate clientEndCert,
|
|
@NonNull PrivateKey clientPrivateKey) {
|
|
return setAuthDigitalSignature(
|
|
serverCaCert,
|
|
clientEndCert,
|
|
new LinkedList<X509Certificate>(),
|
|
clientPrivateKey);
|
|
}
|
|
|
|
/**
|
|
* Configures the {@link IkeSession} to use public-key-signature-based authentication.
|
|
*
|
|
* <p>The public key included by the client end certificate and the private key used for
|
|
* signing MUST be a matching key pair.
|
|
*
|
|
* <p>The IKE library will use the strongest signature algorithm supported by both sides.
|
|
*
|
|
* <p>Currenly only RSA digital signature is supported.
|
|
*
|
|
* @param serverCaCert the CA certificate for validating the received server certificate(s).
|
|
* If a null value is provided, IKE library will try all default CA certificates stored
|
|
* in Android system to do the validation. Otherwise, it will only use the provided CA
|
|
* certificate.
|
|
* @param clientEndCert the end certificate for remote server to verify locally generated
|
|
* signature.
|
|
* @param clientIntermediateCerts intermediate certificates for the remote server to
|
|
* validate the end certificate.
|
|
* @param clientPrivateKey private key to generate outbound digital signature. The {@link
|
|
* PrivateKey} MUST be an instance of {@link RSAKey}.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
// #getLocalAuthConfig and #getRemoveAuthConfig are defined to retrieve
|
|
// authentication configurations
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@NonNull
|
|
public Builder setAuthDigitalSignature(
|
|
@Nullable X509Certificate serverCaCert,
|
|
@NonNull X509Certificate clientEndCert,
|
|
@NonNull List<X509Certificate> clientIntermediateCerts,
|
|
@NonNull PrivateKey clientPrivateKey) {
|
|
if (clientEndCert == null
|
|
|| clientIntermediateCerts == null
|
|
|| clientPrivateKey == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
if (!(clientPrivateKey instanceof RSAKey)) {
|
|
throw new IllegalArgumentException("Unsupported private key type");
|
|
}
|
|
|
|
IkeAuthConfig localConfig =
|
|
new IkeAuthDigitalSignLocalConfig(
|
|
clientEndCert, clientIntermediateCerts, clientPrivateKey);
|
|
IkeAuthConfig remoteConfig = new IkeAuthDigitalSignRemoteConfig(serverCaCert);
|
|
|
|
return setAuth(localConfig, remoteConfig);
|
|
}
|
|
|
|
/**
|
|
* Adds a configuration request. Internal use only.
|
|
*
|
|
* @hide
|
|
*/
|
|
@NonNull
|
|
private Builder addConfigRequest(IkeConfigAttribute configReq) {
|
|
mConfigRequestList.add(configReq);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Adds a specific internal P_CSCF server request to the {@link IkeSessionParams} being
|
|
* built.
|
|
*
|
|
* @param address the requested P_CSCF address.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
// #getConfigurationRequests is defined to retrieve PCSCF server requests
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@SystemApi
|
|
@NonNull
|
|
public Builder addPcscfServerRequest(@NonNull InetAddress address) {
|
|
if (address == null) {
|
|
throw new NullPointerException("Required argument not provided");
|
|
}
|
|
|
|
if (address instanceof Inet4Address) {
|
|
return addConfigRequest(new ConfigAttributeIpv4Pcscf((Inet4Address) address));
|
|
} else if (address instanceof Inet6Address) {
|
|
return addConfigRequest(new ConfigAttributeIpv6Pcscf((Inet6Address) address));
|
|
} else {
|
|
throw new IllegalArgumentException("Invalid address family");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a internal P_CSCF server request to the {@link IkeSessionParams} being built.
|
|
*
|
|
* @param addressFamily the address family. Only {@code AF_INET} and {@code AF_INET6} are
|
|
* allowed.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
// #getConfigurationRequests is defined to retrieve PCSCF server requests
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@SystemApi
|
|
@NonNull
|
|
public Builder addPcscfServerRequest(int addressFamily) {
|
|
if (addressFamily == AF_INET) {
|
|
return addConfigRequest(new ConfigAttributeIpv4Pcscf());
|
|
} else if (addressFamily == AF_INET6) {
|
|
return addConfigRequest(new ConfigAttributeIpv6Pcscf());
|
|
} else {
|
|
throw new IllegalArgumentException("Invalid address family: " + addressFamily);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets hard and soft lifetimes.
|
|
*
|
|
* <p>Lifetimes will not be negotiated with the remote IKE server.
|
|
*
|
|
* @param hardLifetimeSeconds number of seconds after which IKE SA will expire. Defaults to
|
|
* 14400 seconds (4 hours). MUST be a value from 300 seconds (5 minutes) to 86400
|
|
* seconds (24 hours), inclusive.
|
|
* @param softLifetimeSeconds number of seconds after which IKE SA will request rekey.
|
|
* Defaults to 7200 seconds (2 hours). MUST be at least 120 seconds (2 minutes), and at
|
|
* least 60 seconds (1 minute) shorter than the hard lifetime.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
// #getHardLifetimeSeconds and #getSoftLifetimeSeconds are defined for callers to retrieve
|
|
// the lifetimes
|
|
@SuppressLint("MissingGetterMatchingBuilder")
|
|
@NonNull
|
|
public Builder setLifetimeSeconds(
|
|
@IntRange(from = IKE_HARD_LIFETIME_SEC_MINIMUM, to = IKE_HARD_LIFETIME_SEC_MAXIMUM)
|
|
int hardLifetimeSeconds,
|
|
@IntRange(from = IKE_SOFT_LIFETIME_SEC_MINIMUM, to = IKE_HARD_LIFETIME_SEC_MAXIMUM)
|
|
int softLifetimeSeconds) {
|
|
if (hardLifetimeSeconds < IKE_HARD_LIFETIME_SEC_MINIMUM
|
|
|| hardLifetimeSeconds > IKE_HARD_LIFETIME_SEC_MAXIMUM
|
|
|| softLifetimeSeconds < IKE_SOFT_LIFETIME_SEC_MINIMUM
|
|
|| hardLifetimeSeconds - softLifetimeSeconds
|
|
< IKE_LIFETIME_MARGIN_SEC_MINIMUM) {
|
|
throw new IllegalArgumentException("Invalid lifetime value");
|
|
}
|
|
|
|
mHardLifetimeSec = hardLifetimeSeconds;
|
|
mSoftLifetimeSec = softLifetimeSeconds;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the Dead Peer Detection(DPD) delay in seconds.
|
|
*
|
|
* @param dpdDelaySeconds number of seconds after which IKE SA will initiate DPD if no
|
|
* inbound cryptographically protected IKE message was received. Defaults to 120
|
|
* seconds. MUST be a value greater than or equal to than 20 seconds. Setting the value
|
|
* to {@link IkeSessionParams#IKE_DPD_DELAY_SEC_DISABLED} will disable DPD.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setDpdDelaySeconds(
|
|
@IntRange(from = IKE_DPD_DELAY_SEC_MIN) int dpdDelaySeconds) {
|
|
if (dpdDelaySeconds < IKE_DPD_DELAY_SEC_MIN) {
|
|
throw new IllegalArgumentException("Invalid DPD delay value");
|
|
}
|
|
mDpdDelaySec = dpdDelaySeconds;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the Network Address Translation Traversal (NATT) keepalive delay in seconds.
|
|
*
|
|
* @param nattKeepaliveDelaySeconds number of seconds between keepalive packet
|
|
* transmissions. Defaults to 10 seconds. MUST be a value from 10 seconds to 3600
|
|
* seconds, inclusive.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setNattKeepAliveDelaySeconds(
|
|
@IntRange(
|
|
from = IKE_NATT_KEEPALIVE_DELAY_SEC_MIN,
|
|
to = IKE_NATT_KEEPALIVE_DELAY_SEC_MAX)
|
|
int nattKeepaliveDelaySeconds) {
|
|
if (nattKeepaliveDelaySeconds < IKE_NATT_KEEPALIVE_DELAY_SEC_MIN
|
|
|| nattKeepaliveDelaySeconds > IKE_NATT_KEEPALIVE_DELAY_SEC_MAX) {
|
|
throw new IllegalArgumentException("Invalid NATT keepalive delay value");
|
|
}
|
|
mNattKeepaliveDelaySec = nattKeepaliveDelaySeconds;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the DSCP field of the IKE packets.
|
|
*
|
|
* <p>Differentiated services code point (DSCP) is a 6-bit field in the IP header that is
|
|
* used for packet classification and prioritization. The DSCP field is encoded in the 6
|
|
* higher order bits of the Type of Service (ToS) in IPv4 header, or the traffic class (TC)
|
|
* field in IPv6 header.
|
|
*
|
|
* <p>Any 6-bit values (0 to 63) are acceptable, whether IANA-defined, or
|
|
* implementation-specific values.
|
|
*
|
|
* @see <a href="https://tools.ietf.org/html/rfc2474">RFC 2474, Definition of the
|
|
* Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers</a>
|
|
* @see <a href="https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml">
|
|
* Differentiated Services Field Codepoints (DSCP)</a>
|
|
* @param dscp the dscp value. Defaults to 0.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@NonNull
|
|
public Builder setDscp(@IntRange(from = DSCP_MIN, to = DSCP_MAX) int dscp) {
|
|
if (dscp < DSCP_MIN || dscp > DSCP_MAX) {
|
|
throw new IllegalArgumentException("Invalid DSCP value");
|
|
}
|
|
mDscp = dscp;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the IP version to use for ESP packets.
|
|
*
|
|
* @param ipVersion the IP version to use.
|
|
* @return the {@code Builder} to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
@NonNull
|
|
public Builder setIpVersion(@EspIpVersion int ipVersion) {
|
|
if (ESP_IP_VERSION_AUTO != ipVersion
|
|
&& ESP_IP_VERSION_IPV4 != ipVersion
|
|
&& ESP_IP_VERSION_IPV6 != ipVersion) {
|
|
throw new IllegalArgumentException("Invalid IP version : " + ipVersion);
|
|
}
|
|
mIpVersion = ipVersion;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the encapsulation type to use for ESP packets.
|
|
*
|
|
* @param encapType the IP version to use.
|
|
* @return the {@code Builder} to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
|
@NonNull
|
|
public Builder setEncapType(@EspEncapType int encapType) {
|
|
if (ESP_ENCAP_TYPE_AUTO != encapType
|
|
&& ESP_ENCAP_TYPE_NONE != encapType
|
|
&& ESP_ENCAP_TYPE_UDP != encapType) {
|
|
throw new IllegalArgumentException("Invalid encap type : " + encapType);
|
|
}
|
|
mEncapType = encapType;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the retransmission timeout list in milliseconds.
|
|
*
|
|
* <p>Configures the retransmission by providing an array of relative retransmission
|
|
* timeouts in milliseconds. After sending out a request and before receiving the response,
|
|
* the IKE Session will iterate through the array and wait for the relative timeout before
|
|
* the next retry. If the last timeout is exceeded, the IKE Session will be terminated.
|
|
*
|
|
* <p>Each element in the array MUST be a value from 500 ms to 1800000 ms (30 minutes). The
|
|
* length of the array MUST NOT exceed 10. This retransmission timeout list defaults to
|
|
* {0.5s, 1s, 2s, 4s, 8s}
|
|
*
|
|
* @param retransTimeoutMillisList the array of relative retransmission timeout in
|
|
* milliseconds.
|
|
* @return Builder this, to facilitate chaining.
|
|
*/
|
|
@NonNull
|
|
public Builder setRetransmissionTimeoutsMillis(@NonNull int[] retransTimeoutMillisList) {
|
|
boolean isValid = true;
|
|
if (retransTimeoutMillisList == null
|
|
|| retransTimeoutMillisList.length == 0
|
|
|| retransTimeoutMillisList.length > IKE_RETRANS_MAX_ATTEMPTS_MAX) {
|
|
isValid = false;
|
|
}
|
|
for (int t : retransTimeoutMillisList) {
|
|
if (t < IKE_RETRANS_TIMEOUT_MS_MIN || t > IKE_RETRANS_TIMEOUT_MS_MAX) {
|
|
isValid = false;
|
|
}
|
|
}
|
|
if (!isValid) throw new IllegalArgumentException("Invalid retransmission timeout list");
|
|
|
|
mRetransTimeoutMsList = retransTimeoutMillisList;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets a list of retransmission timeouts in milliseconds for performing on-demand liveness
|
|
* checks.
|
|
*
|
|
* <p>Provides the user the ability to set an array of relative retransmission timeouts for
|
|
* on-demand liveness checks in milliseconds. After sending out a request and before
|
|
* receiving the response, the IKE Session will iterate through the array and wait for the
|
|
* relative timeout before the next retry. If the last timeout is exceeded, the IKE Session
|
|
* will be terminated.
|
|
*
|
|
* <p>Each element in the array MUST be a value from 500 ms to 30000 ms. The length of the
|
|
* array MUST NOT exceed 10. The total retransmission timeouts MUST NOT exceed 30000 ms.
|
|
* This retransmission timeout list defaults to {0.5s, 1s, 2s, 4s, 8s}.
|
|
*
|
|
* @param retransTimeoutMillisList the array of relative retransmission timeout in
|
|
* milliseconds for checking peer's liveness.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@FlaggedApi("com.android.ipsec.flags.liveness_check_api")
|
|
@NonNull
|
|
public Builder setLivenessRetransmissionTimeoutsMillis(
|
|
@NonNull int[] retransTimeoutMillisList) {
|
|
boolean isValid = true;
|
|
int totalTimeoutMs = 0;
|
|
if (retransTimeoutMillisList == null
|
|
|| retransTimeoutMillisList.length == 0
|
|
|| retransTimeoutMillisList.length > LIVENESS_RETRANS_MAX_ATTEMPTS_MAX) {
|
|
isValid = false;
|
|
}
|
|
for (int t : retransTimeoutMillisList) {
|
|
totalTimeoutMs += t;
|
|
if (t < LIVENESS_RETRANS_TIMEOUT_MS_MIN || t > LIVENESS_RETRANS_TIMEOUT_MS_MAX) {
|
|
isValid = false;
|
|
}
|
|
}
|
|
if (totalTimeoutMs > LIVENESS_RETRANS_TIMEOUT_MS_TOTAL) {
|
|
isValid = false;
|
|
}
|
|
|
|
if (!isValid) {
|
|
throw new IllegalArgumentException("Invalid liveness retransmission timeout list.");
|
|
}
|
|
mLivenessRetransTimeoutMsList = retransTimeoutMillisList;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the parameters to be used for 3GPP-specific behavior during the IKE Session.
|
|
*
|
|
* <p>Setting the Ike3gppExtension also enables support for non-configurable payloads, such
|
|
* as the Notify - BACKOFF_TIMER payload.
|
|
*
|
|
* @see 3GPP ETSI TS 24.302: Access to the 3GPP Evolved Packet Core (EPC) via non-3GPP
|
|
* access networks
|
|
* @param ike3gppExtension the Ike3gppExtension to use for this IKE Session.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@NonNull
|
|
public Builder setIke3gppExtension(@NonNull Ike3gppExtension ike3gppExtension) {
|
|
Objects.requireNonNull(ike3gppExtension, "ike3gppExtension must not be null");
|
|
|
|
mIke3gppExtension = ike3gppExtension;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the specified IKE Option as enabled.
|
|
*
|
|
* @param ikeOption the option to be enabled.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @throws IllegalArgumentException if the provided option is invalid.
|
|
*/
|
|
@NonNull
|
|
public Builder addIkeOption(@IkeOption int ikeOption) {
|
|
return addIkeOptionInternal(ikeOption);
|
|
}
|
|
|
|
/** @hide */
|
|
@NonNull
|
|
public Builder addIkeOptionInternal(@IkeOption int ikeOption) {
|
|
validateIkeOptionOrThrow(ikeOption);
|
|
if (ikeOption == IKE_OPTION_MOBIKE || ikeOption == IKE_OPTION_REKEY_MOBILITY) {
|
|
if (!SdkLevel.isAtLeastS()) {
|
|
throw new UnsupportedOperationException("Mobility only supported for S/S+");
|
|
} else if (!SdkLevel.isAtLeastT() && ikeOption == IKE_OPTION_MOBIKE) {
|
|
// Automatically enable IKE_OPTION_REKEY_MOBILITY if S <= SDK < T for
|
|
// compatibility
|
|
mIkeOptions |= getOptionBitValue(IKE_OPTION_REKEY_MOBILITY);
|
|
}
|
|
}
|
|
|
|
mIkeOptions |= getOptionBitValue(ikeOption);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Resets (disables) the specified IKE Option.
|
|
*
|
|
* @param ikeOption the option to be disabled.
|
|
* @return Builder this, to facilitate chaining.
|
|
* @throws IllegalArgumentException if the provided option is invalid.
|
|
*/
|
|
// Use #removeIkeOption instead of #clearIkeOption because "clear" sounds indicating
|
|
// clearing all enabled IKE options
|
|
@SuppressLint("BuilderSetStyle")
|
|
@NonNull
|
|
public Builder removeIkeOption(@IkeOption int ikeOption) {
|
|
validateIkeOptionOrThrow(ikeOption);
|
|
mIkeOptions &= ~getOptionBitValue(ikeOption);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Validates and builds the {@link IkeSessionParams}.
|
|
*
|
|
* @return IkeSessionParams the validated IkeSessionParams.
|
|
*/
|
|
@NonNull
|
|
public IkeSessionParams build() {
|
|
if (mSaProposalList.isEmpty()) {
|
|
throw new IllegalArgumentException("IKE SA proposal not found");
|
|
}
|
|
|
|
// TODO: b/178389011 This code block should be removed when
|
|
// IkeSessionParams#getNetwork() and #Builder(Context) can be safely removed. This block
|
|
// makes sure if the Builder is constructed with the deprecated constructor
|
|
// #Builder(Context), #build() still works in the same way and will throw exception when
|
|
// there is no configured or default network.
|
|
Network defaultOrConfiguredNetwork = mCallerConfiguredNetwork;
|
|
if (mConnectivityManager != null && defaultOrConfiguredNetwork == null) {
|
|
defaultOrConfiguredNetwork = mConnectivityManager.getActiveNetwork();
|
|
if (defaultOrConfiguredNetwork == null) {
|
|
throw new IllegalArgumentException("Network not found");
|
|
}
|
|
}
|
|
|
|
if (mServerHostname == null
|
|
|| mLocalIdentification == null
|
|
|| mRemoteIdentification == null
|
|
|| mLocalAuthConfig == null
|
|
|| mRemoteAuthConfig == null) {
|
|
throw new IllegalArgumentException("Necessary parameter missing.");
|
|
}
|
|
|
|
if ((mIkeOptions & getOptionBitValue(IKE_OPTION_EAP_ONLY_AUTH)) != 0) {
|
|
if (!(mLocalAuthConfig instanceof IkeAuthEapConfig)) {
|
|
throw new IllegalArgumentException(
|
|
"If IKE_OPTION_EAP_ONLY_AUTH is set,"
|
|
+ " eap authentication needs to be configured.");
|
|
}
|
|
|
|
IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) mLocalAuthConfig;
|
|
if (!ikeAuthEapConfig.getEapConfig().areAllMethodsEapOnlySafe()) {
|
|
throw new IllegalArgumentException(
|
|
"Only EAP-only safe method allowed" + " when using EAP-only option.");
|
|
}
|
|
}
|
|
|
|
// as of today, the device_identity feature is only implemented for EAP-AKA
|
|
if ((mIke3gppExtension != null
|
|
&& mIke3gppExtension.getIke3gppParams().getMobileDeviceIdentity() != null)) {
|
|
if (!(mLocalAuthConfig instanceof IkeAuthEapConfig)
|
|
|| ((IkeAuthEapConfig) mLocalAuthConfig).getEapConfig().getEapAkaConfig()
|
|
== null) {
|
|
throw new IllegalArgumentException(
|
|
"If device identity is set in Ike3gppParams, then EAP-KA MUST be"
|
|
+ " configured as an acceptable authentication method");
|
|
}
|
|
}
|
|
|
|
if (mLocalAuthConfig.mAuthMethod == IKE_AUTH_METHOD_PUB_KEY_SIGNATURE
|
|
&& mLocalIdentification.idType == IkeIdentification.ID_TYPE_KEY_ID) {
|
|
throw new IllegalArgumentException(
|
|
"It is not allowed to use KEY_ID as local ID when local authentication"
|
|
+ " method is digital-signature-based");
|
|
}
|
|
|
|
if ((mIpVersion == ESP_IP_VERSION_IPV4 && mEncapType == ESP_ENCAP_TYPE_NONE)
|
|
|| (mIpVersion == ESP_IP_VERSION_IPV6 && mEncapType == ESP_ENCAP_TYPE_UDP)) {
|
|
throw new UnsupportedOperationException("Sending packets with IPv4 ESP or IPv6 UDP"
|
|
+ " are not supported");
|
|
}
|
|
|
|
return new IkeSessionParams(
|
|
mServerHostname,
|
|
defaultOrConfiguredNetwork,
|
|
mCallerConfiguredNetwork,
|
|
mSaProposalList.toArray(new IkeSaProposal[0]),
|
|
mLocalIdentification,
|
|
mRemoteIdentification,
|
|
mLocalAuthConfig,
|
|
mRemoteAuthConfig,
|
|
mConfigRequestList.toArray(new IkeConfigAttribute[0]),
|
|
mRetransTimeoutMsList,
|
|
mLivenessRetransTimeoutMsList,
|
|
mIke3gppExtension,
|
|
mIkeOptions,
|
|
mHardLifetimeSec,
|
|
mSoftLifetimeSec,
|
|
mDpdDelaySec,
|
|
mNattKeepaliveDelaySec,
|
|
mDscp,
|
|
mIpVersion,
|
|
mEncapType,
|
|
mIsIkeFragmentationSupported);
|
|
}
|
|
|
|
// TODO: add methods for supporting IKE fragmentation.
|
|
}
|
|
|
|
/**
|
|
* Dumps the state of {@link IkeSessionParams}
|
|
*
|
|
* @param pw {@link PrintWriter} to write the state of the object.
|
|
* @param prefix prefix for indentation
|
|
* @hide
|
|
*/
|
|
public void dump(PrintWriter pw, String prefix) {
|
|
// Please make sure that the dump is thread-safe
|
|
// so the client won't get a crash or exception when adding codes to the dump.
|
|
|
|
pw.println("------------------------------");
|
|
pw.println("IkeSessionParams:");
|
|
pw.println(prefix + "Caller configured network: " + mCallerConfiguredNetwork);
|
|
pw.println(prefix + "Dpd Delay timer in secs: " + mDpdDelaySec);
|
|
pw.println(prefix + "Dscp: " + mDscp);
|
|
pw.println(prefix + "Esp ip version: " + IP_VERSION_TO_STR.get(mIpVersion));
|
|
pw.println(prefix + "Esp encap type: " + ENCAP_TYPE_TO_STR.get(mEncapType));
|
|
pw.println(prefix + "Force port4500 status: " + hasIkeOption(IKE_OPTION_FORCE_PORT_4500));
|
|
pw.println(prefix + "Hard life time in secs: " + mHardLifetimeSec);
|
|
pw.println(
|
|
prefix
|
|
+ "Liveness retransmission timer in millis : "
|
|
+ Arrays.toString(mLivenessRetransTimeoutMsList));
|
|
pw.println(prefix + "Nat keep alive delay in secs: " + mNattKeepaliveDelaySec);
|
|
pw.println(prefix + "Soft life time in secs: " + mSoftLifetimeSec);
|
|
pw.println(prefix + "Remote host name: " + mServerHostname);
|
|
pw.println(
|
|
prefix
|
|
+ "Retransmission timer in millis : "
|
|
+ Arrays.toString(mRetransTimeoutMsList));
|
|
for (IkeSaProposal saProposal : getIkeSaProposals()) {
|
|
pw.println();
|
|
pw.println(prefix + "IkeSaProposal:");
|
|
pw.println(
|
|
prefix
|
|
+ "Encryption algorithm: "
|
|
+ saProposal.getEncryptionAlgorithms().toString());
|
|
pw.println(
|
|
prefix
|
|
+ "Integrity algorithm: "
|
|
+ saProposal.getIntegrityAlgorithms().toString());
|
|
pw.println(prefix + "Dh Group algorithm: " + saProposal.getDhGroups().toString());
|
|
pw.println(
|
|
prefix + "Prf algorithm: " + saProposal.getPseudorandomFunctions().toString());
|
|
}
|
|
pw.println("------------------------------");
|
|
pw.println();
|
|
}
|
|
}
|