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