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