1 /* 2 * Copyright 2020 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.google.android.iwlan.epdg; 18 19 import android.net.ipsec.ike.SaProposal; 20 import android.util.Log; 21 import android.util.Pair; 22 23 import java.util.Collections; 24 import java.util.Iterator; 25 import java.util.LinkedHashSet; 26 import java.util.List; 27 import java.util.Set; 28 29 abstract class EpdgSaProposal { 30 private static final String TAG = EpdgSaProposal.class.getSimpleName(); 31 private static final Set<Integer> VALID_DH_GROUPS; 32 private static final Set<Integer> VALID_KEY_LENGTHS; 33 protected static final Set<Integer> VALID_PRF_ALGOS; 34 private static final Set<Integer> VALID_INTEGRITY_ALGOS; 35 private static final Set<Integer> VALID_ENCRYPTION_ALGOS; 36 private static final Set<Integer> VALID_AEAD_ALGOS; 37 38 private static final String CONFIG_TYPE_DH_GROUP = "dh group"; 39 private static final String CONFIG_TYPE_KEY_LEN = "algorithm key length"; 40 protected static final String CONFIG_TYPE_PRF_ALGO = "prf algorithm"; 41 private static final String CONFIG_TYPE_INTEGRITY_ALGO = "integrity algorithm"; 42 private static final String CONFIG_TYPE_ENCRYPT_ALGO = "encryption algorithm"; 43 private static final String CONFIG_TYPE_AEAD_ALGO = "AEAD algorithm"; 44 45 private boolean mSaferAlgosPrioritized = false; 46 47 /* 48 * Each transform below filled with high secured order and index of each value 49 * represents the priority. 50 * Safer transform has high priority and proposals will be orders based on 51 * the priority order. 52 * For example, DH Group 4096 has more priority compare to 3072, and 3072 53 * has more priority than 2048. 54 * With reference to 3GPP TS 33.210 and RFC 8221, high secured transforms 55 * are prioritized in IKE and CHILD SA proposals. 56 */ 57 static { 58 VALID_DH_GROUPS = 59 Collections.unmodifiableSet( 60 new LinkedHashSet<Integer>( 61 List.of( 62 SaProposal.DH_GROUP_4096_BIT_MODP, 63 SaProposal.DH_GROUP_3072_BIT_MODP, 64 SaProposal.DH_GROUP_2048_BIT_MODP, 65 SaProposal.DH_GROUP_1536_BIT_MODP, 66 SaProposal.DH_GROUP_1024_BIT_MODP))); 67 68 VALID_KEY_LENGTHS = 69 Collections.unmodifiableSet( 70 new LinkedHashSet<Integer>( 71 List.of( 72 SaProposal.KEY_LEN_AES_256, 73 SaProposal.KEY_LEN_AES_192, 74 SaProposal.KEY_LEN_AES_128))); 75 76 VALID_ENCRYPTION_ALGOS = 77 Collections.unmodifiableSet( 78 new LinkedHashSet<Integer>( 79 List.of( 80 SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, 81 SaProposal.ENCRYPTION_ALGORITHM_AES_CTR))); 82 83 VALID_INTEGRITY_ALGOS = 84 Collections.unmodifiableSet( 85 new LinkedHashSet<Integer>( 86 List.of( 87 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 88 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 89 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 90 SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, 91 SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96))); 92 93 VALID_AEAD_ALGOS = 94 Collections.unmodifiableSet( 95 new LinkedHashSet<Integer>( 96 List.of( 97 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, 98 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, 99 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8))); 100 101 VALID_PRF_ALGOS = 102 Collections.unmodifiableSet( 103 new LinkedHashSet<Integer>( 104 List.of( 105 SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512, 106 SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384, 107 SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256, 108 SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, 109 SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC))); 110 } 111 112 protected final LinkedHashSet<Integer> mProposedDhGroups = new LinkedHashSet<>(); 113 protected final LinkedHashSet<Integer> mProposedIntegrityAlgos = new LinkedHashSet<>(); 114 protected final LinkedHashSet<Pair<Integer, Integer>> mProposedEncryptAlgos = 115 new LinkedHashSet<>(); 116 protected final LinkedHashSet<Pair<Integer, Integer>> mProposedAeadAlgos = 117 new LinkedHashSet<>(); 118 119 /** 120 * Add proposed DH groups by the carrier. 121 * 122 * @param dhGroups proposed DH groups 123 */ addProposedDhGroups(int[] dhGroups)124 public void addProposedDhGroups(int[] dhGroups) { 125 for (int dhGroup : dhGroups) { 126 if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) { 127 mProposedDhGroups.add(dhGroup); 128 } 129 } 130 } 131 132 /** 133 * Add proposed integrity algorithms by the carrier. 134 * 135 * @param integrityAlgos proposed integrity algorithms 136 */ addProposedIntegrityAlgorithm(int[] integrityAlgos)137 public void addProposedIntegrityAlgorithm(int[] integrityAlgos) { 138 for (int integrityAlgo : integrityAlgos) { 139 if (validateConfig(integrityAlgo, VALID_INTEGRITY_ALGOS, CONFIG_TYPE_INTEGRITY_ALGO)) { 140 mProposedIntegrityAlgos.add(integrityAlgo); 141 } 142 } 143 } 144 145 /** 146 * Add proposed encryption algorithms and respective key lengths by the carrier. 147 * 148 * @param encryptionAlgo proposed encryption algorithm 149 * @param keyLens proposed key lengths for the encryption algorithm 150 */ addProposedEncryptionAlgorithm(int encryptionAlgo, int[] keyLens)151 public void addProposedEncryptionAlgorithm(int encryptionAlgo, int[] keyLens) { 152 if (validateConfig(encryptionAlgo, VALID_ENCRYPTION_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) { 153 for (int keyLen : keyLens) { 154 if (validateConfig(keyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) { 155 mProposedEncryptAlgos.add(new Pair<Integer, Integer>(encryptionAlgo, keyLen)); 156 } 157 } 158 } 159 } 160 161 /** 162 * Add proposed AEAD algorithms and respective key lengths by the carrier. 163 * 164 * @param aeadAlgo proposed AEAD algorithm 165 * @param keyLens proposed key lengths for the encryption algorithm 166 */ addProposedAeadAlgorithm(int aeadAlgo, int[] keyLens)167 public void addProposedAeadAlgorithm(int aeadAlgo, int[] keyLens) { 168 if (validateConfig(aeadAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_AEAD_ALGO)) { 169 for (int keyLen : keyLens) { 170 if (validateConfig(keyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) { 171 mProposedAeadAlgos.add(new Pair<Integer, Integer>(aeadAlgo, keyLen)); 172 } 173 } 174 } 175 } 176 177 /** Enable to reorder proposals with safer ciphers prioritized. */ enableReorderingSaferProposals()178 public void enableReorderingSaferProposals() { 179 mSaferAlgosPrioritized = true; 180 } 181 182 /** 183 * Disable to reorder proposals with safer ciphers prioritized.Follows default configured order. 184 */ disableReorderingSaferProposals()185 public void disableReorderingSaferProposals() { 186 mSaferAlgosPrioritized = false; 187 } 188 isSaferProposalsPrioritized()189 protected boolean isSaferProposalsPrioritized() { 190 return mSaferAlgosPrioritized; 191 } 192 getIndexOf(Set<Integer> set, int value)193 protected int getIndexOf(Set<Integer> set, int value) { 194 Iterator<Integer> itr = set.iterator(); 195 int index = 0; 196 197 while (itr.hasNext()) { 198 if (itr.next().equals(value)) { 199 return index; 200 } 201 index++; 202 } 203 204 return -1; 205 } 206 207 /** 208 * Compares the priority of the transforms. 209 */ compareTransformPriority(Set<Integer> transformGroup, int item1, int item2)210 protected int compareTransformPriority(Set<Integer> transformGroup, int item1, int item2) { 211 return getIndexOf(transformGroup, item1) - getIndexOf(transformGroup, item2); 212 } 213 214 /** 215 * Compares the priority of the encryption/AEAD transforms. 216 * First value in pair is encryption/AEAD algorithm and 217 * second value in pair is key length of that algorithm. 218 * If Algorithms are same then compare the priotity of the key lengths else compare 219 * the priority of the algorithms. 220 */ compareEncryptionTransformPriority( Set<Integer> algos, Set<Integer> keyLens, Pair<Integer, Integer> item1, Pair<Integer, Integer> item2)221 protected int compareEncryptionTransformPriority( 222 Set<Integer> algos, 223 Set<Integer> keyLens, 224 Pair<Integer, Integer> item1, 225 Pair<Integer, Integer> item2) { 226 return item1.first.equals(item2.first) 227 ? getIndexOf(keyLens, item1.second) - getIndexOf(keyLens, item2.second) 228 : getIndexOf(algos, item1.first) - getIndexOf(algos, item2.first); 229 } 230 getDhGroups()231 protected int[] getDhGroups() { 232 if (isSaferProposalsPrioritized()) { 233 return mProposedDhGroups.stream() 234 .sorted( 235 (item1, item2) -> 236 compareTransformPriority(VALID_DH_GROUPS, item1, item2)) 237 .mapToInt(Integer::intValue) 238 .toArray(); 239 } 240 241 return mProposedDhGroups.stream().mapToInt(Integer::intValue).toArray(); 242 } 243 getSupportedDhGroups()244 protected int[] getSupportedDhGroups() { 245 return VALID_DH_GROUPS.stream().mapToInt(Integer::intValue).toArray(); 246 } 247 getIntegrityAlgos()248 protected int[] getIntegrityAlgos() { 249 if (isSaferProposalsPrioritized()) { 250 return mProposedIntegrityAlgos.stream() 251 .sorted( 252 (item1, item2) -> 253 compareTransformPriority(VALID_INTEGRITY_ALGOS, item1, item2)) 254 .mapToInt(Integer::intValue) 255 .toArray(); 256 } 257 258 return mProposedIntegrityAlgos.stream().mapToInt(Integer::intValue).toArray(); 259 } 260 getSupportedIntegrityAlgos()261 protected int[] getSupportedIntegrityAlgos() { 262 return VALID_INTEGRITY_ALGOS.stream().mapToInt(Integer::intValue).toArray(); 263 } 264 getEncryptionAlgos()265 protected Pair<Integer, Integer>[] getEncryptionAlgos() { 266 if (isSaferProposalsPrioritized()) { 267 return mProposedEncryptAlgos.stream() 268 .sorted( 269 (item1, item2) -> 270 compareEncryptionTransformPriority( 271 VALID_ENCRYPTION_ALGOS, 272 VALID_KEY_LENGTHS, 273 item1, 274 item2)) 275 .toArray(Pair[]::new); 276 } 277 278 return mProposedEncryptAlgos.toArray(new Pair[mProposedEncryptAlgos.size()]); 279 } 280 getSupportedEncryptionAlgos()281 protected Pair<Integer, Integer>[] getSupportedEncryptionAlgos() { 282 Pair<Integer, Integer>[] encrAlgos = 283 new Pair[VALID_ENCRYPTION_ALGOS.size() * VALID_KEY_LENGTHS.size()]; 284 int index = 0; 285 for (int algo : VALID_ENCRYPTION_ALGOS) { 286 for (int len : VALID_KEY_LENGTHS) { 287 encrAlgos[index++] = new Pair(algo, len); 288 } 289 } 290 291 return encrAlgos; 292 } 293 getAeadAlgos()294 protected Pair<Integer, Integer>[] getAeadAlgos() { 295 if (isSaferProposalsPrioritized()) { 296 return mProposedAeadAlgos.stream() 297 .sorted( 298 (item1, item2) -> 299 compareEncryptionTransformPriority( 300 VALID_AEAD_ALGOS, VALID_KEY_LENGTHS, item1, item2)) 301 .toArray(Pair[]::new); 302 } 303 304 return mProposedAeadAlgos.toArray(new Pair[mProposedAeadAlgos.size()]); 305 } 306 getSupportedAeadAlgos()307 protected Pair<Integer, Integer>[] getSupportedAeadAlgos() { 308 Pair<Integer, Integer>[] aeadAlgos = 309 new Pair[VALID_AEAD_ALGOS.size() * VALID_KEY_LENGTHS.size()]; 310 int index = 0; 311 for (int algo : VALID_AEAD_ALGOS) { 312 for (int len : VALID_KEY_LENGTHS) { 313 aeadAlgos[index++] = new Pair(algo, len); 314 } 315 } 316 317 return aeadAlgos; 318 } 319 validateConfig( int config, Set<Integer> validConfigValues, String configType)320 protected static boolean validateConfig( 321 int config, Set<Integer> validConfigValues, String configType) { 322 if (validConfigValues.contains(config)) { 323 return true; 324 } 325 326 Log.e(TAG, "Invalid config value for " + configType + ":" + config); 327 return false; 328 } 329 } 330