• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.NonNull;
20 import android.annotation.SuppressLint;
21 import android.os.PersistableBundle;
22 import android.util.ArraySet;
23 
24 import com.android.internal.net.ipsec.ike.message.IkePayload;
25 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
26 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
27 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform;
28 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform;
29 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform;
30 import com.android.modules.utils.build.SdkLevel;
31 import com.android.server.vcn.util.PersistableBundleUtils;
32 
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Objects;
38 import java.util.Set;
39 
40 /**
41  * IkeSaProposal represents a proposed configuration to negotiate an IKE SA.
42  *
43  * <p>IkeSaProposal will contain cryptograhic algorithms and key generation materials for the
44  * negotiation of an IKE SA.
45  *
46  * <p>User must provide at least one valid IkeSaProposal when they are creating a new IKE 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  */
51 public final class IkeSaProposal extends SaProposal {
52     private static final String PRF_KEY = "mPseudorandomFunctions";
53     private final PrfTransform[] mPseudorandomFunctions;
54 
55     /**
56      * Construct an instance of IkeSaProposal.
57      *
58      * <p>This constructor is either called by IkeSaPayload for building an inbound proposal from a
59      * decoded packet, or called by the inner Builder to build an outbound proposal from user
60      * provided parameters
61      *
62      * @param encryptionAlgos encryption algorithms
63      * @param prfs pseudorandom functions
64      * @param integrityAlgos integrity algorithms
65      * @param dhGroups Diffie-Hellman Groups
66      * @hide
67      */
IkeSaProposal( EncryptionTransform[] encryptionAlgos, PrfTransform[] prfs, IntegrityTransform[] integrityAlgos, DhGroupTransform[] dhGroups)68     public IkeSaProposal(
69             EncryptionTransform[] encryptionAlgos,
70             PrfTransform[] prfs,
71             IntegrityTransform[] integrityAlgos,
72             DhGroupTransform[] dhGroups) {
73         super(IkePayload.PROTOCOL_ID_IKE, encryptionAlgos, integrityAlgos, dhGroups);
74         mPseudorandomFunctions = prfs;
75     }
76 
77     /**
78      * Constructs this object by deserializing a PersistableBundle
79      *
80      * <p>Constructed proposals are guaranteed to be valid, as checked by the IkeSaProposal.Builder.
81      *
82      * @hide
83      */
84     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)85     public static IkeSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
86         Objects.requireNonNull(in, "PersistableBundle is null");
87 
88         IkeSaProposal.Builder builder = new IkeSaProposal.Builder();
89 
90         PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
91         Objects.requireNonNull(encryptionBundle, "Encryption algo bundle is null");
92         List<EncryptionTransform> encryptList =
93                 PersistableBundleUtils.toList(
94                         encryptionBundle, EncryptionTransform::fromPersistableBundle);
95         for (EncryptionTransform t : encryptList) {
96             builder.addEncryptionAlgorithm(t.id, t.getSpecifiedKeyLength());
97         }
98 
99         int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
100         Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array is null");
101         for (int algo : integrityAlgoIdArray) {
102             builder.addIntegrityAlgorithm(algo);
103         }
104 
105         int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
106         Objects.requireNonNull(dhGroupArray, "DH Group array is null");
107         for (int dh : dhGroupArray) {
108             builder.addDhGroup(dh);
109         }
110 
111         int[] prfArray = in.getIntArray(PRF_KEY);
112         Objects.requireNonNull(prfArray, "PRF array is null");
113         for (int prf : prfArray) {
114             builder.addPseudorandomFunction(prf);
115         }
116 
117         return builder.build();
118     }
119 
120     /**
121      * Serializes this object to a PersistableBundle
122      *
123      * @hide
124      */
125     @Override
126     @NonNull
toPersistableBundle()127     public PersistableBundle toPersistableBundle() {
128         final PersistableBundle result = super.toPersistableBundle();
129 
130         int[] prfArray = getPseudorandomFunctions().stream().mapToInt(i -> i).toArray();
131         result.putIntArray(PRF_KEY, prfArray);
132 
133         return result;
134     }
135 
136     /** Returns supported encryption algorithms for IKE SA proposal negotiation. */
137     @NonNull
getSupportedEncryptionAlgorithms()138     public static Set<Integer> getSupportedEncryptionAlgorithms() {
139         return getKeySet(SUPPORTED_ENCRYPTION_ALGO_TO_STR);
140     }
141 
142     /** Returns supported integrity algorithms for IKE SA proposal negotiation. */
143     @NonNull
getSupportedIntegrityAlgorithms()144     public static Set<Integer> getSupportedIntegrityAlgorithms() {
145         final Set<Integer> supportedSet = new HashSet<>();
146         for (int algo : getKeySet(SUPPORTED_INTEGRITY_ALGO_TO_STR)) {
147             if (algo == INTEGRITY_ALGORITHM_AES_CMAC_96 && !SdkLevel.isAtLeastS()) {
148                 continue;
149             } else {
150                 supportedSet.add(algo);
151             }
152         }
153 
154         return supportedSet;
155     }
156 
157     /** Returns supported pseudorandom functions for IKE SA proposal negotiation. */
158     @NonNull
getSupportedPseudorandomFunctions()159     public static Set<Integer> getSupportedPseudorandomFunctions() {
160         final Set<Integer> supportedSet = new HashSet<>();
161         for (int algo : getKeySet(SUPPORTED_PRF_TO_STR)) {
162             if (algo == PSEUDORANDOM_FUNCTION_AES128_CMAC && !SdkLevel.isAtLeastS()) {
163                 continue;
164             } else {
165                 supportedSet.add(algo);
166             }
167         }
168 
169         return supportedSet;
170     }
171 
172     /**
173      * Gets all proposed Pseudorandom Functions
174      *
175      * @return A list of the IANA-defined IDs for the proposed Pseudorandom Functions
176      */
177     @NonNull
getPseudorandomFunctions()178     public List<Integer> getPseudorandomFunctions() {
179         final List<Integer> result = new ArrayList<>();
180         for (Transform transform : mPseudorandomFunctions) {
181             result.add(transform.id);
182         }
183         return result;
184     }
185 
186     /**
187      * Gets all PRF Transforms
188      *
189      * @hide
190      */
getPrfTransforms()191     public PrfTransform[] getPrfTransforms() {
192         return mPseudorandomFunctions;
193     }
194 
195     /** @hide */
196     @Override
getAllTransforms()197     public Transform[] getAllTransforms() {
198         List<Transform> transformList = getAllTransformsAsList();
199         transformList.addAll(Arrays.asList(mPseudorandomFunctions));
200 
201         return transformList.toArray(new Transform[transformList.size()]);
202     }
203 
204     /** @hide */
205     @Override
isNegotiatedFrom(SaProposal reqProposal)206     public boolean isNegotiatedFrom(SaProposal reqProposal) {
207         return super.isNegotiatedFrom(reqProposal)
208                 && isTransformSelectedFrom(
209                         mPseudorandomFunctions,
210                         ((IkeSaProposal) reqProposal).mPseudorandomFunctions);
211     }
212 
213     @Override
hashCode()214     public int hashCode() {
215         return Objects.hash(super.hashCode(), Arrays.hashCode(mPseudorandomFunctions));
216     }
217 
218     @Override
equals(Object o)219     public boolean equals(Object o) {
220         if (!super.equals(o) || !(o instanceof IkeSaProposal)) {
221             return false;
222         }
223 
224         return Arrays.equals(mPseudorandomFunctions, ((IkeSaProposal) o).mPseudorandomFunctions);
225     }
226 
227     /**
228      * This class is used to incrementally construct a IkeSaProposal. IkeSaProposal instances are
229      * immutable once built.
230      */
231     public static final class Builder extends SaProposal.Builder {
232         // TODO: Support users to add algorithms from most preferred to least preferred.
233 
234         // Use set to avoid adding repeated algorithms.
235         private final Set<PrfTransform> mProposedPrfs = new ArraySet<>();
236 
237         /**
238          * Adds an encryption algorithm with a specific key length to the SA proposal being built.
239          *
240          * @param algorithm encryption algorithm to add to IkeSaProposal.
241          * @param keyLength key length of algorithm. For algorithms that have fixed key length (e.g.
242          *     3DES) only {@link SaProposal#KEY_LEN_UNUSED} is allowed.
243          * @return Builder of IkeSaProposal.
244          */
245         // The matching getter is defined in the super class. Please see {@link
246         // SaProposal#getEncryptionAlgorithms}
247         @SuppressLint("MissingGetterMatchingBuilder")
248         @NonNull
addEncryptionAlgorithm(@ncryptionAlgorithm int algorithm, int keyLength)249         public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) {
250             validateAndAddEncryptAlgo(algorithm, keyLength, false /* isChild */);
251             return this;
252         }
253 
254         /**
255          * Adds an integrity algorithm to the SA proposal being built.
256          *
257          * @param algorithm integrity algorithm to add to IkeSaProposal.
258          * @return Builder of IkeSaProposal.
259          */
260         // The matching getter is defined in the super class. Please see
261         // {@link SaProposal#getIntegrityAlgorithms}
262         @SuppressLint("MissingGetterMatchingBuilder")
263         @NonNull
addIntegrityAlgorithm(@ntegrityAlgorithm int algorithm)264         public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
265             validateAndAddIntegrityAlgo(algorithm, false /* isChild */);
266             return this;
267         }
268 
269         /**
270          * Adds a Diffie-Hellman Group to the SA proposal being built.
271          *
272          * @param dhGroup to add to IkeSaProposal.
273          * @return Builder of IkeSaProposal.
274          */
275         // The matching getter is defined in the super class. Please see
276         // {@link SaProposal#getDhGroups}
277         @SuppressLint("MissingGetterMatchingBuilder")
278         @NonNull
addDhGroup(@hGroup int dhGroup)279         public Builder addDhGroup(@DhGroup int dhGroup) {
280             addDh(dhGroup);
281             return this;
282         }
283 
284         /**
285          * Adds a pseudorandom function to the SA proposal being built.
286          *
287          * @param algorithm pseudorandom function to add to IkeSaProposal.
288          * @return Builder of IkeSaProposal.
289          */
290         @NonNull
addPseudorandomFunction(@seudorandomFunction int algorithm)291         public Builder addPseudorandomFunction(@PseudorandomFunction int algorithm) {
292             // Construct PrfTransform and validate proposed algorithm during construction.
293             mProposedPrfs.add(new PrfTransform(algorithm));
294             return this;
295         }
296 
buildIntegAlgosOrThrow()297         private IntegrityTransform[] buildIntegAlgosOrThrow() {
298             // When building IKE SA Proposal with normal-mode ciphers, mProposedIntegrityAlgos must
299             // not be empty and must not have INTEGRITY_ALGORITHM_NONE. When building IKE SA
300             // Proposal with combined-mode ciphers, mProposedIntegrityAlgos must be either empty or
301             // only have INTEGRITY_ALGORITHM_NONE.
302             if (mProposedIntegrityAlgos.isEmpty() && !mHasAead) {
303                 throw new IllegalArgumentException(
304                         ERROR_TAG
305                                 + "Integrity algorithm "
306                                 + "must be proposed with normal ciphers in IKE proposal.");
307             }
308 
309             for (IntegrityTransform transform : mProposedIntegrityAlgos) {
310                 if ((transform.id == INTEGRITY_ALGORITHM_NONE) != mHasAead) {
311                     throw new IllegalArgumentException(
312                             ERROR_TAG
313                                     + "Invalid integrity algorithm configuration"
314                                     + " for this SA Proposal");
315                 }
316             }
317 
318             return mProposedIntegrityAlgos.toArray(
319                     new IntegrityTransform[mProposedIntegrityAlgos.size()]);
320         }
321 
buildDhGroupsOrThrow()322         private DhGroupTransform[] buildDhGroupsOrThrow() {
323             if (mProposedDhGroups.isEmpty()) {
324                 throw new IllegalArgumentException(
325                         ERROR_TAG + "DH group must be proposed in IKE SA proposal.");
326             }
327 
328             for (DhGroupTransform transform : mProposedDhGroups) {
329                 if (transform.id == DH_GROUP_NONE) {
330                     throw new IllegalArgumentException(
331                             ERROR_TAG + "None-value DH group invalid in IKE SA proposal");
332                 }
333             }
334 
335             return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]);
336         }
337 
buildPrfsOrThrow()338         private PrfTransform[] buildPrfsOrThrow() {
339             if (mProposedPrfs.isEmpty()) {
340                 throw new IllegalArgumentException(
341                         ERROR_TAG + "PRF must be proposed in IKE SA proposal.");
342             }
343             return mProposedPrfs.toArray(new PrfTransform[mProposedPrfs.size()]);
344         }
345 
346         /**
347          * Validates and builds the IkeSaProposal.
348          *
349          * @return the validated IkeSaProposal.
350          */
351         @NonNull
build()352         public IkeSaProposal build() {
353             EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow();
354             PrfTransform[] prfTransforms = buildPrfsOrThrow();
355             IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow();
356             DhGroupTransform[] dhGroupTransforms = buildDhGroupsOrThrow();
357 
358             return new IkeSaProposal(
359                     encryptionTransforms, prfTransforms, integrityTransforms, dhGroupTransforms);
360         }
361     }
362 }
363