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 /** Compares the priority of the transforms. */ compareTransformPriority(Set<Integer> transformGroup, int item1, int item2)208 protected int compareTransformPriority(Set<Integer> transformGroup, int item1, int item2) { 209 return getIndexOf(transformGroup, item1) - getIndexOf(transformGroup, item2); 210 } 211 212 /** 213 * Compares the priority of the encryption/AEAD transforms. First value in pair is 214 * encryption/AEAD algorithm and second value in pair is key length of that algorithm. If 215 * Algorithms are same then compare the priotity of the key lengths else compare the priority of 216 * the algorithms. 217 */ compareEncryptionTransformPriority( Set<Integer> algos, Set<Integer> keyLens, Pair<Integer, Integer> item1, Pair<Integer, Integer> item2)218 protected int compareEncryptionTransformPriority( 219 Set<Integer> algos, 220 Set<Integer> keyLens, 221 Pair<Integer, Integer> item1, 222 Pair<Integer, Integer> item2) { 223 return item1.first.equals(item2.first) 224 ? getIndexOf(keyLens, item1.second) - getIndexOf(keyLens, item2.second) 225 : getIndexOf(algos, item1.first) - getIndexOf(algos, item2.first); 226 } 227 getDhGroups()228 protected int[] getDhGroups() { 229 if (isSaferProposalsPrioritized()) { 230 return mProposedDhGroups.stream() 231 .sorted( 232 (item1, item2) -> 233 compareTransformPriority(VALID_DH_GROUPS, item1, item2)) 234 .mapToInt(Integer::intValue) 235 .toArray(); 236 } 237 238 return mProposedDhGroups.stream().mapToInt(Integer::intValue).toArray(); 239 } 240 getSupportedDhGroups()241 protected int[] getSupportedDhGroups() { 242 return VALID_DH_GROUPS.stream().mapToInt(Integer::intValue).toArray(); 243 } 244 getIntegrityAlgos()245 protected int[] getIntegrityAlgos() { 246 if (isSaferProposalsPrioritized()) { 247 return mProposedIntegrityAlgos.stream() 248 .sorted( 249 (item1, item2) -> 250 compareTransformPriority(VALID_INTEGRITY_ALGOS, item1, item2)) 251 .mapToInt(Integer::intValue) 252 .toArray(); 253 } 254 255 return mProposedIntegrityAlgos.stream().mapToInt(Integer::intValue).toArray(); 256 } 257 getSupportedIntegrityAlgos()258 protected int[] getSupportedIntegrityAlgos() { 259 return VALID_INTEGRITY_ALGOS.stream().mapToInt(Integer::intValue).toArray(); 260 } 261 getEncryptionAlgos()262 protected Pair<Integer, Integer>[] getEncryptionAlgos() { 263 if (isSaferProposalsPrioritized()) { 264 return mProposedEncryptAlgos.stream() 265 .sorted( 266 (item1, item2) -> 267 compareEncryptionTransformPriority( 268 VALID_ENCRYPTION_ALGOS, 269 VALID_KEY_LENGTHS, 270 item1, 271 item2)) 272 .toArray(Pair[]::new); 273 } 274 275 return mProposedEncryptAlgos.toArray(new Pair[mProposedEncryptAlgos.size()]); 276 } 277 getSupportedEncryptionAlgos()278 protected Pair<Integer, Integer>[] getSupportedEncryptionAlgos() { 279 Pair<Integer, Integer>[] encrAlgos = 280 new Pair[VALID_ENCRYPTION_ALGOS.size() * VALID_KEY_LENGTHS.size()]; 281 int index = 0; 282 for (int algo : VALID_ENCRYPTION_ALGOS) { 283 for (int len : VALID_KEY_LENGTHS) { 284 encrAlgos[index++] = new Pair(algo, len); 285 } 286 } 287 288 return encrAlgos; 289 } 290 getAeadAlgos()291 protected Pair<Integer, Integer>[] getAeadAlgos() { 292 if (isSaferProposalsPrioritized()) { 293 return mProposedAeadAlgos.stream() 294 .sorted( 295 (item1, item2) -> 296 compareEncryptionTransformPriority( 297 VALID_AEAD_ALGOS, VALID_KEY_LENGTHS, item1, item2)) 298 .toArray(Pair[]::new); 299 } 300 301 return mProposedAeadAlgos.toArray(new Pair[mProposedAeadAlgos.size()]); 302 } 303 getSupportedAeadAlgos()304 protected Pair<Integer, Integer>[] getSupportedAeadAlgos() { 305 Pair<Integer, Integer>[] aeadAlgos = 306 new Pair[VALID_AEAD_ALGOS.size() * VALID_KEY_LENGTHS.size()]; 307 int index = 0; 308 for (int algo : VALID_AEAD_ALGOS) { 309 for (int len : VALID_KEY_LENGTHS) { 310 aeadAlgos[index++] = new Pair(algo, len); 311 } 312 } 313 314 return aeadAlgos; 315 } 316 validateConfig( int config, Set<Integer> validConfigValues, String configType)317 protected static boolean validateConfig( 318 int config, Set<Integer> validConfigValues, String configType) { 319 if (validConfigValues.contains(config)) { 320 return true; 321 } 322 323 Log.e(TAG, "Invalid config value for " + configType + ":" + config); 324 return false; 325 } 326 } 327