• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.ipsec.ike;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.os.PersistableBundle;
22 import android.util.Pair;
23 import android.util.SparseArray;
24 
25 import com.android.internal.net.ipsec.ike.message.IkePayload;
26 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
27 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
28 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform;
29 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform;
30 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform;
31 import com.android.modules.utils.build.SdkLevel;
32 import com.android.server.vcn.util.PersistableBundleUtils;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.HashSet;
39 import java.util.LinkedHashSet;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Objects;
43 import java.util.Set;
44 
45 /**
46  * SaProposal represents a proposed configuration to negotiate an IKE or Child SA.
47  *
48  * <p>SaProposal will contain cryptograhic algorithms and key generation materials for the
49  * negotiation of an IKE or Child SA.
50  *
51  * <p>User must provide at least one valid SaProposal when they are creating a new IKE or Child SA.
52  *
53  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
54  *     Protocol Version 2 (IKEv2)</a>
55  */
56 public abstract class SaProposal {
57     /** @hide */
58     @Retention(RetentionPolicy.SOURCE)
59     @IntDef({
60         ENCRYPTION_ALGORITHM_3DES,
61         ENCRYPTION_ALGORITHM_AES_CBC,
62         ENCRYPTION_ALGORITHM_AES_CTR,
63         ENCRYPTION_ALGORITHM_AES_GCM_8,
64         ENCRYPTION_ALGORITHM_AES_GCM_12,
65         ENCRYPTION_ALGORITHM_AES_GCM_16,
66         ENCRYPTION_ALGORITHM_CHACHA20_POLY1305
67     })
68     public @interface EncryptionAlgorithm {}
69 
70     /** 3DES Encryption/Ciphering Algorithm. */
71     public static final int ENCRYPTION_ALGORITHM_3DES = 3;
72     /** AES-CBC Encryption/Ciphering Algorithm. */
73     public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
74     /** AES-CTR Encryption/Ciphering Algorithm. */
75     public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13;
76     /**
77      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 8-octet ICV
78      * (truncation).
79      */
80     public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18;
81     /**
82      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 12-octet ICV
83      * (truncation).
84      */
85     public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19;
86     /**
87      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV
88      * (truncation).
89      */
90     public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20;
91     /**
92      * ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV
93      * (truncation).
94      */
95     public static final int ENCRYPTION_ALGORITHM_CHACHA20_POLY1305 = 28;
96 
97     /** @hide */
98     protected static final SparseArray<String> SUPPORTED_ENCRYPTION_ALGO_TO_STR;
99 
100     static {
101         SUPPORTED_ENCRYPTION_ALGO_TO_STR = new SparseArray<>();
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_3DES, "ENCR_3DES")102         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_3DES, "ENCR_3DES");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CBC, "ENCR_AES_CBC")103         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CBC, "ENCR_AES_CBC");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CTR, "ENCR_AES_CTR")104         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CTR, "ENCR_AES_CTR");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_8, "ENCR_AES_GCM_8")105         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_8, "ENCR_AES_GCM_8");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_12, "ENCR_AES_GCM_12")106         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_12, "ENCR_AES_GCM_12");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_16, "ENCR_AES_GCM_16")107         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_16, "ENCR_AES_GCM_16");
SUPPORTED_ENCRYPTION_ALGO_TO_STR.put( ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, "ENCR_CHACHA20_POLY1305")108         SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(
109                 ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, "ENCR_CHACHA20_POLY1305");
110     }
111 
112     /**
113      * Key length unused.
114      *
115      * <p>This value should only be used with the Encryption/Ciphering Algorithm that accepts a
116      * fixed key size such as {@link #ENCRYPTION_ALGORITHM_3DES}.
117      */
118     public static final int KEY_LEN_UNUSED = 0;
119     /** AES Encryption/Ciphering Algorithm key length 128 bits. */
120     public static final int KEY_LEN_AES_128 = 128;
121     /** AES Encryption/Ciphering Algorithm key length 192 bits. */
122     public static final int KEY_LEN_AES_192 = 192;
123     /** AES Encryption/Ciphering Algorithm key length 256 bits. */
124     public static final int KEY_LEN_AES_256 = 256;
125 
126     /** @hide */
127     @Retention(RetentionPolicy.SOURCE)
128     @IntDef({
129         PSEUDORANDOM_FUNCTION_HMAC_SHA1,
130         PSEUDORANDOM_FUNCTION_AES128_XCBC,
131         PSEUDORANDOM_FUNCTION_SHA2_256,
132         PSEUDORANDOM_FUNCTION_SHA2_384,
133         PSEUDORANDOM_FUNCTION_SHA2_512,
134         PSEUDORANDOM_FUNCTION_AES128_CMAC
135     })
136     public @interface PseudorandomFunction {}
137 
138     /** HMAC-SHA1 Pseudorandom Function. */
139     public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2;
140     /** AES128-XCBC Pseudorandom Function. */
141     public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4;
142     /** HMAC-SHA2-256 Pseudorandom Function. */
143     public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5;
144     /** HMAC-SHA2-384 Pseudorandom Function. */
145     public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6;
146     /** HMAC-SHA2-384 Pseudorandom Function. */
147     public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7;
148     /** AES128-CMAC Pseudorandom Function. */
149     public static final int PSEUDORANDOM_FUNCTION_AES128_CMAC = 8;
150 
151     /** @hide */
152     protected static final SparseArray<String> SUPPORTED_PRF_TO_STR;
153 
154     static {
155         SUPPORTED_PRF_TO_STR = new SparseArray<>();
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_HMAC_SHA1, "PRF_HMAC_SHA1")156         SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_HMAC_SHA1, "PRF_HMAC_SHA1");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_XCBC, "PRF_AES128_XCBC")157         SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_XCBC, "PRF_AES128_XCBC");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_256, "PRF_HMAC2_256")158         SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_256, "PRF_HMAC2_256");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_384, "PRF_HMAC2_384")159         SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_384, "PRF_HMAC2_384");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_512, "PRF_HMAC2_512")160         SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_512, "PRF_HMAC2_512");
SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_CMAC, "PRF_AES128_CMAC")161         SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_CMAC, "PRF_AES128_CMAC");
162     }
163 
164     /** @hide */
165     @Retention(RetentionPolicy.SOURCE)
166     @IntDef({
167         INTEGRITY_ALGORITHM_NONE,
168         INTEGRITY_ALGORITHM_HMAC_SHA1_96,
169         INTEGRITY_ALGORITHM_AES_XCBC_96,
170         INTEGRITY_ALGORITHM_AES_CMAC_96,
171         INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
172         INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
173         INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
174     })
175     public @interface IntegrityAlgorithm {}
176 
177     /** None Authentication/Integrity Algorithm. */
178     public static final int INTEGRITY_ALGORITHM_NONE = 0;
179     /** HMAC-SHA1 Authentication/Integrity Algorithm. */
180     public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2;
181     /** AES-XCBC-96 Authentication/Integrity Algorithm. */
182     public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5;
183     /** AES-CMAC-96 Authentication/Integrity Algorithm. */
184     public static final int INTEGRITY_ALGORITHM_AES_CMAC_96 = 8;
185     /** HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation. */
186     public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12;
187     /** HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation. */
188     public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13;
189     /** HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation. */
190     public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14;
191 
192     /** @hide */
193     protected static final SparseArray<String> SUPPORTED_INTEGRITY_ALGO_TO_STR;
194 
195     static {
196         SUPPORTED_INTEGRITY_ALGO_TO_STR = new SparseArray<>();
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_NONE, "AUTH_NONE")197         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_NONE, "AUTH_NONE");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96")198         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_XCBC_96, "AUTH_AES_XCBC_96")199         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_XCBC_96, "AUTH_AES_XCBC_96");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_CMAC_96, "AUTH_AES_CMAC_96")200         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_CMAC_96, "AUTH_AES_CMAC_96");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put( INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, "AUTH_HMAC_SHA2_256_128")201         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(
202                 INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, "AUTH_HMAC_SHA2_256_128");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put( INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, "AUTH_HMAC_SHA2_384_192")203         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(
204                 INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, "AUTH_HMAC_SHA2_384_192");
SUPPORTED_INTEGRITY_ALGO_TO_STR.put( INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, "AUTH_HMAC_SHA2_512_256")205         SUPPORTED_INTEGRITY_ALGO_TO_STR.put(
206                 INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, "AUTH_HMAC_SHA2_512_256");
207     }
208 
209     /** @hide */
210     @Retention(RetentionPolicy.SOURCE)
211     @IntDef({
212         DH_GROUP_NONE,
213         DH_GROUP_1024_BIT_MODP,
214         DH_GROUP_1536_BIT_MODP,
215         DH_GROUP_2048_BIT_MODP,
216         DH_GROUP_3072_BIT_MODP,
217         DH_GROUP_4096_BIT_MODP,
218         DH_GROUP_CURVE_25519
219     })
220     public @interface DhGroup {}
221 
222     /** None Diffie-Hellman Group. */
223     public static final int DH_GROUP_NONE = 0;
224     /** 1024-bit MODP Diffie-Hellman Group. */
225     public static final int DH_GROUP_1024_BIT_MODP = 2;
226     /** 1536-bit MODP Diffie-Hellman Group. */
227     public static final int DH_GROUP_1536_BIT_MODP = 5;
228     /** 2048-bit MODP Diffie-Hellman Group. */
229     public static final int DH_GROUP_2048_BIT_MODP = 14;
230     /** 3072-bit MODP Diffie-Hellman Group. */
231     public static final int DH_GROUP_3072_BIT_MODP = 15;
232     /** 4096-bit MODP Diffie-Hellman Group. */
233     public static final int DH_GROUP_4096_BIT_MODP = 16;
234     /** Elliptic Curve Diffie-Hellman 25519. */
235     public static final int DH_GROUP_CURVE_25519 = 31;
236 
237     private static final SparseArray<String> SUPPORTED_DH_GROUP_TO_STR;
238 
239     static {
240         SUPPORTED_DH_GROUP_TO_STR = new SparseArray<>();
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_NONE, "DH_NONE")241         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_NONE, "DH_NONE");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1024_BIT_MODP, "DH_1024_BIT_MODP")242         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1024_BIT_MODP, "DH_1024_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1536_BIT_MODP, "DH_1536_BIT_MODP")243         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1536_BIT_MODP, "DH_1536_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_2048_BIT_MODP, "DH_2048_BIT_MODP")244         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_2048_BIT_MODP, "DH_2048_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_3072_BIT_MODP, "DH_3072_BIT_MODP")245         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_3072_BIT_MODP, "DH_3072_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_4096_BIT_MODP, "DH_4096_BIT_MODP")246         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_4096_BIT_MODP, "DH_4096_BIT_MODP");
SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_CURVE_25519, "DH_GROUP_CURVE_25519")247         SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_CURVE_25519, "DH_GROUP_CURVE_25519");
248     }
249 
250     private static final String PROTOCOL_ID_KEY = "mProtocolId";
251     /** @hide */
252     protected static final String ENCRYPT_ALGO_KEY = "mEncryptionAlgorithms";
253     /** @hide */
254     protected static final String INTEGRITY_ALGO_KEY = "mIntegrityAlgorithms";
255     /** @hide */
256     protected static final String DH_GROUP_KEY = "mDhGroups";
257 
258     @IkePayload.ProtocolId private final int mProtocolId;
259     private final EncryptionTransform[] mEncryptionAlgorithms;
260     private final IntegrityTransform[] mIntegrityAlgorithms;
261     private final DhGroupTransform[] mDhGroups;
262 
263     /** @hide */
SaProposal( @kePayload.ProtocolId int protocol, EncryptionTransform[] encryptionAlgos, IntegrityTransform[] integrityAlgos, DhGroupTransform[] dhGroups)264     protected SaProposal(
265             @IkePayload.ProtocolId int protocol,
266             EncryptionTransform[] encryptionAlgos,
267             IntegrityTransform[] integrityAlgos,
268             DhGroupTransform[] dhGroups) {
269         mProtocolId = protocol;
270         mEncryptionAlgorithms = encryptionAlgos;
271         mIntegrityAlgorithms = integrityAlgos;
272         mDhGroups = dhGroups;
273     }
274 
275     /**
276      * Constructs this object by deserializing a PersistableBundle
277      *
278      * @hide
279      */
280     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)281     public static SaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
282         Objects.requireNonNull(in, "PersistableBundle is null");
283 
284         int protocolId = in.getInt(PROTOCOL_ID_KEY);
285         switch (protocolId) {
286             case IkePayload.PROTOCOL_ID_IKE:
287                 return IkeSaProposal.fromPersistableBundle(in);
288             case IkePayload.PROTOCOL_ID_ESP:
289                 return ChildSaProposal.fromPersistableBundle(in);
290             default:
291                 throw new IllegalArgumentException("Invalid protocol ID " + protocolId);
292         }
293     }
294 
295     /**
296      * Serializes this object to a PersistableBundle
297      *
298      * @hide
299      */
300     @NonNull
toPersistableBundle()301     public PersistableBundle toPersistableBundle() {
302         final PersistableBundle result = new PersistableBundle();
303 
304         result.putInt(PROTOCOL_ID_KEY, mProtocolId);
305 
306         PersistableBundle encryptionBundle =
307                 PersistableBundleUtils.fromList(
308                         Arrays.asList(mEncryptionAlgorithms),
309                         EncryptionTransform::toPersistableBundle);
310         result.putPersistableBundle(ENCRYPT_ALGO_KEY, encryptionBundle);
311 
312         int[] integrityAlgoIdArray = getIntegrityAlgorithms().stream().mapToInt(i -> i).toArray();
313         result.putIntArray(INTEGRITY_ALGO_KEY, integrityAlgoIdArray);
314 
315         int[] dhGroupArray = getDhGroups().stream().mapToInt(i -> i).toArray();
316         result.putIntArray(DH_GROUP_KEY, dhGroupArray);
317 
318         return result;
319     }
320 
321     /**
322      * Check if the current SaProposal from the SA responder is consistent with the selected
323      * reqProposal from the SA initiator.
324      *
325      * <p>As per RFC 7296, The accepted cryptographic suite MUST contain exactly one transform of
326      * each type included in the proposal. But for interoperability reason, IKE library allows
327      * exceptions when the accepted suite or the request proposal has a NONE value transform.
328      * Currently only IntegrityTransform and DhGroupTransform have NONE value transform ID defined.
329      *
330      * @param reqProposal selected SaProposal from SA initiator
331      * @return if current SaProposal from SA responder is consistent with the selected reqProposal
332      *     from SA initiator.
333      * @hide
334      */
isNegotiatedFrom(SaProposal reqProposal)335     public boolean isNegotiatedFrom(SaProposal reqProposal) {
336         return this.mProtocolId == reqProposal.mProtocolId
337                 && isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms)
338                 && isIntegrityTransformSelectedFrom(
339                         mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms)
340                 && isDhGroupTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups);
341     }
342 
343     /**
344      * Check if the response transform can be selected from the request transforms
345      *
346      * <p>Package private
347      */
isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom)348     static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) {
349         // If the selected proposal has multiple transforms with the same type, the responder MUST
350         // choose a single one.
351         if ((selected.length > 1) || (selected.length == 0) != (selectFrom.length == 0)) {
352             return false;
353         }
354 
355         if (selected.length == 0) return true;
356 
357         return Arrays.asList(selectFrom).contains(selected[0]);
358     }
359 
360     /**
361      * Check if the response integrity transform can be selected from the request integrity
362      * transforms.
363      *
364      * <p>For interoperability reason, it is allowed to do not include integrity transform in the
365      * response proposal when the request proposal has a NONE value integrity transform; and it is
366      * also allowed to have a NONE value integrity transform when the request proposal does not have
367      * integrity transforms.
368      */
isIntegrityTransformSelectedFrom( IntegrityTransform[] selected, IntegrityTransform[] selectFrom)369     private static boolean isIntegrityTransformSelectedFrom(
370             IntegrityTransform[] selected, IntegrityTransform[] selectFrom) {
371         if (selected.length == 0) {
372             selected = new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)};
373         }
374         if (selectFrom.length == 0) {
375             selectFrom =
376                     new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)};
377         }
378         return isTransformSelectedFrom(selected, selectFrom);
379     }
380 
381     /**
382      * Check if the response DH group can be selected from the request DH groups
383      *
384      * <p>For interoperability reason, it is allowed to do not include DH group in the response
385      * proposal when the request proposal has a NONE value DH group; and it is also allowed to have
386      * a NONE value DH group when the request proposal does not have DH groups.
387      */
isDhGroupTransformSelectedFrom( DhGroupTransform[] selected, DhGroupTransform[] selectFrom)388     private static boolean isDhGroupTransformSelectedFrom(
389             DhGroupTransform[] selected, DhGroupTransform[] selectFrom) {
390         if (selected.length == 0) {
391             selected = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)};
392         }
393         if (selectFrom.length == 0) {
394             selectFrom = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)};
395         }
396         return isTransformSelectedFrom(selected, selectFrom);
397     }
398 
399     /** @hide */
400     @IkePayload.ProtocolId
getProtocolId()401     public int getProtocolId() {
402         return mProtocolId;
403     }
404 
405     /**
406      * Gets all proposed encryption algorithms
407      *
408      * @return A list of Pairs, with the IANA-defined ID for the proposed encryption algorithm as
409      *     the first item, and the key length (in bits) as the second.
410      */
411     @NonNull
getEncryptionAlgorithms()412     public List<Pair<Integer, Integer>> getEncryptionAlgorithms() {
413         final List<Pair<Integer, Integer>> result = new ArrayList<>();
414         for (EncryptionTransform transform : mEncryptionAlgorithms) {
415             result.add(new Pair(transform.id, transform.getSpecifiedKeyLength()));
416         }
417         return result;
418     }
419 
420     /**
421      * Gets all proposed integrity algorithms
422      *
423      * @return A list of the IANA-defined IDs for the proposed integrity algorithms
424      */
425     @NonNull
getIntegrityAlgorithms()426     public List<Integer> getIntegrityAlgorithms() {
427         final List<Integer> result = new ArrayList<>();
428         for (Transform transform : mIntegrityAlgorithms) {
429             result.add(transform.id);
430         }
431         return result;
432     }
433 
434     /**
435      * Gets all proposed Diffie-Hellman groups
436      *
437      * @return A list of the IANA-defined IDs for the proposed Diffie-Hellman groups
438      */
439     @NonNull
getDhGroups()440     public List<Integer> getDhGroups() {
441         final List<Integer> result = new ArrayList<>();
442         for (Transform transform : mDhGroups) {
443             result.add(transform.id);
444         }
445         return result;
446     }
447 
448     /** @hide */
getEncryptionTransforms()449     public EncryptionTransform[] getEncryptionTransforms() {
450         return mEncryptionAlgorithms;
451     }
452 
453     /** @hide */
getIntegrityTransforms()454     public IntegrityTransform[] getIntegrityTransforms() {
455         return mIntegrityAlgorithms;
456     }
457 
458     /** @hide */
getDhGroupTransforms()459     public DhGroupTransform[] getDhGroupTransforms() {
460         return mDhGroups;
461     }
462 
463     /** @hide */
getAllTransformsAsList()464     protected List<Transform> getAllTransformsAsList() {
465         List<Transform> transformList = new LinkedList<>();
466 
467         transformList.addAll(Arrays.asList(mEncryptionAlgorithms));
468         transformList.addAll(Arrays.asList(mIntegrityAlgorithms));
469         transformList.addAll(Arrays.asList(mDhGroups));
470 
471         return transformList;
472     }
473 
474     /**
475      * Return all SA Transforms in this SaProposal to be encoded for building an outbound IKE
476      * message.
477      *
478      * <p>This method should be called by only IKE library.
479      *
480      * @return Array of Transforms to be encoded.
481      * @hide
482      */
getAllTransforms()483     public abstract Transform[] getAllTransforms();
484 
485     /**
486      * This class is an abstract Builder for building a SaProposal.
487      *
488      * @hide
489      */
490     protected abstract static class Builder {
491         protected static final String ERROR_TAG = "Invalid SA Proposal: ";
492 
493         // Use LinkedHashSet to ensure uniqueness and that ordering is maintained.
494         protected final LinkedHashSet<EncryptionTransform> mProposedEncryptAlgos =
495                 new LinkedHashSet<>();
496         protected final LinkedHashSet<PrfTransform> mProposedPrfs = new LinkedHashSet<>();
497         protected final LinkedHashSet<IntegrityTransform> mProposedIntegrityAlgos =
498                 new LinkedHashSet<>();
499         protected final LinkedHashSet<DhGroupTransform> mProposedDhGroups = new LinkedHashSet<>();
500 
501         protected boolean mHasAead = false;
502 
isAead(@ncryptionAlgorithm int algorithm)503         protected static boolean isAead(@EncryptionAlgorithm int algorithm) {
504             switch (algorithm) {
505                 case ENCRYPTION_ALGORITHM_3DES:
506                     // Fall through
507                 case ENCRYPTION_ALGORITHM_AES_CBC:
508                     // Fall through
509                 case ENCRYPTION_ALGORITHM_AES_CTR:
510                     return false;
511                 case ENCRYPTION_ALGORITHM_AES_GCM_8:
512                     // Fall through
513                 case ENCRYPTION_ALGORITHM_AES_GCM_12:
514                     // Fall through
515                 case ENCRYPTION_ALGORITHM_AES_GCM_16:
516                     // Fall through
517                 case ENCRYPTION_ALGORITHM_CHACHA20_POLY1305:
518                     return true;
519                 default:
520                     // Won't hit here.
521                     throw new IllegalArgumentException("Unsupported Encryption Algorithm.");
522             }
523         }
524 
buildEncryptAlgosOrThrow()525         protected EncryptionTransform[] buildEncryptAlgosOrThrow() {
526             if (mProposedEncryptAlgos.isEmpty()) {
527                 throw new IllegalArgumentException(
528                         ERROR_TAG + "Encryption algorithm must be proposed.");
529             }
530 
531             return mProposedEncryptAlgos.toArray(
532                     new EncryptionTransform[mProposedEncryptAlgos.size()]);
533         }
534 
validateAndAddEncryptAlgo( @ncryptionAlgorithm int algorithm, int keyLength, boolean isChild)535         protected void validateAndAddEncryptAlgo(
536                 @EncryptionAlgorithm int algorithm, int keyLength, boolean isChild) {
537             // Construct EncryptionTransform and validate proposed algorithm during
538             // construction.
539             EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength);
540 
541             // For Child SA algorithm, check if that is supported by IPsec
542             if (isChild
543                     && !ChildSaProposal.getSupportedEncryptionAlgorithms().contains(algorithm)) {
544                 throw new IllegalArgumentException("Unsupported encryption algorithm " + algorithm);
545             }
546 
547             // Validate that only one mode encryption algorithm has been proposed.
548             boolean isCurrentAead = isAead(algorithm);
549             if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) {
550                 throw new IllegalArgumentException(
551                         ERROR_TAG
552                                 + "Proposal cannot has both normal ciphers "
553                                 + "and combined-mode ciphers.");
554             }
555             if (isCurrentAead) mHasAead = true;
556 
557             mProposedEncryptAlgos.add(encryptionTransform);
558         }
559 
validateAndAddIntegrityAlgo( @ntegrityAlgorithm int algorithm, boolean isChild)560         protected void validateAndAddIntegrityAlgo(
561                 @IntegrityAlgorithm int algorithm, boolean isChild) {
562             // For Child SA algorithm, check if that is supported by IPsec
563             if (isChild && !ChildSaProposal.getSupportedIntegrityAlgorithms().contains(algorithm)) {
564                 throw new IllegalArgumentException("Unsupported integrity algorithm " + algorithm);
565             }
566 
567             // Construct IntegrityTransform and validate proposed algorithm during
568             // construction.
569             mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm));
570         }
571 
addDh(@hGroup int dhGroup)572         protected void addDh(@DhGroup int dhGroup) {
573             // Construct DhGroupTransform and validate proposed dhGroup during
574             // construction.
575             mProposedDhGroups.add(new DhGroupTransform(dhGroup));
576         }
577     }
578 
579     /** @hide */
580     @Override
581     @NonNull
toString()582     public String toString() {
583         StringBuilder sb = new StringBuilder();
584 
585         sb.append(IkePayload.getProtocolTypeString(mProtocolId)).append(": ");
586 
587         int len = getAllTransforms().length;
588         for (int i = 0; i < len; i++) {
589             sb.append(getAllTransforms()[i].toString());
590             if (i < len - 1) sb.append("|");
591         }
592 
593         return sb.toString();
594     }
595 
596     @Override
hashCode()597     public int hashCode() {
598         return Objects.hash(
599                 mProtocolId,
600                 Arrays.hashCode(mEncryptionAlgorithms),
601                 Arrays.hashCode(mIntegrityAlgorithms),
602                 Arrays.hashCode(mDhGroups));
603     }
604 
605     @Override
equals(Object o)606     public boolean equals(Object o) {
607         if (!(o instanceof SaProposal)) {
608             return false;
609         }
610 
611         SaProposal other = (SaProposal) o;
612 
613         return mProtocolId == other.mProtocolId
614                 && Arrays.equals(mEncryptionAlgorithms, other.mEncryptionAlgorithms)
615                 && Arrays.equals(mIntegrityAlgorithms, other.mIntegrityAlgorithms)
616                 && Arrays.equals(mDhGroups, other.mDhGroups);
617     }
618 
619     /** @hide */
getKeySet(SparseArray array)620     protected static Set<Integer> getKeySet(SparseArray array) {
621         Set<Integer> result = new HashSet<>();
622         for (int i = 0; i < array.size(); i++) {
623             result.add(array.keyAt(i));
624         }
625 
626         return result;
627     }
628 
629     /** Returns supported DH groups for IKE and Child SA proposal negotiation. */
630     @NonNull
getSupportedDhGroups()631     public static Set<Integer> getSupportedDhGroups() {
632         final Set<Integer> supportedSet = new HashSet<>();
633         for (int dh : getKeySet(SUPPORTED_DH_GROUP_TO_STR)) {
634             if (dh == DH_GROUP_CURVE_25519 && !SdkLevel.isAtLeastS()) {
635                 continue;
636             } else {
637                 supportedSet.add(dh);
638             }
639         }
640         return supportedSet;
641     }
642 
643     /**
644      * Return the encryption algorithm as a String.
645      *
646      * @hide
647      */
getEncryptionAlgorithmString(int algorithm)648     public static String getEncryptionAlgorithmString(int algorithm) {
649         if (SUPPORTED_ENCRYPTION_ALGO_TO_STR.contains(algorithm)) {
650             return SUPPORTED_ENCRYPTION_ALGO_TO_STR.get(algorithm);
651         }
652         return "ENC_Unknown_" + algorithm;
653     }
654 
655     /**
656      * Return the pseudorandom function as a String.
657      *
658      * @hide
659      */
getPseudorandomFunctionString(int algorithm)660     public static String getPseudorandomFunctionString(int algorithm) {
661         if (SUPPORTED_PRF_TO_STR.contains(algorithm)) {
662             return SUPPORTED_PRF_TO_STR.get(algorithm);
663         }
664         return "PRF_Unknown_" + algorithm;
665     }
666 
667     /**
668      * Return the integrity algorithm as a String.
669      *
670      * @hide
671      */
getIntegrityAlgorithmString(int algorithm)672     public static String getIntegrityAlgorithmString(int algorithm) {
673         if (SUPPORTED_INTEGRITY_ALGO_TO_STR.contains(algorithm)) {
674             return SUPPORTED_INTEGRITY_ALGO_TO_STR.get(algorithm);
675         }
676         return "AUTH_Unknown_" + algorithm;
677     }
678 
679     /**
680      * Return Diffie-Hellman Group as a String.
681      *
682      * @hide
683      */
getDhGroupString(int dhGroup)684     public static String getDhGroupString(int dhGroup) {
685         if (SUPPORTED_DH_GROUP_TO_STR.contains(dhGroup)) {
686             return SUPPORTED_DH_GROUP_TO_STR.get(dhGroup);
687         }
688         return "DH_Unknown_" + dhGroup;
689     }
690 }
691