1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.net;
18 
19 import static com.android.testutils.ParcelUtils.assertParcelSane;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotEquals;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertTrue;
26 
27 import android.net.IpSecAlgorithm;
28 import android.os.Build;
29 
30 import androidx.test.filters.SmallTest;
31 
32 import com.android.testutils.DevSdkIgnoreRule;
33 import com.android.testutils.DevSdkIgnoreRunner;
34 
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
41 
42 /** Unit tests for {@link VpnProfile}. */
43 @SmallTest
44 @RunWith(DevSdkIgnoreRunner.class)
45 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
46 public class VpnProfileTest {
47     private static final String DUMMY_PROFILE_KEY = "Test";
48 
49     private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
50     private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
51 
52     @Test
53     public void testDefaults() throws Exception {
54         final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
55 
56         assertEquals(DUMMY_PROFILE_KEY, p.key);
57         assertEquals("", p.name);
58         assertEquals(VpnProfile.TYPE_PPTP, p.type);
59         assertEquals("", p.server);
60         assertEquals("", p.username);
61         assertEquals("", p.password);
62         assertEquals("", p.dnsServers);
63         assertEquals("", p.searchDomains);
64         assertEquals("", p.routes);
65         assertTrue(p.mppe);
66         assertEquals("", p.l2tpSecret);
67         assertEquals("", p.ipsecIdentifier);
68         assertEquals("", p.ipsecSecret);
69         assertEquals("", p.ipsecUserCert);
70         assertEquals("", p.ipsecCaCert);
71         assertEquals("", p.ipsecServerCert);
72         assertEquals(null, p.proxy);
73         assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
74         assertFalse(p.isBypassable);
75         assertFalse(p.isMetered);
76         assertEquals(1360, p.maxMtu);
77         assertFalse(p.areAuthParamsInline);
78         assertFalse(p.isRestrictedToTestNetworks);
79     }
80 
81     private VpnProfile getSampleIkev2Profile(String key) {
82         final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
83 
84         p.name = "foo";
85         p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
86         p.server = "bar";
87         p.username = "baz";
88         p.password = "qux";
89         p.dnsServers = "8.8.8.8";
90         p.searchDomains = "";
91         p.routes = "0.0.0.0/0";
92         p.mppe = false;
93         p.l2tpSecret = "";
94         p.ipsecIdentifier = "quux";
95         p.ipsecSecret = "quuz";
96         p.ipsecUserCert = "corge";
97         p.ipsecCaCert = "grault";
98         p.ipsecServerCert = "garply";
99         p.proxy = null;
100         p.setAllowedAlgorithms(
101                 Arrays.asList(
102                         IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
103                         IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
104                         IpSecAlgorithm.AUTH_HMAC_SHA512,
105                         IpSecAlgorithm.CRYPT_AES_CBC));
106         p.isBypassable = true;
107         p.isMetered = true;
108         p.maxMtu = 1350;
109         p.areAuthParamsInline = true;
110 
111         // Not saved, but also not compared.
112         p.saveLogin = true;
113 
114         return p;
115     }
116 
117     @Test
118     public void testEquals() {
119         assertEquals(
120                 getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
121 
122         final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
123         modified.maxMtu--;
124         assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
125     }
126 
127     @Test
128     public void testParcelUnparcel() {
129         assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
130     }
131 
132     @Test
133     public void testEncodeDecode() {
134         final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
135         final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
136         assertEquals(profile, decoded);
137     }
138 
139     @Test
140     public void testEncodeDecodeTooManyValues() {
141         final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
142         final byte[] tooManyValues =
143                 (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
144 
145         assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
146     }
147 
148     private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
149         // Sort to ensure when we remove, we can do it from greatest first.
150         Arrays.sort(missingIndices);
151 
152         final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
153         final List<String> parts =
154                 new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
155 
156         // Remove from back first to ensure indexing is consistent.
157         for (int i = missingIndices.length - 1; i >= 0; i--) {
158             parts.remove(missingIndices[i]);
159         }
160 
161         return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
162     }
163 
164     @Test
165     public void testEncodeDecodeInvalidNumberOfValues() {
166         final String tooFewValues =
167                 getEncodedDecodedIkev2ProfileMissingValues(
168                         ENCODED_INDEX_AUTH_PARAMS_INLINE,
169                         ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
170 
171         assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
172     }
173 
174     @Test
175     public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
176         final String tooFewValues =
177                 getEncodedDecodedIkev2ProfileMissingValues(
178                         ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
179 
180         // Verify decoding without isRestrictedToTestNetworks defaults to false
181         final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
182         assertFalse(decoded.isRestrictedToTestNetworks);
183     }
184 
185     @Test
186     public void testEncodeDecodeLoginsNotSaved() {
187         final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
188         profile.saveLogin = false;
189 
190         final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
191         assertNotEquals(profile, decoded);
192 
193         // Add the username/password back, everything else must be equal.
194         decoded.username = profile.username;
195         decoded.password = profile.password;
196         assertEquals(profile, decoded);
197     }
198 }
199