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.SystemApi;
21 import android.util.ArraySet;
22 
23 import com.android.internal.net.ipsec.ike.message.IkePayload;
24 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
25 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
26 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform;
27 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform;
28 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform;
29 
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Set;
34 
35 /**
36  * IkeSaProposal represents a proposed configuration to negotiate an IKE SA.
37  *
38  * <p>IkeSaProposal will contain cryptograhic algorithms and key generation materials for the
39  * negotiation of an IKE SA.
40  *
41  * <p>User must provide at least one valid IkeSaProposal when they are creating a new IKE SA.
42  *
43  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
44  *     Protocol Version 2 (IKEv2)</a>
45  * @hide
46  */
47 @SystemApi
48 public final class IkeSaProposal extends SaProposal {
49     private final PrfTransform[] mPseudorandomFunctions;
50 
51     /**
52      * Construct an instance of IkeSaProposal.
53      *
54      * <p>This constructor is either called by IkeSaPayload for building an inbound proposal from a
55      * decoded packet, or called by the inner Builder to build an outbound proposal from user
56      * provided parameters
57      *
58      * @param encryptionAlgos encryption algorithms
59      * @param prfs pseudorandom functions
60      * @param integrityAlgos integrity algorithms
61      * @param dhGroups Diffie-Hellman Groups
62      * @hide
63      */
IkeSaProposal( EncryptionTransform[] encryptionAlgos, PrfTransform[] prfs, IntegrityTransform[] integrityAlgos, DhGroupTransform[] dhGroups)64     public IkeSaProposal(
65             EncryptionTransform[] encryptionAlgos,
66             PrfTransform[] prfs,
67             IntegrityTransform[] integrityAlgos,
68             DhGroupTransform[] dhGroups) {
69         super(IkePayload.PROTOCOL_ID_IKE, encryptionAlgos, integrityAlgos, dhGroups);
70         mPseudorandomFunctions = prfs;
71     }
72 
73     /**
74      * Gets all proposed Pseudorandom Functions
75      *
76      * @return A list of the IANA-defined IDs for the proposed Pseudorandom Functions
77      */
78     @NonNull
getPseudorandomFunctions()79     public List<Integer> getPseudorandomFunctions() {
80         final List<Integer> result = new ArrayList<>();
81         for (Transform transform : mPseudorandomFunctions) {
82             result.add(transform.id);
83         }
84         return result;
85     }
86 
87     /**
88      * Gets all PRF Transforms
89      *
90      * @hide
91      */
getPrfTransforms()92     public PrfTransform[] getPrfTransforms() {
93         return mPseudorandomFunctions;
94     }
95 
96     /** @hide */
97     @Override
getAllTransforms()98     public Transform[] getAllTransforms() {
99         List<Transform> transformList = getAllTransformsAsList();
100         transformList.addAll(Arrays.asList(mPseudorandomFunctions));
101 
102         return transformList.toArray(new Transform[transformList.size()]);
103     }
104 
105     /** @hide */
106     @Override
isNegotiatedFrom(SaProposal reqProposal)107     public boolean isNegotiatedFrom(SaProposal reqProposal) {
108         return super.isNegotiatedFrom(reqProposal)
109                 && isTransformSelectedFrom(
110                         mPseudorandomFunctions,
111                         ((IkeSaProposal) reqProposal).mPseudorandomFunctions);
112     }
113 
114     /**
115      * This class is used to incrementally construct a IkeSaProposal. IkeSaProposal instances are
116      * immutable once built.
117      */
118     public static final class Builder extends SaProposal.Builder {
119         // TODO: Support users to add algorithms from most preferred to least preferred.
120 
121         // Use set to avoid adding repeated algorithms.
122         private final Set<PrfTransform> mProposedPrfs = new ArraySet<>();
123 
124         /**
125          * Adds an encryption algorithm with a specific key length to the SA proposal being built.
126          *
127          * @param algorithm encryption algorithm to add to IkeSaProposal.
128          * @param keyLength key length of algorithm. For algorithms that have fixed key length (e.g.
129          *     3DES) only {@link SaProposal.KEY_LEN_UNUSED} is allowed.
130          * @return Builder of IkeSaProposal.
131          */
132         @NonNull
addEncryptionAlgorithm(@ncryptionAlgorithm int algorithm, int keyLength)133         public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) {
134             validateAndAddEncryptAlgo(algorithm, keyLength);
135             return this;
136         }
137 
138         /**
139          * Adds an integrity algorithm to the SA proposal being built.
140          *
141          * @param algorithm integrity algorithm to add to IkeSaProposal.
142          * @return Builder of IkeSaProposal.
143          */
144         @NonNull
addIntegrityAlgorithm(@ntegrityAlgorithm int algorithm)145         public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
146             addIntegrityAlgo(algorithm);
147             return this;
148         }
149 
150         /**
151          * Adds a Diffie-Hellman Group to the SA proposal being built.
152          *
153          * @param dhGroup to add to IkeSaProposal.
154          * @return Builder of IkeSaProposal.
155          */
156         @NonNull
addDhGroup(@hGroup int dhGroup)157         public Builder addDhGroup(@DhGroup int dhGroup) {
158             addDh(dhGroup);
159             return this;
160         }
161 
162         /**
163          * Adds a pseudorandom function to the SA proposal being built.
164          *
165          * @param algorithm pseudorandom function to add to IkeSaProposal.
166          * @return Builder of IkeSaProposal.
167          */
168         @NonNull
addPseudorandomFunction(@seudorandomFunction int algorithm)169         public Builder addPseudorandomFunction(@PseudorandomFunction int algorithm) {
170             // Construct PrfTransform and validate proposed algorithm during construction.
171             mProposedPrfs.add(new PrfTransform(algorithm));
172             return this;
173         }
174 
buildIntegAlgosOrThrow()175         private IntegrityTransform[] buildIntegAlgosOrThrow() {
176             // When building IKE SA Proposal with normal-mode ciphers, mProposedIntegrityAlgos must
177             // not be empty and must not have INTEGRITY_ALGORITHM_NONE. When building IKE SA
178             // Proposal with combined-mode ciphers, mProposedIntegrityAlgos must be either empty or
179             // only have INTEGRITY_ALGORITHM_NONE.
180             if (mProposedIntegrityAlgos.isEmpty() && !mHasAead) {
181                 throw new IllegalArgumentException(
182                         ERROR_TAG
183                                 + "Integrity algorithm "
184                                 + "must be proposed with normal ciphers in IKE proposal.");
185             }
186 
187             for (IntegrityTransform transform : mProposedIntegrityAlgos) {
188                 if ((transform.id == INTEGRITY_ALGORITHM_NONE) != mHasAead) {
189                     throw new IllegalArgumentException(
190                             ERROR_TAG
191                                     + "Invalid integrity algorithm configuration"
192                                     + " for this SA Proposal");
193                 }
194             }
195 
196             return mProposedIntegrityAlgos.toArray(
197                     new IntegrityTransform[mProposedIntegrityAlgos.size()]);
198         }
199 
buildDhGroupsOrThrow()200         private DhGroupTransform[] buildDhGroupsOrThrow() {
201             if (mProposedDhGroups.isEmpty()) {
202                 throw new IllegalArgumentException(
203                         ERROR_TAG + "DH group must be proposed in IKE SA proposal.");
204             }
205 
206             for (DhGroupTransform transform : mProposedDhGroups) {
207                 if (transform.id == DH_GROUP_NONE) {
208                     throw new IllegalArgumentException(
209                             ERROR_TAG + "None-value DH group invalid in IKE SA proposal");
210                 }
211             }
212 
213             return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]);
214         }
215 
buildPrfsOrThrow()216         private PrfTransform[] buildPrfsOrThrow() {
217             if (mProposedPrfs.isEmpty()) {
218                 throw new IllegalArgumentException(
219                         ERROR_TAG + "PRF must be proposed in IKE SA proposal.");
220             }
221             return mProposedPrfs.toArray(new PrfTransform[mProposedPrfs.size()]);
222         }
223 
224         /**
225          * Validates and builds the IkeSaProposal.
226          *
227          * @return the validated IkeSaProposal.
228          */
229         @NonNull
build()230         public IkeSaProposal build() {
231             EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow();
232             PrfTransform[] prfTransforms = buildPrfsOrThrow();
233             IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow();
234             DhGroupTransform[] dhGroupTransforms = buildDhGroupsOrThrow();
235 
236             return new IkeSaProposal(
237                     encryptionTransforms, prfTransforms, integrityTransforms, dhGroupTransforms);
238         }
239     }
240 }
241