• 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 static com.android.internal.net.ipsec.ike.message.IkeSaPayload.EsnTransform.ESN_POLICY_NO_EXTENDED;
20 
21 import android.annotation.NonNull;
22 import android.annotation.SuppressLint;
23 import android.net.IpSecAlgorithm;
24 import android.os.PersistableBundle;
25 import android.util.ArraySet;
26 
27 import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
28 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
29 import com.android.internal.net.ipsec.ike.message.IkePayload;
30 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
31 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
32 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EsnTransform;
33 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform;
34 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform;
35 import com.android.modules.utils.build.SdkLevel;
36 import com.android.server.vcn.util.PersistableBundleUtils;
37 
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.Set;
42 
43 /**
44  * ChildSaProposal represents a proposed configuration to negotiate a Child SA.
45  *
46  * <p>ChildSaProposal will contain cryptograhic algorithms and key generation materials for the
47  * negotiation of a Child SA.
48  *
49  * <p>User must provide at least one valid ChildSaProposal when they are creating a new Child SA.
50  *
51  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
52  *     Protocol Version 2 (IKEv2)</a>
53  */
54 public final class ChildSaProposal extends SaProposal {
55     // Before SDK S, there is no API in IpSecAlgorithm to retrieve supported algorithms. Thus hard
56     // coded these algorithms here.
57     private static final Set<Integer> SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S;
58     private static final Set<Integer> SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S;
59 
60     static {
61         SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S = new ArraySet<>();
62         SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S.add(ENCRYPTION_ALGORITHM_AES_CBC);
63         SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S.add(ENCRYPTION_ALGORITHM_AES_GCM_8);
64         SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S.add(ENCRYPTION_ALGORITHM_AES_GCM_12);
65         SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S.add(ENCRYPTION_ALGORITHM_AES_GCM_16);
66 
67         SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S = new ArraySet<>();
68         SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S.add(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
69         SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S.add(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
70         SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S.add(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
71         SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S.add(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
72     }
73 
74     private static final String ESN_KEY = "mEsns";
75     private final EsnTransform[] mEsns;
76 
77     /**
78      * Construct an instance of ChildSaProposal.
79      *
80      * <p>This constructor is either called by ChildSaPayload for building an inbound proposal from
81      * a decoded packet, or called by the inner Builder to build an outbound proposal from user
82      * provided parameters
83      *
84      * @param encryptionAlgos encryption algorithms
85      * @param integrityAlgos integrity algorithms
86      * @param dhGroups Diffie-Hellman Groups
87      * @param esns ESN policies
88      * @hide
89      */
ChildSaProposal( EncryptionTransform[] encryptionAlgos, IntegrityTransform[] integrityAlgos, DhGroupTransform[] dhGroups, EsnTransform[] esns)90     public ChildSaProposal(
91             EncryptionTransform[] encryptionAlgos,
92             IntegrityTransform[] integrityAlgos,
93             DhGroupTransform[] dhGroups,
94             EsnTransform[] esns) {
95         super(IkePayload.PROTOCOL_ID_ESP, encryptionAlgos, integrityAlgos, dhGroups);
96         mEsns = esns;
97     }
98 
99     /**
100      * Constructs this object by deserializing a PersistableBundle
101      *
102      * <p>Constructed proposals are guaranteed to be valid, as checked by the
103      * ChildSaProposal.Builder.
104      *
105      * @hide
106      */
107     @NonNull
fromPersistableBundle(@onNull PersistableBundle in)108     public static ChildSaProposal fromPersistableBundle(@NonNull PersistableBundle in) {
109         Objects.requireNonNull(in, "PersistableBundle is null");
110 
111         ChildSaProposal.Builder builder = new ChildSaProposal.Builder();
112 
113         PersistableBundle encryptionBundle = in.getPersistableBundle(ENCRYPT_ALGO_KEY);
114         Objects.requireNonNull(encryptionBundle, "Encryption algo bundle is null");
115         List<EncryptionTransform> encryptList =
116                 PersistableBundleUtils.toList(
117                         encryptionBundle, EncryptionTransform::fromPersistableBundle);
118         for (EncryptionTransform t : encryptList) {
119             builder.addEncryptionAlgorithm(t.id, t.getSpecifiedKeyLength());
120         }
121 
122         int[] integrityAlgoIdArray = in.getIntArray(INTEGRITY_ALGO_KEY);
123         Objects.requireNonNull(integrityAlgoIdArray, "Integrity algo array is null");
124         for (int algo : integrityAlgoIdArray) {
125             builder.addIntegrityAlgorithm(algo);
126         }
127 
128         int[] dhGroupArray = in.getIntArray(DH_GROUP_KEY);
129         Objects.requireNonNull(dhGroupArray, "DH Group array is null");
130         for (int dh : dhGroupArray) {
131             builder.addDhGroup(dh);
132         }
133 
134         int[] esnPolicies = in.getIntArray(ESN_KEY);
135         Objects.requireNonNull(esnPolicies, "ESN policy array is null");
136 
137         for (int p : esnPolicies) {
138             switch (p) {
139                 case ESN_POLICY_NO_EXTENDED:
140                     // Ignored. All ChildSaProposal(s) are proposed with this automatically
141                     break;
142                 default:
143                     throw new IllegalArgumentException(
144                             "Proposing ESN policy: " + p + " is unsupported");
145             }
146         }
147 
148         return builder.build();
149     }
150 
151     /**
152      * Serializes this object to a PersistableBundle
153      *
154      * @hide
155      */
156     @Override
157     @NonNull
toPersistableBundle()158     public PersistableBundle toPersistableBundle() {
159         final PersistableBundle result = super.toPersistableBundle();
160         int[] esnPolicies = Arrays.asList(mEsns).stream().mapToInt(esn -> esn.id).toArray();
161         result.putIntArray(ESN_KEY, esnPolicies);
162 
163         return result;
164     }
165 
166     /**
167      * Returns supported encryption algorithms for Child SA proposal negotiation.
168      *
169      * <p>Some algorithms may not be supported on old devices.
170      */
171     @NonNull
getSupportedEncryptionAlgorithms()172     public static Set<Integer> getSupportedEncryptionAlgorithms() {
173         if (SdkLevel.isAtLeastS()) {
174             Set<Integer> algoIds = new ArraySet<>();
175             for (int i = 0; i < SUPPORTED_ENCRYPTION_ALGO_TO_STR.size(); i++) {
176                 int ikeAlgoId = SUPPORTED_ENCRYPTION_ALGO_TO_STR.keyAt(i);
177                 String ipSecAlgoName = IkeCipher.getIpSecAlgorithmName(ikeAlgoId);
178                 if (IpSecAlgorithm.getSupportedAlgorithms().contains(ipSecAlgoName)) {
179                     algoIds.add(ikeAlgoId);
180                 }
181             }
182             return algoIds;
183         } else {
184             return SUPPORTED_IPSEC_ENCRYPTION_BEFORE_SDK_S;
185         }
186     }
187 
188     /**
189      * Returns supported integrity algorithms for Child SA proposal negotiation.
190      *
191      * <p>Some algorithms may not be supported on old devices.
192      */
193     @NonNull
getSupportedIntegrityAlgorithms()194     public static Set<Integer> getSupportedIntegrityAlgorithms() {
195         Set<Integer> algoIds = new ArraySet<>();
196 
197         // Although IpSecAlgorithm does not support INTEGRITY_ALGORITHM_NONE, IKE supports
198         // negotiating it and won't build IpSecAlgorithm with it.
199         algoIds.add(INTEGRITY_ALGORITHM_NONE);
200 
201         if (SdkLevel.isAtLeastS()) {
202             for (int i = 0; i < SUPPORTED_INTEGRITY_ALGO_TO_STR.size(); i++) {
203                 int ikeAlgoId = SUPPORTED_INTEGRITY_ALGO_TO_STR.keyAt(i);
204                 String ipSecAlgoName = IkeMacIntegrity.getIpSecAlgorithmName(ikeAlgoId);
205                 if (IpSecAlgorithm.getSupportedAlgorithms().contains(ipSecAlgoName)) {
206                     algoIds.add(ikeAlgoId);
207                 }
208             }
209         } else {
210             algoIds.addAll(SUPPORTED_IPSEC_INTEGRITY_BEFORE_SDK_S);
211         }
212         return algoIds;
213     }
214 
215     /**
216      * Gets all ESN policies.
217      *
218      * @hide
219      */
getEsnTransforms()220     public EsnTransform[] getEsnTransforms() {
221         return mEsns;
222     }
223 
224     /**
225      * Gets a copy of proposal without all proposed DH groups.
226      *
227      * <p>This is used to avoid negotiating DH Group for negotiating first Child SA.
228      *
229      * @hide
230      */
getCopyWithoutDhTransform()231     public ChildSaProposal getCopyWithoutDhTransform() {
232         return new ChildSaProposal(
233                 getEncryptionTransforms(),
234                 getIntegrityTransforms(),
235                 new DhGroupTransform[0],
236                 getEsnTransforms());
237     }
238 
239     /** @hide */
240     @Override
getAllTransforms()241     public Transform[] getAllTransforms() {
242         List<Transform> transformList = getAllTransformsAsList();
243         transformList.addAll(Arrays.asList(mEsns));
244 
245         return transformList.toArray(new Transform[transformList.size()]);
246     }
247 
248     /** @hide */
249     @Override
isNegotiatedFrom(SaProposal reqProposal)250     public boolean isNegotiatedFrom(SaProposal reqProposal) {
251         return super.isNegotiatedFrom(reqProposal)
252                 && isTransformSelectedFrom(mEsns, ((ChildSaProposal) reqProposal).mEsns);
253     }
254 
255     /** @hide */
isNegotiatedFromExceptDhGroup(SaProposal saProposal)256     public boolean isNegotiatedFromExceptDhGroup(SaProposal saProposal) {
257         return getProtocolId() == saProposal.getProtocolId()
258                 && isTransformSelectedFrom(
259                         getEncryptionTransforms(), saProposal.getEncryptionTransforms())
260                 && isTransformSelectedFrom(
261                         getIntegrityTransforms(), saProposal.getIntegrityTransforms())
262                 && isTransformSelectedFrom(mEsns, ((ChildSaProposal) saProposal).mEsns);
263     }
264 
265     /** @hide */
getCopyWithAdditionalDhTransform(int dhGroup)266     public ChildSaProposal getCopyWithAdditionalDhTransform(int dhGroup) {
267         return new ChildSaProposal(
268                 getEncryptionTransforms(),
269                 getIntegrityTransforms(),
270                 new DhGroupTransform[] {new DhGroupTransform(dhGroup)},
271                 getEsnTransforms());
272     }
273 
274     @Override
hashCode()275     public int hashCode() {
276         return Objects.hash(super.hashCode(), Arrays.hashCode(mEsns));
277     }
278 
279     @Override
equals(Object o)280     public boolean equals(Object o) {
281         if (!super.equals(o) || !(o instanceof ChildSaProposal)) {
282             return false;
283         }
284 
285         return Arrays.equals(mEsns, ((ChildSaProposal) o).mEsns);
286     }
287 
288     /**
289      * This class is used to incrementally construct a ChildSaProposal. ChildSaProposal instances
290      * are immutable once built.
291      */
292     public static final class Builder extends SaProposal.Builder {
293         /**
294          * Adds an encryption algorithm with a specific key length to the SA proposal being built.
295          *
296          * @param algorithm encryption algorithm to add to ChildSaProposal.
297          * @param keyLength key length of algorithm. For algorithms that have fixed key length (e.g.
298          *     3DES) only {@link SaProposal#KEY_LEN_UNUSED} is allowed.
299          * @return Builder of ChildSaProposal.
300          */
301         // The matching getter is defined in the super class. Please see {@link
302         // SaProposal#getEncryptionAlgorithms}
303         @SuppressLint("MissingGetterMatchingBuilder")
304         @NonNull
addEncryptionAlgorithm(@ncryptionAlgorithm int algorithm, int keyLength)305         public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) {
306             validateAndAddEncryptAlgo(algorithm, keyLength, true /* isChild */);
307             return this;
308         }
309 
310         /**
311          * Adds an integrity algorithm to the SA proposal being built.
312          *
313          * @param algorithm integrity algorithm to add to ChildSaProposal.
314          * @return Builder of ChildSaProposal.
315          */
316         // The matching getter is defined in the super class. Please see
317         // {@link SaProposal#getIntegrityAlgorithms}
318         @SuppressLint("MissingGetterMatchingBuilder")
319         @NonNull
addIntegrityAlgorithm(@ntegrityAlgorithm int algorithm)320         public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
321             validateAndAddIntegrityAlgo(algorithm, true /* isChild */);
322             return this;
323         }
324 
325         /**
326          * Adds a Diffie-Hellman Group to the SA proposal being built.
327          *
328          * @param dhGroup to add to ChildSaProposal.
329          * @return Builder of ChildSaProposal.
330          */
331         // The matching getter is defined in the super class. Please see
332         // {@link SaProposal#getDhGroups}
333         @SuppressLint("MissingGetterMatchingBuilder")
334         @NonNull
addDhGroup(@hGroup int dhGroup)335         public Builder addDhGroup(@DhGroup int dhGroup) {
336             addDh(dhGroup);
337             return this;
338         }
339 
buildIntegAlgosOrThrow()340         private IntegrityTransform[] buildIntegAlgosOrThrow() {
341             // When building Child SA Proposal with normal-mode ciphers, there is no contraint on
342             // integrity algorithm. When building Child SA Proposal with combined-mode ciphers,
343             // mProposedIntegrityAlgos must be either empty or only have INTEGRITY_ALGORITHM_NONE.
344             for (IntegrityTransform transform : mProposedIntegrityAlgos) {
345                 if (transform.id != INTEGRITY_ALGORITHM_NONE && mHasAead) {
346                     throw new IllegalArgumentException(
347                             ERROR_TAG
348                                     + "Only INTEGRITY_ALGORITHM_NONE can be"
349                                     + " proposed with combined-mode ciphers in any proposal.");
350                 }
351             }
352 
353             return mProposedIntegrityAlgos.toArray(
354                     new IntegrityTransform[mProposedIntegrityAlgos.size()]);
355         }
356 
357         /**
358          * Validates and builds the ChildSaProposal.
359          *
360          * @return the validated ChildSaProposal.
361          */
362         @NonNull
build()363         public ChildSaProposal build() {
364             EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow();
365             IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow();
366 
367             return new ChildSaProposal(
368                     encryptionTransforms,
369                     integrityTransforms,
370                     mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]),
371                     new EsnTransform[] {new EsnTransform()});
372         }
373     }
374 }
375