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 
22 import com.android.internal.net.ipsec.ike.message.IkePayload;
23 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform;
24 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
25 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EsnTransform;
26 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform;
27 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform;
28 
29 import java.util.Arrays;
30 import java.util.List;
31 
32 /**
33  * ChildSaProposal represents a proposed configuration to negotiate a Child SA.
34  *
35  * <p>ChildSaProposal will contain cryptograhic algorithms and key generation materials for the
36  * negotiation of a Child SA.
37  *
38  * <p>User must provide at least one valid ChildSaProposal when they are creating a new Child SA.
39  *
40  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
41  *     Protocol Version 2 (IKEv2)</a>
42  * @hide
43  */
44 @SystemApi
45 public final class ChildSaProposal extends SaProposal {
46     private final EsnTransform[] mEsns;
47 
48     /**
49      * Construct an instance of ChildSaProposal.
50      *
51      * <p>This constructor is either called by ChildSaPayload for building an inbound proposal from
52      * a decoded packet, or called by the inner Builder to build an outbound proposal from user
53      * provided parameters
54      *
55      * @param encryptionAlgos encryption algorithms
56      * @param integrityAlgos integrity algorithms
57      * @param dhGroups Diffie-Hellman Groups
58      * @param esns ESN policies
59      * @hide
60      */
ChildSaProposal( EncryptionTransform[] encryptionAlgos, IntegrityTransform[] integrityAlgos, DhGroupTransform[] dhGroups, EsnTransform[] esns)61     public ChildSaProposal(
62             EncryptionTransform[] encryptionAlgos,
63             IntegrityTransform[] integrityAlgos,
64             DhGroupTransform[] dhGroups,
65             EsnTransform[] esns) {
66         super(IkePayload.PROTOCOL_ID_ESP, encryptionAlgos, integrityAlgos, dhGroups);
67         mEsns = esns;
68     }
69 
70     /**
71      * Gets all ESN policies.
72      *
73      * @hide
74      */
getEsnTransforms()75     public EsnTransform[] getEsnTransforms() {
76         return mEsns;
77     }
78 
79     /**
80      * Gets a copy of proposal without all proposed DH groups.
81      *
82      * <p>This is used to avoid negotiating DH Group for negotiating first Child SA.
83      *
84      * @hide
85      */
getCopyWithoutDhTransform()86     public ChildSaProposal getCopyWithoutDhTransform() {
87         return new ChildSaProposal(
88                 getEncryptionTransforms(),
89                 getIntegrityTransforms(),
90                 new DhGroupTransform[0],
91                 getEsnTransforms());
92     }
93 
94     /** @hide */
95     @Override
getAllTransforms()96     public Transform[] getAllTransforms() {
97         List<Transform> transformList = getAllTransformsAsList();
98         transformList.addAll(Arrays.asList(mEsns));
99 
100         return transformList.toArray(new Transform[transformList.size()]);
101     }
102 
103     /** @hide */
104     @Override
isNegotiatedFrom(SaProposal reqProposal)105     public boolean isNegotiatedFrom(SaProposal reqProposal) {
106         return super.isNegotiatedFrom(reqProposal)
107                 && isTransformSelectedFrom(mEsns, ((ChildSaProposal) reqProposal).mEsns);
108     }
109 
110     /** @hide */
isNegotiatedFromExceptDhGroup(SaProposal saProposal)111     public boolean isNegotiatedFromExceptDhGroup(SaProposal saProposal) {
112         return getProtocolId() == saProposal.getProtocolId()
113                 && isTransformSelectedFrom(
114                         getEncryptionTransforms(), saProposal.getEncryptionTransforms())
115                 && isTransformSelectedFrom(
116                         getIntegrityTransforms(), saProposal.getIntegrityTransforms())
117                 && isTransformSelectedFrom(mEsns, ((ChildSaProposal) saProposal).mEsns);
118     }
119 
120     /** @hide */
getCopyWithAdditionalDhTransform(int dhGroup)121     public ChildSaProposal getCopyWithAdditionalDhTransform(int dhGroup) {
122         return new ChildSaProposal(
123                 getEncryptionTransforms(),
124                 getIntegrityTransforms(),
125                 new DhGroupTransform[] {new DhGroupTransform(dhGroup)},
126                 getEsnTransforms());
127     }
128 
129     /**
130      * This class is used to incrementally construct a ChildSaProposal. ChildSaProposal instances
131      * are immutable once built.
132      */
133     public static final class Builder extends SaProposal.Builder {
134         // TODO: Support users to add algorithms from most preferred to least preferred.
135 
136         /**
137          * Adds an encryption algorithm with a specific key length to the SA proposal being built.
138          *
139          * @param algorithm encryption algorithm to add to ChildSaProposal.
140          * @param keyLength key length of algorithm. For algorithms that have fixed key length (e.g.
141          *     3DES) only {@link SaProposal.KEY_LEN_UNUSED} is allowed.
142          * @return Builder of ChildSaProposal.
143          */
144         @NonNull
addEncryptionAlgorithm(@ncryptionAlgorithm int algorithm, int keyLength)145         public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) {
146             validateAndAddEncryptAlgo(algorithm, keyLength);
147             return this;
148         }
149 
150         /**
151          * Adds an integrity algorithm to the SA proposal being built.
152          *
153          * @param algorithm integrity algorithm to add to ChildSaProposal.
154          * @return Builder of ChildSaProposal.
155          */
156         @NonNull
addIntegrityAlgorithm(@ntegrityAlgorithm int algorithm)157         public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
158             addIntegrityAlgo(algorithm);
159             return this;
160         }
161 
162         /**
163          * Adds a Diffie-Hellman Group to the SA proposal being built.
164          *
165          * @param dhGroup to add to ChildSaProposal.
166          * @return Builder of ChildSaProposal.
167          */
168         @NonNull
addDhGroup(@hGroup int dhGroup)169         public Builder addDhGroup(@DhGroup int dhGroup) {
170             addDh(dhGroup);
171             return this;
172         }
173 
buildIntegAlgosOrThrow()174         private IntegrityTransform[] buildIntegAlgosOrThrow() {
175             // When building Child SA Proposal with normal-mode ciphers, there is no contraint on
176             // integrity algorithm. When building Child SA Proposal with combined-mode ciphers,
177             // mProposedIntegrityAlgos must be either empty or only have INTEGRITY_ALGORITHM_NONE.
178             for (IntegrityTransform transform : mProposedIntegrityAlgos) {
179                 if (transform.id != INTEGRITY_ALGORITHM_NONE && mHasAead) {
180                     throw new IllegalArgumentException(
181                             ERROR_TAG
182                                     + "Only INTEGRITY_ALGORITHM_NONE can be"
183                                     + " proposed with combined-mode ciphers in any proposal.");
184                 }
185             }
186 
187             return mProposedIntegrityAlgos.toArray(
188                     new IntegrityTransform[mProposedIntegrityAlgos.size()]);
189         }
190 
191         /**
192          * Validates and builds the ChildSaProposal.
193          *
194          * @return the validated ChildSaProposal.
195          */
196         @NonNull
build()197         public ChildSaProposal build() {
198             EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow();
199             IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow();
200 
201             return new ChildSaProposal(
202                     encryptionTransforms,
203                     integrityTransforms,
204                     mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]),
205                     new EsnTransform[] {new EsnTransform()});
206         }
207     }
208 }
209