blob: 11227e17d97ee1a90afb06a0405eec62358d68ed [file] [log] [blame]
/*
* Copyright (C) 2018 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 android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.PersistableBundle;
import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.net.ipsec.ike.message.IkePayload;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform;
import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform;
import com.android.server.vcn.util.PersistableBundleUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* SaProposal represents a proposed configuration to negotiate an IKE or Child SA.
*
* <p>SaProposal will contain cryptograhic algorithms and key generation materials for the
* negotiation of an IKE or Child SA.
*
* <p>User must provide at least one valid SaProposal when they are creating a new IKE or Child SA.
*
* @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
* Protocol Version 2 (IKEv2)</a>
*/
public abstract class SaProposal {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
ENCRYPTION_ALGORITHM_3DES,
ENCRYPTION_ALGORITHM_AES_CBC,
ENCRYPTION_ALGORITHM_AES_CTR,
ENCRYPTION_ALGORITHM_AES_GCM_8,
ENCRYPTION_ALGORITHM_AES_GCM_12,
ENCRYPTION_ALGORITHM_AES_GCM_16,
ENCRYPTION_ALGORITHM_CHACHA20_POLY1305
})
public @interface EncryptionAlgorithm {}
/** 3DES Encryption/Ciphering Algorithm. */
public static final int ENCRYPTION_ALGORITHM_3DES = 3;
/** AES-CBC Encryption/Ciphering Algorithm. */
public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
/** AES-CTR Encryption/Ciphering Algorithm. */
public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13;
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 8-octet ICV
* (truncation).
*/
public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18;
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 12-octet ICV
* (truncation).
*/
public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19;
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV
* (truncation).
*/
public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20;
/**
* ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV
* (truncation).
*/
public static final int ENCRYPTION_ALGORITHM_CHACHA20_POLY1305 = 28;
/** @hide */
protected static final SparseArray<String> SUPPORTED_ENCRYPTION_ALGO_TO_STR;
static {
SUPPORTED_ENCRYPTION_ALGO_TO_STR = new SparseArray<>();
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_3DES, "ENCR_3DES");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CBC, "ENCR_AES_CBC");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CTR, "ENCR_AES_CTR");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_8, "ENCR_AES_GCM_8");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_12, "ENCR_AES_GCM_12");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_16, "ENCR_AES_GCM_16");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(
ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, "ENCR_CHACHA20_POLY1305");
}
/**
* Key length unused.
*
* <p>This value should only be used with the Encryption/Ciphering Algorithm that accepts a
* fixed key size such as {@link ENCRYPTION_ALGORITHM_3DES}.
*/
public static final int KEY_LEN_UNUSED = 0;
/** AES Encryption/Ciphering Algorithm key length 128 bits. */
public static final int KEY_LEN_AES_128 = 128;
/** AES Encryption/Ciphering Algorithm key length 192 bits. */
public static final int KEY_LEN_AES_192 = 192;
/** AES Encryption/Ciphering Algorithm key length 256 bits. */
public static final int KEY_LEN_AES_256 = 256;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PSEUDORANDOM_FUNCTION_HMAC_SHA1,
PSEUDORANDOM_FUNCTION_AES128_XCBC,
PSEUDORANDOM_FUNCTION_SHA2_256,
PSEUDORANDOM_FUNCTION_SHA2_384,
PSEUDORANDOM_FUNCTION_SHA2_512
})
public @interface PseudorandomFunction {}
/** HMAC-SHA1 Pseudorandom Function. */
public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2;
/** AES128-XCBC Pseudorandom Function. */
public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4;
/** HMAC-SHA2-256 Pseudorandom Function. */
public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5;
/** HMAC-SHA2-384 Pseudorandom Function. */
public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6;
/** HMAC-SHA2-384 Pseudorandom Function. */
public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7;
/** @hide */
protected static final SparseArray<String> SUPPORTED_PRF_TO_STR;
static {
SUPPORTED_PRF_TO_STR = new SparseArray<>();
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_HMAC_SHA1, "PRF_HMAC_SHA1");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_XCBC, "PRF_AES128_XCBC");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_256, "PRF_HMAC2_256");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_384, "PRF_HMAC2_384");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_512, "PRF_HMAC2_512");
}
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
INTEGRITY_ALGORITHM_NONE,
INTEGRITY_ALGORITHM_HMAC_SHA1_96,
INTEGRITY_ALGORITHM_AES_XCBC_96,
INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
})
public @interface IntegrityAlgorithm {}
/** None Authentication/Integrity Algorithm. */
public static final int INTEGRITY_ALGORITHM_NONE = 0;
/** HMAC-SHA1 Authentication/Integrity Algorithm. */
public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2;
/** AES-XCBC-96 Authentication/Integrity Algorithm. */
public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5;
/** HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation. */
public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12;
/** HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation. */
public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13;
/** HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation. */
public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14;
/** @hide */
protected static final SparseArray<String> SUPPORTED_INTEGRITY_ALGO_TO_STR;
static {
SUPPORTED_INTEGRITY_ALGO_TO_STR = new SparseArray<>();
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_NONE, "AUTH_NONE");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_XCBC_96, "AUTH_AES_XCBC_96");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(
INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, "AUTH_HMAC_SHA2_256_128");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(
INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, "AUTH_HMAC_SHA2_384_192");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(
INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, "AUTH_HMAC_SHA2_512_256");
}
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
DH_GROUP_NONE,
DH_GROUP_1024_BIT_MODP,
DH_GROUP_1536_BIT_MODP,
DH_GROUP_2048_BIT_MODP,
DH_GROUP_3072_BIT_MODP,
DH_GROUP_4096_BIT_MODP
})
public @interface DhGroup {}
/** None Diffie-Hellman Group. */
public static final int DH_GROUP_NONE = 0;
/** 1024-bit MODP Diffie-Hellman Group. */
public static final int DH_GROUP_1024_BIT_MODP = 2;
/** 1536-bit MODP Diffie-Hellman Group. */
public static final int DH_GROUP_1536_BIT_MODP = 5;
/** 2048-bit MODP Diffie-Hellman Group. */
public static final int DH_GROUP_2048_BIT_MODP = 14;
/** 3072-bit MODP Diffie-Hellman Group. */
public static final int DH_GROUP_3072_BIT_MODP = 15;
/** 4096-bit MODP Diffie-Hellman Group. */
public static final int DH_GROUP_4096_BIT_MODP = 16;
private static final SparseArray<String> SUPPORTED_DH_GROUP_TO_STR;
static {
SUPPORTED_DH_GROUP_TO_STR = new SparseArray<>();
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_NONE, "DH_NONE");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1024_BIT_MODP, "DH_1024_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1536_BIT_MODP, "DH_1536_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_2048_BIT_MODP, "DH_2048_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_3072_BIT_MODP, "DH_3072_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_4096_BIT_MODP, "DH_4096_BIT_MODP");
}
private static final String PROTOCOL_ID_KEY = "mProtocolId";
/** @hide */
protected static final String ENCRYPT_ALGO_KEY = "mEncryptionAlgorithms";
/** @hide */
protected static final String INTEGRITY_ALGO_KEY = "mIntegrityAlgorithms";
/** @hide */
protected static final String DH_GROUP_KEY = "mDhGroups";
@IkePayload.ProtocolId private final int mProtocolId;
private final EncryptionTransform[] mEncryptionAlgorithms;
private final IntegrityTransform[] mIntegrityAlgorithms;
private final DhGroupTransform[] mDhGroups;
/** @hide */
protected SaProposal(
@IkePayload.ProtocolId int protocol,
EncryptionTransform[] encryptionAlgos,
IntegrityTransform[] integrityAlgos,
DhGroupTransform[] dhGroups) {
mProtocolId = protocol;
mEncryptionAlgorithms = encryptionAlgos;
mIntegrityAlgorithms = integrityAlgos;
mDhGroups = dhGroups;
}
/**
* Constructs this object by deserializing a PersistableBundle
*
* @hide
*/
@NonNull
public static SaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
int protocolId = in.getInt(PROTOCOL_ID_KEY);
switch (protocolId) {
case IkePayload.PROTOCOL_ID_IKE:
return IkeSaProposal.fromPersistableBundle(in);
case IkePayload.PROTOCOL_ID_ESP:
return ChildSaProposal.fromPersistableBundle(in);
default:
throw new IllegalArgumentException("Invalid protocol ID " + protocolId);
}
}
/**
* Serializes this object to a PersistableBundle
*
* @hide
*/
@NonNull
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = new PersistableBundle();
result.putInt(PROTOCOL_ID_KEY, mProtocolId);
PersistableBundle encryptionBundle =
PersistableBundleUtils.fromList(
Arrays.asList(mEncryptionAlgorithms),
EncryptionTransform::toPersistableBundle);
result.putPersistableBundle(ENCRYPT_ALGO_KEY, encryptionBundle);
int[] integrityAlgoIdArray = getIntegrityAlgorithms().stream().mapToInt(i -> i).toArray();
result.putIntArray(INTEGRITY_ALGO_KEY, integrityAlgoIdArray);
int[] dhGroupArray = getDhGroups().stream().mapToInt(i -> i).toArray();
result.putIntArray(DH_GROUP_KEY, dhGroupArray);
return result;
}
/**
* Check if the current SaProposal from the SA responder is consistent with the selected
* reqProposal from the SA initiator.
*
* <p>As per RFC 7296, The accepted cryptographic suite MUST contain exactly one transform of
* each type included in the proposal. But for interoperability reason, IKE library allows
* exceptions when the accepted suite or the request proposal has a NONE value transform.
* Currently only IntegrityTransform and DhGroupTransform have NONE value transform ID defined.
*
* @param reqProposal selected SaProposal from SA initiator
* @return if current SaProposal from SA responder is consistent with the selected reqProposal
* from SA initiator.
* @hide
*/
public boolean isNegotiatedFrom(SaProposal reqProposal) {
return this.mProtocolId == reqProposal.mProtocolId
&& isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms)
&& isIntegrityTransformSelectedFrom(
mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms)
&& isDhGroupTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups);
}
/**
* Check if the response transform can be selected from the request transforms
*
* <p>Package private
*/
static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) {
// If the selected proposal has multiple transforms with the same type, the responder MUST
// choose a single one.
if ((selected.length > 1) || (selected.length == 0) != (selectFrom.length == 0)) {
return false;
}
if (selected.length == 0) return true;
return Arrays.asList(selectFrom).contains(selected[0]);
}
/**
* Check if the response integrity transform can be selected from the request integrity
* transforms.
*
* <p>For interoperability reason, it is allowed to do not include integrity transform in the
* response proposal when the request proposal has a NONE value integrity transform; and it is
* also allowed to have a NONE value integrity transform when the request proposal does not have
* integrity transforms.
*/
private static boolean isIntegrityTransformSelectedFrom(
IntegrityTransform[] selected, IntegrityTransform[] selectFrom) {
if (selected.length == 0) {
selected = new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)};
}
if (selectFrom.length == 0) {
selectFrom =
new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)};
}
return isTransformSelectedFrom(selected, selectFrom);
}
/**
* Check if the response DH group can be selected from the request DH groups
*
* <p>For interoperability reason, it is allowed to do not include DH group in the response
* proposal when the request proposal has a NONE value DH group; and it is also allowed to have
* a NONE value DH group when the request proposal does not have DH groups.
*/
private static boolean isDhGroupTransformSelectedFrom(
DhGroupTransform[] selected, DhGroupTransform[] selectFrom) {
if (selected.length == 0) {
selected = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)};
}
if (selectFrom.length == 0) {
selectFrom = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)};
}
return isTransformSelectedFrom(selected, selectFrom);
}
/** @hide */
@IkePayload.ProtocolId
public int getProtocolId() {
return mProtocolId;
}
/**
* Gets all proposed encryption algorithms
*
* @return A list of Pairs, with the IANA-defined ID for the proposed encryption algorithm as
* the first item, and the key length (in bits) as the second.
*/
@NonNull
public List<Pair<Integer, Integer>> getEncryptionAlgorithms() {
final List<Pair<Integer, Integer>> result = new ArrayList<>();
for (EncryptionTransform transform : mEncryptionAlgorithms) {
result.add(new Pair(transform.id, transform.getSpecifiedKeyLength()));
}
return result;
}
/**
* Gets all proposed integrity algorithms
*
* @return A list of the IANA-defined IDs for the proposed integrity algorithms
*/
@NonNull
public List<Integer> getIntegrityAlgorithms() {
final List<Integer> result = new ArrayList<>();
for (Transform transform : mIntegrityAlgorithms) {
result.add(transform.id);
}
return result;
}
/**
* Gets all proposed Diffie-Hellman groups
*
* @return A list of the IANA-defined IDs for the proposed Diffie-Hellman groups
*/
@NonNull
public List<Integer> getDhGroups() {
final List<Integer> result = new ArrayList<>();
for (Transform transform : mDhGroups) {
result.add(transform.id);
}
return result;
}
/** @hide */
public EncryptionTransform[] getEncryptionTransforms() {
return mEncryptionAlgorithms;
}
/** @hide */
public IntegrityTransform[] getIntegrityTransforms() {
return mIntegrityAlgorithms;
}
/** @hide */
public DhGroupTransform[] getDhGroupTransforms() {
return mDhGroups;
}
/** @hide */
protected List<Transform> getAllTransformsAsList() {
List<Transform> transformList = new LinkedList<>();
transformList.addAll(Arrays.asList(mEncryptionAlgorithms));
transformList.addAll(Arrays.asList(mIntegrityAlgorithms));
transformList.addAll(Arrays.asList(mDhGroups));
return transformList;
}
/**
* Return all SA Transforms in this SaProposal to be encoded for building an outbound IKE
* message.
*
* <p>This method should be called by only IKE library.
*
* @return Array of Transforms to be encoded.
* @hide
*/
public abstract Transform[] getAllTransforms();
/**
* This class is an abstract Builder for building a SaProposal.
*
* @hide
*/
protected abstract static class Builder {
protected static final String ERROR_TAG = "Invalid SA Proposal: ";
// Use LinkedHashSet to ensure uniqueness and that ordering is maintained.
protected final LinkedHashSet<EncryptionTransform> mProposedEncryptAlgos =
new LinkedHashSet<>();
protected final LinkedHashSet<PrfTransform> mProposedPrfs = new LinkedHashSet<>();
protected final LinkedHashSet<IntegrityTransform> mProposedIntegrityAlgos =
new LinkedHashSet<>();
protected final LinkedHashSet<DhGroupTransform> mProposedDhGroups = new LinkedHashSet<>();
protected boolean mHasAead = false;
protected static boolean isAead(@EncryptionAlgorithm int algorithm) {
switch (algorithm) {
case ENCRYPTION_ALGORITHM_3DES:
// Fall through
case ENCRYPTION_ALGORITHM_AES_CBC:
// Fall through
case ENCRYPTION_ALGORITHM_AES_CTR:
return false;
case ENCRYPTION_ALGORITHM_AES_GCM_8:
// Fall through
case ENCRYPTION_ALGORITHM_AES_GCM_12:
// Fall through
case ENCRYPTION_ALGORITHM_AES_GCM_16:
// Fall through
case ENCRYPTION_ALGORITHM_CHACHA20_POLY1305:
return true;
default:
// Won't hit here.
throw new IllegalArgumentException("Unsupported Encryption Algorithm.");
}
}
protected EncryptionTransform[] buildEncryptAlgosOrThrow() {
if (mProposedEncryptAlgos.isEmpty()) {
throw new IllegalArgumentException(
ERROR_TAG + "Encryption algorithm must be proposed.");
}
return mProposedEncryptAlgos.toArray(
new EncryptionTransform[mProposedEncryptAlgos.size()]);
}
protected void validateAndAddEncryptAlgo(
@EncryptionAlgorithm int algorithm, int keyLength, boolean isChild) {
// Construct EncryptionTransform and validate proposed algorithm during
// construction.
EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength);
// For Child SA algorithm, check if that is supported by IPsec
if (isChild
&& !ChildSaProposal.getSupportedEncryptionAlgorithms().contains(algorithm)) {
throw new IllegalArgumentException("Unsupported encryption algorithm " + algorithm);
}
// Validate that only one mode encryption algorithm has been proposed.
boolean isCurrentAead = isAead(algorithm);
if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) {
throw new IllegalArgumentException(
ERROR_TAG
+ "Proposal cannot has both normal ciphers "
+ "and combined-mode ciphers.");
}
if (isCurrentAead) mHasAead = true;
mProposedEncryptAlgos.add(encryptionTransform);
}
protected void validateAndAddIntegrityAlgo(
@IntegrityAlgorithm int algorithm, boolean isChild) {
// For Child SA algorithm, check if that is supported by IPsec
if (isChild && !ChildSaProposal.getSupportedIntegrityAlgorithms().contains(algorithm)) {
throw new IllegalArgumentException("Unsupported integrity algorithm " + algorithm);
}
// Construct IntegrityTransform and validate proposed algorithm during
// construction.
mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm));
}
protected void addDh(@DhGroup int dhGroup) {
// Construct DhGroupTransform and validate proposed dhGroup during
// construction.
mProposedDhGroups.add(new DhGroupTransform(dhGroup));
}
}
/** @hide */
@Override
@NonNull
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(IkePayload.getProtocolTypeString(mProtocolId)).append(": ");
int len = getAllTransforms().length;
for (int i = 0; i < len; i++) {
sb.append(getAllTransforms()[i].toString());
if (i < len - 1) sb.append("|");
}
return sb.toString();
}
@Override
public int hashCode() {
return Objects.hash(
mProtocolId,
Arrays.hashCode(mEncryptionAlgorithms),
Arrays.hashCode(mIntegrityAlgorithms),
Arrays.hashCode(mDhGroups));
}
@Override
public boolean equals(Object o) {
if (!(o instanceof SaProposal)) {
return false;
}
SaProposal other = (SaProposal) o;
return mProtocolId == other.mProtocolId
&& Arrays.equals(mEncryptionAlgorithms, other.mEncryptionAlgorithms)
&& Arrays.equals(mIntegrityAlgorithms, other.mIntegrityAlgorithms)
&& Arrays.equals(mDhGroups, other.mDhGroups);
}
/** @hide */
protected static Set<Integer> getKeySet(SparseArray array) {
Set<Integer> result = new HashSet<>();
for (int i = 0; i < array.size(); i++) {
result.add(array.keyAt(i));
}
return result;
}
/**
* Returns supported DH groups for IKE and Child SA proposal negotiation.
*
* @hide
*/
@NonNull
public static Set<Integer> getSupportedDhGroups() {
return getKeySet(SUPPORTED_DH_GROUP_TO_STR);
}
/**
* Return the encryption algorithm as a String.
*
* @hide
*/
public static String getEncryptionAlgorithmString(int algorithm) {
if (SUPPORTED_ENCRYPTION_ALGO_TO_STR.contains(algorithm)) {
return SUPPORTED_ENCRYPTION_ALGO_TO_STR.get(algorithm);
}
return "ENC_Unknown_" + algorithm;
}
/**
* Return the pseudorandom function as a String.
*
* @hide
*/
public static String getPseudorandomFunctionString(int algorithm) {
if (SUPPORTED_PRF_TO_STR.contains(algorithm)) {
return SUPPORTED_PRF_TO_STR.get(algorithm);
}
return "PRF_Unknown_" + algorithm;
}
/**
* Return the integrity algorithm as a String.
*
* @hide
*/
public static String getIntegrityAlgorithmString(int algorithm) {
if (SUPPORTED_PRF_TO_STR.contains(algorithm)) {
return SUPPORTED_INTEGRITY_ALGO_TO_STR.get(algorithm);
}
return "AUTH_Unknown_" + algorithm;
}
/**
* Return Diffie-Hellman Group as a String.
*
* @hide
*/
public static String getDhGroupString(int dhGroup) {
if (SUPPORTED_DH_GROUP_TO_STR.contains(dhGroup)) {
return SUPPORTED_DH_GROUP_TO_STR.get(dhGroup);
}
return "DH_Unknown_" + dhGroup;
}
}