• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.mock;
26 
27 import android.content.res.Resources;
28 import android.os.Build;
29 import android.os.Parcel;
30 import android.os.SystemProperties;
31 
32 import androidx.test.filters.SmallTest;
33 
34 import com.android.internal.util.CollectionUtils;
35 import com.android.testutils.DevSdkIgnoreRule;
36 import com.android.testutils.DevSdkIgnoreRunner;
37 
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.util.AbstractMap.SimpleEntry;
42 import java.util.Arrays;
43 import java.util.HashSet;
44 import java.util.Map.Entry;
45 import java.util.Random;
46 import java.util.Set;
47 
48 /** Unit tests for {@link IpSecAlgorithm}. */
49 @SmallTest
50 @RunWith(DevSdkIgnoreRunner.class)
51 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
52 public class IpSecAlgorithmTest {
53     private static final byte[] KEY_MATERIAL;
54 
55     private final Resources mMockResources = mock(Resources.class);
56 
57     static {
58         KEY_MATERIAL = new byte[128];
59         new Random().nextBytes(KEY_MATERIAL);
60     };
61 
generateKey(int keyLenInBits)62     private static byte[] generateKey(int keyLenInBits) {
63         return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8);
64     }
65 
66     @Test
testNoTruncLen()67     public void testNoTruncLen() throws Exception {
68         Entry<String, Integer>[] authAndAeadList =
69                 new Entry[] {
70                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128),
71                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160),
72                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
73                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
74                     new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
75                     new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224),
76                 };
77 
78         // Expect auth and aead algorithms to throw errors if trunclen is omitted.
79         for (Entry<String, Integer> algData : authAndAeadList) {
80             try {
81                 new IpSecAlgorithm(
82                         algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8));
83                 fail("Expected exception on unprovided auth trunclen");
84             } catch (IllegalArgumentException expected) {
85             }
86         }
87 
88         // Ensure crypt works with no truncation length supplied.
89         new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
90     }
91 
checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen)92     private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen)
93             throws Exception {
94         new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen);
95 
96         try {
97             new IpSecAlgorithm(algoName, generateKey(keyLen));
98             fail("Expected exception on unprovided auth trunclen");
99         } catch (IllegalArgumentException pass) {
100         }
101 
102         try {
103             new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen);
104             fail("Invalid key length not validated");
105         } catch (IllegalArgumentException pass) {
106         }
107 
108         try {
109             new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1);
110             fail("Invalid truncation length not validated");
111         } catch (IllegalArgumentException pass) {
112         }
113     }
114 
checkCryptKeyLenValidation(String algoName, int keyLen)115     private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception {
116         new IpSecAlgorithm(algoName, generateKey(keyLen));
117 
118         try {
119             new IpSecAlgorithm(algoName, generateKey(keyLen + 8));
120             fail("Invalid key length not validated");
121         } catch (IllegalArgumentException pass) {
122         }
123     }
124 
125     @Test
testValidationForAlgosAddedInS()126     public void testValidationForAlgosAddedInS() throws Exception {
127         if (SystemProperties.getInt("ro.vendor.api_level", 10000) <= Build.VERSION_CODES.R) return;
128 
129         for (int len : new int[] {160, 224, 288}) {
130             checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
131         }
132         checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96);
133         checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_CMAC, 128, 96);
134         checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128);
135     }
136 
137     @Test
testTruncLenValidation()138     public void testTruncLenValidation() throws Exception {
139         for (int truncLen : new int[] {256, 512}) {
140             new IpSecAlgorithm(
141                     IpSecAlgorithm.AUTH_HMAC_SHA512,
142                     Arrays.copyOf(KEY_MATERIAL, 512 / 8),
143                     truncLen);
144         }
145 
146         for (int truncLen : new int[] {255, 513}) {
147             try {
148                 new IpSecAlgorithm(
149                         IpSecAlgorithm.AUTH_HMAC_SHA512,
150                         Arrays.copyOf(KEY_MATERIAL, 512 / 8),
151                         truncLen);
152                 fail("Invalid truncation length not validated");
153             } catch (IllegalArgumentException pass) {
154             }
155         }
156     }
157 
158     @Test
testLenValidation()159     public void testLenValidation() throws Exception {
160         for (int len : new int[] {128, 192, 256}) {
161             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, len / 8));
162         }
163         try {
164             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 384 / 8));
165             fail("Invalid key length not validated");
166         } catch (IllegalArgumentException pass) {
167         }
168     }
169 
170     @Test
testAlgoNameValidation()171     public void testAlgoNameValidation() throws Exception {
172         try {
173             new IpSecAlgorithm("rot13", Arrays.copyOf(KEY_MATERIAL, 128 / 8));
174             fail("Invalid algorithm name not validated");
175         } catch (IllegalArgumentException pass) {
176         }
177     }
178 
179     @Test
testParcelUnparcel()180     public void testParcelUnparcel() throws Exception {
181         IpSecAlgorithm init =
182                 new IpSecAlgorithm(
183                         IpSecAlgorithm.AUTH_HMAC_SHA512, Arrays.copyOf(KEY_MATERIAL, 512 / 8), 256);
184 
185         Parcel p = Parcel.obtain();
186         p.setDataPosition(0);
187         init.writeToParcel(p, 0);
188 
189         p.setDataPosition(0);
190         IpSecAlgorithm fin = IpSecAlgorithm.CREATOR.createFromParcel(p);
191         assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin));
192         p.recycle();
193     }
194 
getMandatoryAlgos()195     private static Set<String> getMandatoryAlgos() {
196         int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level", 10000);
197         return CollectionUtils.filter(
198                 ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
199                 i -> vendorApiLevel >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
200     }
201 
getOptionalAlgos()202     private static Set<String> getOptionalAlgos() {
203         int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level", 10000);
204         return CollectionUtils.filter(
205                 ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
206                 i -> vendorApiLevel < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
207     }
208 
209     @Test
testGetSupportedAlgorithms()210     public void testGetSupportedAlgorithms() throws Exception {
211         assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos()));
212         assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll(
213                 IpSecAlgorithm.getSupportedAlgorithms()));
214     }
215 
216     @Test
testLoadAlgos()217     public void testLoadAlgos() throws Exception {
218         final Set<String> optionalAlgoSet = getOptionalAlgos();
219         final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]);
220 
221         // Query the identifier instead of using the R.array constant, as the test may be built
222         // separately from the platform and they may not match.
223         final int resId = Resources.getSystem().getIdentifier("config_optionalIpSecAlgorithms",
224                 "array", "android");
225         doReturn(optionalAlgos).when(mMockResources).getStringArray(resId);
226 
227         final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources));
228         final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet();
229 
230         assertEquals(expectedAlgos, enabledAlgos);
231     }
232 }
233