Merge changes I1de322c9,Id0983d4b into sc-dev am: 453acbbf29

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14687246

Change-Id: If93dfeab89c644f67001c144253a594996c96eef
This commit is contained in:
TreeHugger Robot
2021-05-22 05:12:50 +00:00
committed by Automerger Merge Worker
5 changed files with 107 additions and 75 deletions

View File

@@ -16,6 +16,16 @@
package android.net; package android.net;
import static android.net.IpSecAlgorithm.AUTH_AES_CMAC;
import static android.net.IpSecAlgorithm.AUTH_AES_XCBC;
import static android.net.IpSecAlgorithm.AUTH_CRYPT_AES_GCM;
import static android.net.IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305;
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA256;
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.Preconditions.checkStringNotEmpty; import static com.android.internal.util.Preconditions.checkStringNotEmpty;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
@@ -70,13 +80,28 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
private static final String EMPTY_CERT = ""; private static final String EMPTY_CERT = "";
/** @hide */ /** @hide */
public static final List<String> DEFAULT_ALGORITHMS = public static final List<String> DEFAULT_ALGORITHMS;
Collections.unmodifiableList(Arrays.asList(
IpSecAlgorithm.CRYPT_AES_CBC, private static void addAlgorithmIfSupported(List<String> algorithms, String ipSecAlgoName) {
IpSecAlgorithm.AUTH_HMAC_SHA256, if (IpSecAlgorithm.getSupportedAlgorithms().contains(ipSecAlgoName)) {
IpSecAlgorithm.AUTH_HMAC_SHA384, algorithms.add(ipSecAlgoName);
IpSecAlgorithm.AUTH_HMAC_SHA512, }
IpSecAlgorithm.AUTH_CRYPT_AES_GCM)); }
static {
final List<String> algorithms = new ArrayList<>();
addAlgorithmIfSupported(algorithms, CRYPT_AES_CBC);
addAlgorithmIfSupported(algorithms, CRYPT_AES_CTR);
addAlgorithmIfSupported(algorithms, AUTH_HMAC_SHA256);
addAlgorithmIfSupported(algorithms, AUTH_HMAC_SHA384);
addAlgorithmIfSupported(algorithms, AUTH_HMAC_SHA512);
addAlgorithmIfSupported(algorithms, AUTH_AES_XCBC);
addAlgorithmIfSupported(algorithms, AUTH_AES_CMAC);
addAlgorithmIfSupported(algorithms, AUTH_CRYPT_AES_GCM);
addAlgorithmIfSupported(algorithms, AUTH_CRYPT_CHACHA20_POLY1305);
DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms);
}
@NonNull private final String mServerAddr; @NonNull private final String mServerAddr;
@NonNull private final String mUserIdentity; @NonNull private final String mUserIdentity;
@@ -195,8 +220,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
* @param allowedAlgorithms The list to be validated * @param allowedAlgorithms The list to be validated
*/ */
private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) { private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) {
VpnProfile.validateAllowedAlgorithms(algorithmNames);
// First, make sure no insecure algorithms were proposed. // First, make sure no insecure algorithms were proposed.
if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5) if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5)
|| algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) { || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) {

View File

@@ -30,7 +30,10 @@ import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.ProxyUtils; import com.android.net.module.util.ProxyUtils;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -74,6 +77,9 @@ public final class VpnProfile implements Cloneable, Parcelable {
private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0"; private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0";
/** Default URL encoding. */
private static final String DEFAULT_ENCODING = StandardCharsets.UTF_8.name();
// Entity fields. // Entity fields.
@UnsupportedAppUsage @UnsupportedAppUsage
public final String key; // -1 public final String key; // -1
@@ -129,9 +135,6 @@ public final class VpnProfile implements Cloneable, Parcelable {
/** /**
* The list of allowable algorithms. * The list of allowable algorithms.
*
* <p>This list is validated in the setter to ensure that encoding characters (list, value
* delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()}
*/ */
private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19 private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19
public boolean isBypassable = false; // 20 public boolean isBypassable = false; // 20
@@ -196,11 +199,8 @@ public final class VpnProfile implements Cloneable, Parcelable {
* *
* @param allowedAlgorithms the list of allowable algorithms, as listed in {@link * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link
* IpSecAlgorithm}. * IpSecAlgorithm}.
* @throws IllegalArgumentException if any delimiters are used in algorithm names. See {@link
* #VALUE_DELIMITER} and {@link LIST_DELIMITER}.
*/ */
public void setAllowedAlgorithms(List<String> allowedAlgorithms) { public void setAllowedAlgorithms(List<String> allowedAlgorithms) {
validateAllowedAlgorithms(allowedAlgorithms);
mAllowedAlgorithms = allowedAlgorithms; mAllowedAlgorithms = allowedAlgorithms;
} }
@@ -297,7 +297,11 @@ public final class VpnProfile implements Cloneable, Parcelable {
// Either all must be present, or none must be. // Either all must be present, or none must be.
if (values.length >= 24) { if (values.length >= 24) {
profile.mAllowedAlgorithms = Arrays.asList(values[19].split(LIST_DELIMITER)); profile.mAllowedAlgorithms = new ArrayList<>();
for (String algo : Arrays.asList(values[19].split(LIST_DELIMITER))) {
profile.mAllowedAlgorithms.add(URLDecoder.decode(algo, DEFAULT_ENCODING));
}
profile.isBypassable = Boolean.parseBoolean(values[20]); profile.isBypassable = Boolean.parseBoolean(values[20]);
profile.isMetered = Boolean.parseBoolean(values[21]); profile.isMetered = Boolean.parseBoolean(values[21]);
profile.maxMtu = Integer.parseInt(values[22]); profile.maxMtu = Integer.parseInt(values[22]);
@@ -348,7 +352,19 @@ public final class VpnProfile implements Cloneable, Parcelable {
builder.append(ENCODED_NULL_PROXY_INFO); builder.append(ENCODED_NULL_PROXY_INFO);
} }
builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, mAllowedAlgorithms)); final List<String> encodedAlgoNames = new ArrayList<>();
try {
for (String algo : mAllowedAlgorithms) {
encodedAlgoNames.add(URLEncoder.encode(algo, DEFAULT_ENCODING));
}
} catch (UnsupportedEncodingException e) {
// Unexpected error
throw new IllegalStateException("Failed to encode algorithms.", e);
}
builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, encodedAlgoNames));
builder.append(VALUE_DELIMITER).append(isBypassable); builder.append(VALUE_DELIMITER).append(isBypassable);
builder.append(VALUE_DELIMITER).append(isMetered); builder.append(VALUE_DELIMITER).append(isMetered);
builder.append(VALUE_DELIMITER).append(maxMtu); builder.append(VALUE_DELIMITER).append(maxMtu);
@@ -425,20 +441,6 @@ public final class VpnProfile implements Cloneable, Parcelable {
return true; return true;
} }
/**
* Validates that the provided list of algorithms does not contain illegal characters.
*
* @param allowedAlgorithms The list to be validated
*/
public static void validateAllowedAlgorithms(List<String> allowedAlgorithms) {
for (final String alg : allowedAlgorithms) {
if (alg.contains(VALUE_DELIMITER) || alg.contains(LIST_DELIMITER)) {
throw new IllegalArgumentException(
"Algorithm contained illegal ('\0' or ',') character");
}
}
}
/** Generates a hashcode over the VpnProfile. */ /** Generates a hashcode over the VpnProfile. */
@Override @Override
public int hashCode() { public int hashCode() {

View File

@@ -29,8 +29,8 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnProfile; import com.android.internal.net.VpnProfile;
import com.android.net.module.util.ProxyUtils;
import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator; import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
import com.android.net.module.util.ProxyUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -170,7 +170,10 @@ public class Ikev2VpnProfileTest {
final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
builder.setAuthPsk(PSK_BYTES); builder.setAuthPsk(PSK_BYTES);
List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); List<String> allowedAlgorithms =
Arrays.asList(
IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305);
builder.setAllowedAlgorithms(allowedAlgorithms); builder.setAllowedAlgorithms(allowedAlgorithms);
final Ikev2VpnProfile profile = builder.build(); final Ikev2VpnProfile profile = builder.build();
@@ -183,7 +186,12 @@ public class Ikev2VpnProfileTest {
builder.setAuthPsk(PSK_BYTES); builder.setAuthPsk(PSK_BYTES);
List<String> allowedAlgorithms = List<String> allowedAlgorithms =
Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC); Arrays.asList(
IpSecAlgorithm.AUTH_HMAC_SHA512,
IpSecAlgorithm.AUTH_AES_XCBC,
IpSecAlgorithm.AUTH_AES_CMAC,
IpSecAlgorithm.CRYPT_AES_CBC,
IpSecAlgorithm.CRYPT_AES_CTR);
builder.setAllowedAlgorithms(allowedAlgorithms); builder.setAllowedAlgorithms(allowedAlgorithms);
final Ikev2VpnProfile profile = builder.build(); final Ikev2VpnProfile profile = builder.build();

View File

@@ -23,7 +23,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.IpSecAlgorithm; import android.net.IpSecAlgorithm;
@@ -97,6 +96,7 @@ public class VpnProfileTest {
p.setAllowedAlgorithms( p.setAllowedAlgorithms(
Arrays.asList( Arrays.asList(
IpSecAlgorithm.AUTH_CRYPT_AES_GCM, IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.AUTH_HMAC_SHA512,
IpSecAlgorithm.CRYPT_AES_CBC)); IpSecAlgorithm.CRYPT_AES_CBC));
p.isBypassable = true; p.isBypassable = true;
@@ -125,30 +125,6 @@ public class VpnProfileTest {
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23); assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
} }
@Test
public void testSetInvalidAlgorithmValueDelimiter() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
try {
profile.setAllowedAlgorithms(
Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test"));
fail("Expected failure due to value separator in algorithm name");
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testSetInvalidAlgorithmListDelimiter() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
try {
profile.setAllowedAlgorithms(
Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test"));
fail("Expected failure due to value separator in algorithm name");
} catch (IllegalArgumentException expected) {
}
}
@Test @Test
public void testEncodeDecode() { public void testEncodeDecode() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);

View File

@@ -213,19 +213,25 @@ public class VpnIkev2Utils {
/** Builds a child SA proposal based on the allowed IPsec algorithms */ /** Builds a child SA proposal based on the allowed IPsec algorithms */
private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) { private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) {
// TODO: Add new IpSecAlgorithm in the followup commit
final List<ChildSaProposal> proposals = new ArrayList<>(); final List<ChildSaProposal> proposals = new ArrayList<>();
final List<Integer> aesKeyLenOptions =
Arrays.asList(KEY_LEN_AES_256, KEY_LEN_AES_192, KEY_LEN_AES_128);
// Add non-AEAD options // Add non-AEAD options
if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) { if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) {
final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder(); final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
// Encryption Algorithms: // Encryption Algorithms:
// AES-CBC is currently the only supported encryption algorithm. // AES-CBC and AES_CTR are currently the only supported encryption algorithms.
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); for (int len : aesKeyLenOptions) {
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); if (allowedAlgorithms.contains(IpSecAlgorithm.CRYPT_AES_CTR)) {
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, len);
}
if (allowedAlgorithms.contains(IpSecAlgorithm.CRYPT_AES_CBC)) {
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, len);
}
}
// Authentication/Integrity Algorithms: // Authentication/Integrity Algorithms:
// Guaranteed by Ikev2VpnProfile constructor to contain at least one of these. // Guaranteed by Ikev2VpnProfile constructor to contain at least one of these.
@@ -238,6 +244,12 @@ public class VpnIkev2Utils {
if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) { if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) {
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
} }
if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_AES_XCBC)) {
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
}
if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_AES_CMAC)) {
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_CMAC_96);
}
ChildSaProposal proposal = normalModeBuilder.build(); ChildSaProposal proposal = normalModeBuilder.build();
if (proposal.getIntegrityAlgorithms().isEmpty()) { if (proposal.getIntegrityAlgorithms().isEmpty()) {
@@ -252,16 +264,27 @@ public class VpnIkev2Utils {
if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) { if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) {
final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder(); final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
// AES-GCM is currently the only supported AEAD algorithm if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305)) {
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256); aeadBuilder.addEncryptionAlgorithm(
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256); ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, KEY_LEN_UNUSED);
}
if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM)) {
aeadBuilder.addEncryptionAlgorithm(
ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
aeadBuilder.addEncryptionAlgorithm(
ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256); aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192); aeadBuilder.addEncryptionAlgorithm(
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192); ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
aeadBuilder.addEncryptionAlgorithm(
ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192); aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128); aeadBuilder.addEncryptionAlgorithm(
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
aeadBuilder.addEncryptionAlgorithm(
ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
}
proposals.add(aeadBuilder.build()); proposals.add(aeadBuilder.build());
} }