| /* |
| * 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; |
| } |
| } |