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