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