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 android.net;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.os.Build;
27 import android.test.mock.MockContext;
28 
29 import androidx.test.filters.SmallTest;
30 
31 import com.android.internal.net.VpnProfile;
32 import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
33 import com.android.net.module.util.ProxyUtils;
34 import com.android.testutils.DevSdkIgnoreRule;
35 import com.android.testutils.DevSdkIgnoreRunner;
36 
37 import org.junit.Before;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.math.BigInteger;
42 import java.security.KeyPair;
43 import java.security.KeyPairGenerator;
44 import java.security.PrivateKey;
45 import java.security.cert.X509Certificate;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Date;
49 import java.util.List;
50 import java.util.concurrent.TimeUnit;
51 
52 import javax.security.auth.x500.X500Principal;
53 
54 /** Unit tests for {@link Ikev2VpnProfile.Builder}. */
55 @SmallTest
56 @RunWith(DevSdkIgnoreRunner.class)
57 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
58 public class Ikev2VpnProfileTest {
59     private static final String SERVER_ADDR_STRING = "1.2.3.4";
60     private static final String IDENTITY_STRING = "Identity";
61     private static final String USERNAME_STRING = "username";
62     private static final String PASSWORD_STRING = "pa55w0rd";
63     private static final String EXCL_LIST = "exclList";
64     private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
65     private static final int TEST_MTU = 1300;
66 
67     private final MockContext mMockContext =
68             new MockContext() {
69                 @Override
70                 public String getOpPackageName() {
71                     return "fooPackage";
72                 }
73             };
74     private final ProxyInfo mProxy = ProxyInfo.buildDirectProxy(
75             SERVER_ADDR_STRING, -1, ProxyUtils.exclusionStringAsList(EXCL_LIST));
76 
77     private X509Certificate mUserCert;
78     private X509Certificate mServerRootCa;
79     private PrivateKey mPrivateKey;
80 
81     @Before
setUp()82     public void setUp() throws Exception {
83         mServerRootCa = generateRandomCertAndKeyPair().cert;
84 
85         final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
86         mUserCert = userCertKey.cert;
87         mPrivateKey = userCertKey.key;
88     }
89 
getBuilderWithDefaultOptions()90     private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
91         final Ikev2VpnProfile.Builder builder =
92                 new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
93 
94         builder.setBypassable(true);
95         builder.setProxy(mProxy);
96         builder.setMaxMtu(TEST_MTU);
97         builder.setMetered(true);
98 
99         return builder;
100     }
101 
102     @Test
testBuildValidProfileWithOptions()103     public void testBuildValidProfileWithOptions() throws Exception {
104         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
105 
106         builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
107         final Ikev2VpnProfile profile = builder.build();
108         assertNotNull(profile);
109 
110         // Check non-auth parameters correctly stored
111         assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
112         assertEquals(IDENTITY_STRING, profile.getUserIdentity());
113         assertEquals(mProxy, profile.getProxyInfo());
114         assertTrue(profile.isBypassable());
115         assertTrue(profile.isMetered());
116         assertEquals(TEST_MTU, profile.getMaxMtu());
117         assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms());
118     }
119 
120     @Test
testBuildUsernamePasswordProfile()121     public void testBuildUsernamePasswordProfile() throws Exception {
122         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
123 
124         builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
125         final Ikev2VpnProfile profile = builder.build();
126         assertNotNull(profile);
127 
128         assertEquals(USERNAME_STRING, profile.getUsername());
129         assertEquals(PASSWORD_STRING, profile.getPassword());
130         assertEquals(mServerRootCa, profile.getServerRootCaCert());
131 
132         assertNull(profile.getPresharedKey());
133         assertNull(profile.getRsaPrivateKey());
134         assertNull(profile.getUserCert());
135     }
136 
137     @Test
testBuildDigitalSignatureProfile()138     public void testBuildDigitalSignatureProfile() throws Exception {
139         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
140 
141         builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
142         final Ikev2VpnProfile profile = builder.build();
143         assertNotNull(profile);
144 
145         assertEquals(profile.getUserCert(), mUserCert);
146         assertEquals(mPrivateKey, profile.getRsaPrivateKey());
147         assertEquals(profile.getServerRootCaCert(), mServerRootCa);
148 
149         assertNull(profile.getPresharedKey());
150         assertNull(profile.getUsername());
151         assertNull(profile.getPassword());
152     }
153 
154     @Test
testBuildPresharedKeyProfile()155     public void testBuildPresharedKeyProfile() throws Exception {
156         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
157 
158         builder.setAuthPsk(PSK_BYTES);
159         final Ikev2VpnProfile profile = builder.build();
160         assertNotNull(profile);
161 
162         assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
163 
164         assertNull(profile.getServerRootCaCert());
165         assertNull(profile.getUsername());
166         assertNull(profile.getPassword());
167         assertNull(profile.getRsaPrivateKey());
168         assertNull(profile.getUserCert());
169     }
170 
171     @Test
testBuildWithAllowedAlgorithmsAead()172     public void testBuildWithAllowedAlgorithmsAead() throws Exception {
173         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
174         builder.setAuthPsk(PSK_BYTES);
175 
176         List<String> allowedAlgorithms =
177                 Arrays.asList(
178                         IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
179                         IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305);
180         builder.setAllowedAlgorithms(allowedAlgorithms);
181 
182         final Ikev2VpnProfile profile = builder.build();
183         assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
184     }
185 
186     @Test
testBuildWithAllowedAlgorithmsNormal()187     public void testBuildWithAllowedAlgorithmsNormal() throws Exception {
188         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
189         builder.setAuthPsk(PSK_BYTES);
190 
191         List<String> allowedAlgorithms =
192                 Arrays.asList(
193                         IpSecAlgorithm.AUTH_HMAC_SHA512,
194                         IpSecAlgorithm.AUTH_AES_XCBC,
195                         IpSecAlgorithm.AUTH_AES_CMAC,
196                         IpSecAlgorithm.CRYPT_AES_CBC,
197                         IpSecAlgorithm.CRYPT_AES_CTR);
198         builder.setAllowedAlgorithms(allowedAlgorithms);
199 
200         final Ikev2VpnProfile profile = builder.build();
201         assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
202     }
203 
204     @Test
testSetAllowedAlgorithmsEmptyList()205     public void testSetAllowedAlgorithmsEmptyList() throws Exception {
206         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
207 
208         try {
209             builder.setAllowedAlgorithms(new ArrayList<>());
210             fail("Expected exception due to no valid algorithm set");
211         } catch (IllegalArgumentException expected) {
212         }
213     }
214 
215     @Test
testSetAllowedAlgorithmsInvalidList()216     public void testSetAllowedAlgorithmsInvalidList() throws Exception {
217         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
218         List<String> allowedAlgorithms = new ArrayList<>();
219 
220         try {
221             builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256));
222             fail("Expected exception due to missing encryption");
223         } catch (IllegalArgumentException expected) {
224         }
225 
226         try {
227             builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC));
228             fail("Expected exception due to missing authentication");
229         } catch (IllegalArgumentException expected) {
230         }
231     }
232 
233     @Test
testSetAllowedAlgorithmsInsecureAlgorithm()234     public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception {
235         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
236         List<String> allowedAlgorithms = new ArrayList<>();
237 
238         try {
239             builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5));
240             fail("Expected exception due to insecure algorithm");
241         } catch (IllegalArgumentException expected) {
242         }
243 
244         try {
245             builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1));
246             fail("Expected exception due to insecure algorithm");
247         } catch (IllegalArgumentException expected) {
248         }
249     }
250 
251     @Test
testBuildNoAuthMethodSet()252     public void testBuildNoAuthMethodSet() throws Exception {
253         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
254 
255         try {
256             builder.build();
257             fail("Expected exception due to lack of auth method");
258         } catch (IllegalArgumentException expected) {
259         }
260     }
261 
262     @Test
testBuildInvalidMtu()263     public void testBuildInvalidMtu() throws Exception {
264         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
265 
266         try {
267             builder.setMaxMtu(500);
268             fail("Expected exception due to too-small MTU");
269         } catch (IllegalArgumentException expected) {
270         }
271     }
272 
verifyVpnProfileCommon(VpnProfile profile)273     private void verifyVpnProfileCommon(VpnProfile profile) {
274         assertEquals(SERVER_ADDR_STRING, profile.server);
275         assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
276         assertEquals(mProxy, profile.proxy);
277         assertTrue(profile.isBypassable);
278         assertTrue(profile.isMetered);
279         assertEquals(TEST_MTU, profile.maxMtu);
280     }
281 
282     @Test
testPskConvertToVpnProfile()283     public void testPskConvertToVpnProfile() throws Exception {
284         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
285 
286         builder.setAuthPsk(PSK_BYTES);
287         final VpnProfile profile = builder.build().toVpnProfile();
288 
289         verifyVpnProfileCommon(profile);
290         assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
291 
292         // Check nothing else is set
293         assertEquals("", profile.username);
294         assertEquals("", profile.password);
295         assertEquals("", profile.ipsecUserCert);
296         assertEquals("", profile.ipsecCaCert);
297     }
298 
299     @Test
testUsernamePasswordConvertToVpnProfile()300     public void testUsernamePasswordConvertToVpnProfile() throws Exception {
301         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
302 
303         builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
304         final VpnProfile profile = builder.build().toVpnProfile();
305 
306         verifyVpnProfileCommon(profile);
307         assertEquals(USERNAME_STRING, profile.username);
308         assertEquals(PASSWORD_STRING, profile.password);
309         assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
310 
311         // Check nothing else is set
312         assertEquals("", profile.ipsecUserCert);
313         assertEquals("", profile.ipsecSecret);
314     }
315 
316     @Test
testRsaConvertToVpnProfile()317     public void testRsaConvertToVpnProfile() throws Exception {
318         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
319 
320         builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
321         final VpnProfile profile = builder.build().toVpnProfile();
322 
323         final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
324                 + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
325         verifyVpnProfileCommon(profile);
326         assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
327         assertEquals(
328                 expectedSecret,
329                 profile.ipsecSecret);
330         assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
331 
332         // Check nothing else is set
333         assertEquals("", profile.username);
334         assertEquals("", profile.password);
335     }
336 
337     @Test
testPskFromVpnProfileDiscardsIrrelevantValues()338     public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
339         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
340 
341         builder.setAuthPsk(PSK_BYTES);
342         final VpnProfile profile = builder.build().toVpnProfile();
343         profile.username = USERNAME_STRING;
344         profile.password = PASSWORD_STRING;
345         profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
346         profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
347 
348         final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
349         assertNull(result.getUsername());
350         assertNull(result.getPassword());
351         assertNull(result.getUserCert());
352         assertNull(result.getRsaPrivateKey());
353         assertNull(result.getServerRootCaCert());
354     }
355 
356     @Test
testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues()357     public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
358         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
359 
360         builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
361         final VpnProfile profile = builder.build().toVpnProfile();
362         profile.ipsecSecret = new String(PSK_BYTES);
363         profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
364 
365         final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
366         assertNull(result.getPresharedKey());
367         assertNull(result.getUserCert());
368         assertNull(result.getRsaPrivateKey());
369     }
370 
371     @Test
testRsaFromVpnProfileDiscardsIrrelevantValues()372     public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
373         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
374 
375         builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
376         final VpnProfile profile = builder.build().toVpnProfile();
377         profile.username = USERNAME_STRING;
378         profile.password = PASSWORD_STRING;
379 
380         final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
381         assertNull(result.getUsername());
382         assertNull(result.getPassword());
383         assertNull(result.getPresharedKey());
384     }
385 
386     @Test
testPskConversionIsLossless()387     public void testPskConversionIsLossless() throws Exception {
388         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
389 
390         builder.setAuthPsk(PSK_BYTES);
391         final Ikev2VpnProfile ikeProfile = builder.build();
392 
393         assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
394     }
395 
396     @Test
testUsernamePasswordConversionIsLossless()397     public void testUsernamePasswordConversionIsLossless() throws Exception {
398         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
399 
400         builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
401         final Ikev2VpnProfile ikeProfile = builder.build();
402 
403         assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
404     }
405 
406     @Test
testRsaConversionIsLossless()407     public void testRsaConversionIsLossless() throws Exception {
408         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
409 
410         builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
411         final Ikev2VpnProfile ikeProfile = builder.build();
412 
413         assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
414     }
415 
416     private static class CertificateAndKey {
417         public final X509Certificate cert;
418         public final PrivateKey key;
419 
CertificateAndKey(X509Certificate cert, PrivateKey key)420         CertificateAndKey(X509Certificate cert, PrivateKey key) {
421             this.cert = cert;
422             this.key = key;
423         }
424     }
425 
generateRandomCertAndKeyPair()426     private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
427         final Date validityBeginDate =
428                 new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
429         final Date validityEndDate =
430                 new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
431 
432         // Generate a keypair
433         final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
434         keyPairGenerator.initialize(512);
435         final KeyPair keyPair = keyPairGenerator.generateKeyPair();
436 
437         final X500Principal dnName = new X500Principal("CN=test.android.com");
438         final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
439         certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
440         certGen.setSubjectDN(dnName);
441         certGen.setIssuerDN(dnName);
442         certGen.setNotBefore(validityBeginDate);
443         certGen.setNotAfter(validityEndDate);
444         certGen.setPublicKey(keyPair.getPublic());
445         certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
446 
447         final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
448         return new CertificateAndKey(cert, keyPair.getPrivate());
449     }
450 }
451