Loading core/java/android/net/Ikev2VpnProfile.java +74 −4 Original line number Diff line number Diff line Loading @@ -70,6 +70,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s"; private static final String EMPTY_CERT = ""; /** @hide */ public static final List<String> DEFAULT_ALGORITHMS = Collections.unmodifiableList(Arrays.asList( IpSecAlgorithm.CRYPT_AES_CBC, IpSecAlgorithm.AUTH_HMAC_SHA256, IpSecAlgorithm.AUTH_HMAC_SHA384, IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)); @NonNull private final String mServerAddr; @NonNull private final String mUserIdentity; Loading Loading @@ -172,7 +181,56 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { throw new IllegalArgumentException("Invalid auth method set"); } VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms); validateAllowedAlgorithms(mAllowedAlgorithms); } /** * Validates that the allowed algorithms are a valid set for IPsec purposes * * <p>In order for the algorithm list to be a valid set, it must contain at least one algorithm * that provides Authentication, and one that provides Encryption. Authenticated Encryption with * Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption. * * @param allowedAlgorithms The list to be validated */ private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) { VpnProfile.validateAllowedAlgorithms(algorithmNames); // First, make sure no insecure algorithms were proposed. if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5) || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) { throw new IllegalArgumentException("Algorithm not supported for IKEv2 VPN profiles"); } // Validate that some valid combination (AEAD or AUTH + CRYPT) is present if (hasAeadAlgorithms(algorithmNames) || hasNormalModeAlgorithms(algorithmNames)) { return; } throw new IllegalArgumentException("Algorithm set missing support for Auth, Crypt or both"); } /** * Checks if the provided list has AEAD algorithms * * @hide */ public static boolean hasAeadAlgorithms(@NonNull List<String> algorithmNames) { return algorithmNames.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); } /** * Checks the provided list has acceptable (non-AEAD) authentication and encryption algorithms * * @hide */ public static boolean hasNormalModeAlgorithms(@NonNull List<String> algorithmNames) { final boolean hasCrypt = algorithmNames.contains(IpSecAlgorithm.CRYPT_AES_CBC); final boolean hasAuth = algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA256) || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA384) || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA512); return hasCrypt && hasAuth; } /** Retrieves the server address string. */ Loading Loading @@ -559,7 +617,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @Nullable private X509Certificate mUserCert; @Nullable private ProxyInfo mProxyInfo; @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>(); @NonNull private List<String> mAllowedAlgorithms = DEFAULT_ALGORITHMS; private boolean mIsBypassable = false; private boolean mIsMetered = true; private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; Loading Loading @@ -756,7 +814,19 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** * Sets the allowable set of IPsec algorithms * * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm} * <p>If set, this will constrain the set of algorithms that the IPsec tunnel will use for * integrity verification and encryption to the provided list. * * <p>The set of allowed IPsec algorithms is defined in {@link IpSecAlgorithm}. Adding of * algorithms that are considered insecure (such as AUTH_HMAC_MD5 and AUTH_HMAC_SHA1) is not * permitted, and will result in an IllegalArgumentException being thrown. * * <p>The provided algorithm list must contain at least one algorithm that provides * Authentication, and one that provides Encryption. Authenticated Encryption with * Associated Data (AEAD) algorithms provide both Authentication and Encryption. * * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm}, * with the exception of those considered insecure (as described above). * * @param algorithmNames the list of supported IPsec algorithms * @return this {@link Builder} object to facilitate chaining of method calls Loading @@ -765,7 +835,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) { checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames"); VpnProfile.validateAllowedAlgorithms(algorithmNames); validateAllowedAlgorithms(algorithmNames); mAllowedAlgorithms = algorithmNames; return this; Loading services/core/java/com/android/server/connectivity/Vpn.java +3 −1 Original line number Diff line number Diff line Loading @@ -1955,6 +1955,7 @@ public class Vpn { profile.ipsecCaCert = caCert; // Start VPN profile profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS); startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore); return; case VpnProfile.TYPE_IKEV2_IPSEC_PSK: Loading @@ -1963,6 +1964,7 @@ public class Vpn { Ikev2VpnProfile.encodeForIpsecSecret(profile.ipsecSecret.getBytes()); // Start VPN profile profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS); startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore); return; case VpnProfile.TYPE_L2TP_IPSEC_PSK: Loading Loading @@ -2359,7 +2361,7 @@ public class Vpn { final IkeSessionParams ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, network); final ChildSessionParams childSessionParams = VpnIkev2Utils.buildChildSessionParams(); VpnIkev2Utils.buildChildSessionParams(mProfile.getAllowedAlgorithms()); // TODO: Remove the need for adding two unused addresses with // IPsec tunnels. Loading services/core/java/com/android/server/connectivity/VpnIkev2Utils.java +52 −30 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16; import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256; Loading @@ -39,6 +38,7 @@ import android.content.Context; import android.net.Ikev2VpnProfile; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecAlgorithm; import android.net.IpSecTransform; import android.net.Network; import android.net.RouteInfo; Loading Loading @@ -83,6 +83,8 @@ import java.util.List; * @hide */ public class VpnIkev2Utils { private static final String TAG = VpnIkev2Utils.class.getSimpleName(); static IkeSessionParams buildIkeSessionParams( @NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) { final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity()); Loading @@ -103,11 +105,11 @@ public class VpnIkev2Utils { return ikeOptionsBuilder.build(); } static ChildSessionParams buildChildSessionParams() { static ChildSessionParams buildChildSessionParams(List<String> allowedAlgorithms) { final TunnelModeChildSessionParams.Builder childOptionsBuilder = new TunnelModeChildSessionParams.Builder(); for (final ChildSaProposal childProposal : getChildSaProposals()) { for (final ChildSaProposal childProposal : getChildSaProposals(allowedAlgorithms)) { childOptionsBuilder.addSaProposal(childProposal); } Loading Loading @@ -144,7 +146,7 @@ public class VpnIkev2Utils { } private static List<IkeSaProposal> getIkeSaProposals() { // TODO: filter this based on allowedAlgorithms // TODO: Add ability to filter this when IKEv2 API is made Public API final List<IkeSaProposal> proposals = new ArrayList<>(); // Encryption Algorithms: Currently only AES_CBC is supported. Loading @@ -160,7 +162,6 @@ public class VpnIkev2Utils { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); // Add AEAD options final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder(); Loading @@ -187,26 +188,46 @@ public class VpnIkev2Utils { return proposals; } private static List<ChildSaProposal> getChildSaProposals() { // TODO: filter this based on allowedAlgorithms /** Builds a child SA proposal based on the allowed IPsec algorithms */ private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) { final List<ChildSaProposal> proposals = new ArrayList<>(); // Add non-AEAD options if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) { final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder(); // Encryption Algorithms: Currently only AES_CBC is supported. // Encryption Algorithms: // AES-CBC is currently the only supported encryption algorithm. normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); // Authentication/Integrity Algorithms // Authentication/Integrity Algorithms: // Guaranteed by Ikev2VpnProfile constructor to contain at least one of these. if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA512)) { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); } if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)) { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); } if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); } ChildSaProposal proposal = normalModeBuilder.build(); if (proposal.getIntegrityAlgorithms().isEmpty()) { // Should be impossible; Verified in Ikev2VpnProfile. Log.wtf(TAG, "Missing integrity algorithm when buildling Child SA proposal"); } else { proposals.add(normalModeBuilder.build()); } } // Add AEAD options if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) { final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder(); // AES-GCM is currently the only supported AEAD algorithm 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); Loading @@ -217,8 +238,9 @@ public class VpnIkev2Utils { aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); proposals.add(normalModeBuilder.build()); proposals.add(aeadBuilder.build()); } return proposals; } Loading tests/net/java/android/net/Ikev2VpnProfileTest.java +76 −0 Original line number Diff line number Diff line Loading @@ -40,7 +40,10 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import javax.security.auth.x500.X500Principal; Loading Loading @@ -106,6 +109,7 @@ public class Ikev2VpnProfileTest { assertTrue(profile.isBypassable()); assertTrue(profile.isMetered()); assertEquals(TEST_MTU, profile.getMaxMtu()); assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms()); } @Test Loading Loading @@ -159,6 +163,78 @@ public class Ikev2VpnProfileTest { assertNull(profile.getUserCert()); } @Test public void testBuildWithAllowedAlgorithmsAead() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); builder.setAuthPsk(PSK_BYTES); List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); builder.setAllowedAlgorithms(allowedAlgorithms); final Ikev2VpnProfile profile = builder.build(); assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); } @Test public void testBuildWithAllowedAlgorithmsNormal() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); builder.setAuthPsk(PSK_BYTES); List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC); builder.setAllowedAlgorithms(allowedAlgorithms); final Ikev2VpnProfile profile = builder.build(); assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); } @Test public void testSetAllowedAlgorithmsEmptyList() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); try { builder.setAllowedAlgorithms(new ArrayList<>()); fail("Expected exception due to no valid algorithm set"); } catch (IllegalArgumentException expected) { } } @Test public void testSetAllowedAlgorithmsInvalidList() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); List<String> allowedAlgorithms = new ArrayList<>(); try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256)); fail("Expected exception due to missing encryption"); } catch (IllegalArgumentException expected) { } try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC)); fail("Expected exception due to missing authentication"); } catch (IllegalArgumentException expected) { } } @Test public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); List<String> allowedAlgorithms = new ArrayList<>(); try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5)); fail("Expected exception due to insecure algorithm"); } catch (IllegalArgumentException expected) { } try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1)); fail("Expected exception due to insecure algorithm"); } catch (IllegalArgumentException expected) { } } @Test public void testBuildNoAuthMethodSet() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); Loading Loading
core/java/android/net/Ikev2VpnProfile.java +74 −4 Original line number Diff line number Diff line Loading @@ -70,6 +70,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s"; private static final String EMPTY_CERT = ""; /** @hide */ public static final List<String> DEFAULT_ALGORITHMS = Collections.unmodifiableList(Arrays.asList( IpSecAlgorithm.CRYPT_AES_CBC, IpSecAlgorithm.AUTH_HMAC_SHA256, IpSecAlgorithm.AUTH_HMAC_SHA384, IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)); @NonNull private final String mServerAddr; @NonNull private final String mUserIdentity; Loading Loading @@ -172,7 +181,56 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { throw new IllegalArgumentException("Invalid auth method set"); } VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms); validateAllowedAlgorithms(mAllowedAlgorithms); } /** * Validates that the allowed algorithms are a valid set for IPsec purposes * * <p>In order for the algorithm list to be a valid set, it must contain at least one algorithm * that provides Authentication, and one that provides Encryption. Authenticated Encryption with * Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption. * * @param allowedAlgorithms The list to be validated */ private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) { VpnProfile.validateAllowedAlgorithms(algorithmNames); // First, make sure no insecure algorithms were proposed. if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5) || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) { throw new IllegalArgumentException("Algorithm not supported for IKEv2 VPN profiles"); } // Validate that some valid combination (AEAD or AUTH + CRYPT) is present if (hasAeadAlgorithms(algorithmNames) || hasNormalModeAlgorithms(algorithmNames)) { return; } throw new IllegalArgumentException("Algorithm set missing support for Auth, Crypt or both"); } /** * Checks if the provided list has AEAD algorithms * * @hide */ public static boolean hasAeadAlgorithms(@NonNull List<String> algorithmNames) { return algorithmNames.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); } /** * Checks the provided list has acceptable (non-AEAD) authentication and encryption algorithms * * @hide */ public static boolean hasNormalModeAlgorithms(@NonNull List<String> algorithmNames) { final boolean hasCrypt = algorithmNames.contains(IpSecAlgorithm.CRYPT_AES_CBC); final boolean hasAuth = algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA256) || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA384) || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA512); return hasCrypt && hasAuth; } /** Retrieves the server address string. */ Loading Loading @@ -559,7 +617,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @Nullable private X509Certificate mUserCert; @Nullable private ProxyInfo mProxyInfo; @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>(); @NonNull private List<String> mAllowedAlgorithms = DEFAULT_ALGORITHMS; private boolean mIsBypassable = false; private boolean mIsMetered = true; private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; Loading Loading @@ -756,7 +814,19 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** * Sets the allowable set of IPsec algorithms * * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm} * <p>If set, this will constrain the set of algorithms that the IPsec tunnel will use for * integrity verification and encryption to the provided list. * * <p>The set of allowed IPsec algorithms is defined in {@link IpSecAlgorithm}. Adding of * algorithms that are considered insecure (such as AUTH_HMAC_MD5 and AUTH_HMAC_SHA1) is not * permitted, and will result in an IllegalArgumentException being thrown. * * <p>The provided algorithm list must contain at least one algorithm that provides * Authentication, and one that provides Encryption. Authenticated Encryption with * Associated Data (AEAD) algorithms provide both Authentication and Encryption. * * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm}, * with the exception of those considered insecure (as described above). * * @param algorithmNames the list of supported IPsec algorithms * @return this {@link Builder} object to facilitate chaining of method calls Loading @@ -765,7 +835,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) { checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames"); VpnProfile.validateAllowedAlgorithms(algorithmNames); validateAllowedAlgorithms(algorithmNames); mAllowedAlgorithms = algorithmNames; return this; Loading
services/core/java/com/android/server/connectivity/Vpn.java +3 −1 Original line number Diff line number Diff line Loading @@ -1955,6 +1955,7 @@ public class Vpn { profile.ipsecCaCert = caCert; // Start VPN profile profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS); startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore); return; case VpnProfile.TYPE_IKEV2_IPSEC_PSK: Loading @@ -1963,6 +1964,7 @@ public class Vpn { Ikev2VpnProfile.encodeForIpsecSecret(profile.ipsecSecret.getBytes()); // Start VPN profile profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS); startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore); return; case VpnProfile.TYPE_L2TP_IPSEC_PSK: Loading Loading @@ -2359,7 +2361,7 @@ public class Vpn { final IkeSessionParams ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, network); final ChildSessionParams childSessionParams = VpnIkev2Utils.buildChildSessionParams(); VpnIkev2Utils.buildChildSessionParams(mProfile.getAllowedAlgorithms()); // TODO: Remove the need for adding two unused addresses with // IPsec tunnels. Loading
services/core/java/com/android/server/connectivity/VpnIkev2Utils.java +52 −30 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16; import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192; import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256; Loading @@ -39,6 +38,7 @@ import android.content.Context; import android.net.Ikev2VpnProfile; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecAlgorithm; import android.net.IpSecTransform; import android.net.Network; import android.net.RouteInfo; Loading Loading @@ -83,6 +83,8 @@ import java.util.List; * @hide */ public class VpnIkev2Utils { private static final String TAG = VpnIkev2Utils.class.getSimpleName(); static IkeSessionParams buildIkeSessionParams( @NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) { final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity()); Loading @@ -103,11 +105,11 @@ public class VpnIkev2Utils { return ikeOptionsBuilder.build(); } static ChildSessionParams buildChildSessionParams() { static ChildSessionParams buildChildSessionParams(List<String> allowedAlgorithms) { final TunnelModeChildSessionParams.Builder childOptionsBuilder = new TunnelModeChildSessionParams.Builder(); for (final ChildSaProposal childProposal : getChildSaProposals()) { for (final ChildSaProposal childProposal : getChildSaProposals(allowedAlgorithms)) { childOptionsBuilder.addSaProposal(childProposal); } Loading Loading @@ -144,7 +146,7 @@ public class VpnIkev2Utils { } private static List<IkeSaProposal> getIkeSaProposals() { // TODO: filter this based on allowedAlgorithms // TODO: Add ability to filter this when IKEv2 API is made Public API final List<IkeSaProposal> proposals = new ArrayList<>(); // Encryption Algorithms: Currently only AES_CBC is supported. Loading @@ -160,7 +162,6 @@ public class VpnIkev2Utils { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); // Add AEAD options final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder(); Loading @@ -187,26 +188,46 @@ public class VpnIkev2Utils { return proposals; } private static List<ChildSaProposal> getChildSaProposals() { // TODO: filter this based on allowedAlgorithms /** Builds a child SA proposal based on the allowed IPsec algorithms */ private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) { final List<ChildSaProposal> proposals = new ArrayList<>(); // Add non-AEAD options if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) { final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder(); // Encryption Algorithms: Currently only AES_CBC is supported. // Encryption Algorithms: // AES-CBC is currently the only supported encryption algorithm. normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); // Authentication/Integrity Algorithms // Authentication/Integrity Algorithms: // Guaranteed by Ikev2VpnProfile constructor to contain at least one of these. if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA512)) { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); } if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)) { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); } if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) { normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); } ChildSaProposal proposal = normalModeBuilder.build(); if (proposal.getIntegrityAlgorithms().isEmpty()) { // Should be impossible; Verified in Ikev2VpnProfile. Log.wtf(TAG, "Missing integrity algorithm when buildling Child SA proposal"); } else { proposals.add(normalModeBuilder.build()); } } // Add AEAD options if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) { final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder(); // AES-GCM is currently the only supported AEAD algorithm 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); Loading @@ -217,8 +238,9 @@ public class VpnIkev2Utils { aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); proposals.add(normalModeBuilder.build()); proposals.add(aeadBuilder.build()); } return proposals; } Loading
tests/net/java/android/net/Ikev2VpnProfileTest.java +76 −0 Original line number Diff line number Diff line Loading @@ -40,7 +40,10 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import javax.security.auth.x500.X500Principal; Loading Loading @@ -106,6 +109,7 @@ public class Ikev2VpnProfileTest { assertTrue(profile.isBypassable()); assertTrue(profile.isMetered()); assertEquals(TEST_MTU, profile.getMaxMtu()); assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms()); } @Test Loading Loading @@ -159,6 +163,78 @@ public class Ikev2VpnProfileTest { assertNull(profile.getUserCert()); } @Test public void testBuildWithAllowedAlgorithmsAead() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); builder.setAuthPsk(PSK_BYTES); List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); builder.setAllowedAlgorithms(allowedAlgorithms); final Ikev2VpnProfile profile = builder.build(); assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); } @Test public void testBuildWithAllowedAlgorithmsNormal() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); builder.setAuthPsk(PSK_BYTES); List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC); builder.setAllowedAlgorithms(allowedAlgorithms); final Ikev2VpnProfile profile = builder.build(); assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); } @Test public void testSetAllowedAlgorithmsEmptyList() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); try { builder.setAllowedAlgorithms(new ArrayList<>()); fail("Expected exception due to no valid algorithm set"); } catch (IllegalArgumentException expected) { } } @Test public void testSetAllowedAlgorithmsInvalidList() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); List<String> allowedAlgorithms = new ArrayList<>(); try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256)); fail("Expected exception due to missing encryption"); } catch (IllegalArgumentException expected) { } try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC)); fail("Expected exception due to missing authentication"); } catch (IllegalArgumentException expected) { } } @Test public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); List<String> allowedAlgorithms = new ArrayList<>(); try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5)); fail("Expected exception due to insecure algorithm"); } catch (IllegalArgumentException expected) { } try { builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1)); fail("Expected exception due to insecure algorithm"); } catch (IllegalArgumentException expected) { } } @Test public void testBuildNoAuthMethodSet() throws Exception { final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); Loading