1 /* 2 * Copyright (C) 2018 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.IntDef; 20 import android.annotation.NonNull; 21 import android.os.PersistableBundle; 22 import android.util.Pair; 23 import android.util.SparseArray; 24 25 import com.android.internal.net.ipsec.ike.message.IkePayload; 26 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.DhGroupTransform; 27 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform; 28 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.IntegrityTransform; 29 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.PrfTransform; 30 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.Transform; 31 import com.android.modules.utils.build.SdkLevel; 32 import com.android.server.vcn.util.PersistableBundleUtils; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.HashSet; 39 import java.util.LinkedHashSet; 40 import java.util.LinkedList; 41 import java.util.List; 42 import java.util.Objects; 43 import java.util.Set; 44 45 /** 46 * SaProposal represents a proposed configuration to negotiate an IKE or Child SA. 47 * 48 * <p>SaProposal will contain cryptograhic algorithms and key generation materials for the 49 * negotiation of an IKE or Child SA. 50 * 51 * <p>User must provide at least one valid SaProposal when they are creating a new IKE or Child SA. 52 * 53 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange 54 * Protocol Version 2 (IKEv2)</a> 55 */ 56 public abstract class SaProposal { 57 /** @hide */ 58 @Retention(RetentionPolicy.SOURCE) 59 @IntDef({ 60 ENCRYPTION_ALGORITHM_3DES, 61 ENCRYPTION_ALGORITHM_AES_CBC, 62 ENCRYPTION_ALGORITHM_AES_CTR, 63 ENCRYPTION_ALGORITHM_AES_GCM_8, 64 ENCRYPTION_ALGORITHM_AES_GCM_12, 65 ENCRYPTION_ALGORITHM_AES_GCM_16, 66 ENCRYPTION_ALGORITHM_CHACHA20_POLY1305 67 }) 68 public @interface EncryptionAlgorithm {} 69 70 /** 3DES Encryption/Ciphering Algorithm. */ 71 public static final int ENCRYPTION_ALGORITHM_3DES = 3; 72 /** AES-CBC Encryption/Ciphering Algorithm. */ 73 public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; 74 /** AES-CTR Encryption/Ciphering Algorithm. */ 75 public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13; 76 /** 77 * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 8-octet ICV 78 * (truncation). 79 */ 80 public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; 81 /** 82 * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 12-octet ICV 83 * (truncation). 84 */ 85 public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; 86 /** 87 * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV 88 * (truncation). 89 */ 90 public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; 91 /** 92 * ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV 93 * (truncation). 94 */ 95 public static final int ENCRYPTION_ALGORITHM_CHACHA20_POLY1305 = 28; 96 97 /** @hide */ 98 protected static final SparseArray<String> SUPPORTED_ENCRYPTION_ALGO_TO_STR; 99 100 static { 101 SUPPORTED_ENCRYPTION_ALGO_TO_STR = new SparseArray<>(); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_3DES, "ENCR_3DES")102 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_3DES, "ENCR_3DES"); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CBC, "ENCR_AES_CBC")103 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CBC, "ENCR_AES_CBC"); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CTR, "ENCR_AES_CTR")104 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_CTR, "ENCR_AES_CTR"); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_8, "ENCR_AES_GCM_8")105 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_8, "ENCR_AES_GCM_8"); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_12, "ENCR_AES_GCM_12")106 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_12, "ENCR_AES_GCM_12"); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_16, "ENCR_AES_GCM_16")107 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put(ENCRYPTION_ALGORITHM_AES_GCM_16, "ENCR_AES_GCM_16"); SUPPORTED_ENCRYPTION_ALGO_TO_STR.put( ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, "ENCR_CHACHA20_POLY1305")108 SUPPORTED_ENCRYPTION_ALGO_TO_STR.put( 109 ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, "ENCR_CHACHA20_POLY1305"); 110 } 111 112 /** 113 * Key length unused. 114 * 115 * <p>This value should only be used with the Encryption/Ciphering Algorithm that accepts a 116 * fixed key size such as {@link #ENCRYPTION_ALGORITHM_3DES}. 117 */ 118 public static final int KEY_LEN_UNUSED = 0; 119 /** AES Encryption/Ciphering Algorithm key length 128 bits. */ 120 public static final int KEY_LEN_AES_128 = 128; 121 /** AES Encryption/Ciphering Algorithm key length 192 bits. */ 122 public static final int KEY_LEN_AES_192 = 192; 123 /** AES Encryption/Ciphering Algorithm key length 256 bits. */ 124 public static final int KEY_LEN_AES_256 = 256; 125 126 /** @hide */ 127 @Retention(RetentionPolicy.SOURCE) 128 @IntDef({ 129 PSEUDORANDOM_FUNCTION_HMAC_SHA1, 130 PSEUDORANDOM_FUNCTION_AES128_XCBC, 131 PSEUDORANDOM_FUNCTION_SHA2_256, 132 PSEUDORANDOM_FUNCTION_SHA2_384, 133 PSEUDORANDOM_FUNCTION_SHA2_512, 134 PSEUDORANDOM_FUNCTION_AES128_CMAC 135 }) 136 public @interface PseudorandomFunction {} 137 138 /** HMAC-SHA1 Pseudorandom Function. */ 139 public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; 140 /** AES128-XCBC Pseudorandom Function. */ 141 public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; 142 /** HMAC-SHA2-256 Pseudorandom Function. */ 143 public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5; 144 /** HMAC-SHA2-384 Pseudorandom Function. */ 145 public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6; 146 /** HMAC-SHA2-384 Pseudorandom Function. */ 147 public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7; 148 /** AES128-CMAC Pseudorandom Function. */ 149 public static final int PSEUDORANDOM_FUNCTION_AES128_CMAC = 8; 150 151 /** @hide */ 152 protected static final SparseArray<String> SUPPORTED_PRF_TO_STR; 153 154 static { 155 SUPPORTED_PRF_TO_STR = new SparseArray<>(); SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_HMAC_SHA1, "PRF_HMAC_SHA1")156 SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_HMAC_SHA1, "PRF_HMAC_SHA1"); SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_XCBC, "PRF_AES128_XCBC")157 SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_XCBC, "PRF_AES128_XCBC"); SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_256, "PRF_HMAC2_256")158 SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_256, "PRF_HMAC2_256"); SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_384, "PRF_HMAC2_384")159 SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_384, "PRF_HMAC2_384"); SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_512, "PRF_HMAC2_512")160 SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_SHA2_512, "PRF_HMAC2_512"); SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_CMAC, "PRF_AES128_CMAC")161 SUPPORTED_PRF_TO_STR.put(PSEUDORANDOM_FUNCTION_AES128_CMAC, "PRF_AES128_CMAC"); 162 } 163 164 /** @hide */ 165 @Retention(RetentionPolicy.SOURCE) 166 @IntDef({ 167 INTEGRITY_ALGORITHM_NONE, 168 INTEGRITY_ALGORITHM_HMAC_SHA1_96, 169 INTEGRITY_ALGORITHM_AES_XCBC_96, 170 INTEGRITY_ALGORITHM_AES_CMAC_96, 171 INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 172 INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 173 INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 174 }) 175 public @interface IntegrityAlgorithm {} 176 177 /** None Authentication/Integrity Algorithm. */ 178 public static final int INTEGRITY_ALGORITHM_NONE = 0; 179 /** HMAC-SHA1 Authentication/Integrity Algorithm. */ 180 public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; 181 /** AES-XCBC-96 Authentication/Integrity Algorithm. */ 182 public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; 183 /** AES-CMAC-96 Authentication/Integrity Algorithm. */ 184 public static final int INTEGRITY_ALGORITHM_AES_CMAC_96 = 8; 185 /** HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation. */ 186 public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; 187 /** HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation. */ 188 public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; 189 /** HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation. */ 190 public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; 191 192 /** @hide */ 193 protected static final SparseArray<String> SUPPORTED_INTEGRITY_ALGO_TO_STR; 194 195 static { 196 SUPPORTED_INTEGRITY_ALGO_TO_STR = new SparseArray<>(); SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_NONE, "AUTH_NONE")197 SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_NONE, "AUTH_NONE"); SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96")198 SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, "AUTH_HMAC_SHA1_96"); SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_XCBC_96, "AUTH_AES_XCBC_96")199 SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_XCBC_96, "AUTH_AES_XCBC_96"); SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_CMAC_96, "AUTH_AES_CMAC_96")200 SUPPORTED_INTEGRITY_ALGO_TO_STR.put(INTEGRITY_ALGORITHM_AES_CMAC_96, "AUTH_AES_CMAC_96"); SUPPORTED_INTEGRITY_ALGO_TO_STR.put( INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, "AUTH_HMAC_SHA2_256_128")201 SUPPORTED_INTEGRITY_ALGO_TO_STR.put( 202 INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, "AUTH_HMAC_SHA2_256_128"); SUPPORTED_INTEGRITY_ALGO_TO_STR.put( INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, "AUTH_HMAC_SHA2_384_192")203 SUPPORTED_INTEGRITY_ALGO_TO_STR.put( 204 INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, "AUTH_HMAC_SHA2_384_192"); SUPPORTED_INTEGRITY_ALGO_TO_STR.put( INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, "AUTH_HMAC_SHA2_512_256")205 SUPPORTED_INTEGRITY_ALGO_TO_STR.put( 206 INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, "AUTH_HMAC_SHA2_512_256"); 207 } 208 209 /** @hide */ 210 @Retention(RetentionPolicy.SOURCE) 211 @IntDef({ 212 DH_GROUP_NONE, 213 DH_GROUP_1024_BIT_MODP, 214 DH_GROUP_1536_BIT_MODP, 215 DH_GROUP_2048_BIT_MODP, 216 DH_GROUP_3072_BIT_MODP, 217 DH_GROUP_4096_BIT_MODP, 218 DH_GROUP_CURVE_25519 219 }) 220 public @interface DhGroup {} 221 222 /** None Diffie-Hellman Group. */ 223 public static final int DH_GROUP_NONE = 0; 224 /** 1024-bit MODP Diffie-Hellman Group. */ 225 public static final int DH_GROUP_1024_BIT_MODP = 2; 226 /** 1536-bit MODP Diffie-Hellman Group. */ 227 public static final int DH_GROUP_1536_BIT_MODP = 5; 228 /** 2048-bit MODP Diffie-Hellman Group. */ 229 public static final int DH_GROUP_2048_BIT_MODP = 14; 230 /** 3072-bit MODP Diffie-Hellman Group. */ 231 public static final int DH_GROUP_3072_BIT_MODP = 15; 232 /** 4096-bit MODP Diffie-Hellman Group. */ 233 public static final int DH_GROUP_4096_BIT_MODP = 16; 234 /** Elliptic Curve Diffie-Hellman 25519. */ 235 public static final int DH_GROUP_CURVE_25519 = 31; 236 237 private static final SparseArray<String> SUPPORTED_DH_GROUP_TO_STR; 238 239 static { 240 SUPPORTED_DH_GROUP_TO_STR = new SparseArray<>(); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_NONE, "DH_NONE")241 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_NONE, "DH_NONE"); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1024_BIT_MODP, "DH_1024_BIT_MODP")242 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1024_BIT_MODP, "DH_1024_BIT_MODP"); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1536_BIT_MODP, "DH_1536_BIT_MODP")243 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_1536_BIT_MODP, "DH_1536_BIT_MODP"); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_2048_BIT_MODP, "DH_2048_BIT_MODP")244 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_2048_BIT_MODP, "DH_2048_BIT_MODP"); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_3072_BIT_MODP, "DH_3072_BIT_MODP")245 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_3072_BIT_MODP, "DH_3072_BIT_MODP"); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_4096_BIT_MODP, "DH_4096_BIT_MODP")246 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_4096_BIT_MODP, "DH_4096_BIT_MODP"); SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_CURVE_25519, "DH_GROUP_CURVE_25519")247 SUPPORTED_DH_GROUP_TO_STR.put(DH_GROUP_CURVE_25519, "DH_GROUP_CURVE_25519"); 248 } 249 250 private static final String PROTOCOL_ID_KEY = "mProtocolId"; 251 /** @hide */ 252 protected static final String ENCRYPT_ALGO_KEY = "mEncryptionAlgorithms"; 253 /** @hide */ 254 protected static final String INTEGRITY_ALGO_KEY = "mIntegrityAlgorithms"; 255 /** @hide */ 256 protected static final String DH_GROUP_KEY = "mDhGroups"; 257 258 @IkePayload.ProtocolId private final int mProtocolId; 259 private final EncryptionTransform[] mEncryptionAlgorithms; 260 private final IntegrityTransform[] mIntegrityAlgorithms; 261 private final DhGroupTransform[] mDhGroups; 262 263 /** @hide */ SaProposal( @kePayload.ProtocolId int protocol, EncryptionTransform[] encryptionAlgos, IntegrityTransform[] integrityAlgos, DhGroupTransform[] dhGroups)264 protected SaProposal( 265 @IkePayload.ProtocolId int protocol, 266 EncryptionTransform[] encryptionAlgos, 267 IntegrityTransform[] integrityAlgos, 268 DhGroupTransform[] dhGroups) { 269 mProtocolId = protocol; 270 mEncryptionAlgorithms = encryptionAlgos; 271 mIntegrityAlgorithms = integrityAlgos; 272 mDhGroups = dhGroups; 273 } 274 275 /** 276 * Constructs this object by deserializing a PersistableBundle 277 * 278 * @hide 279 */ 280 @NonNull fromPersistableBundle(@onNull PersistableBundle in)281 public static SaProposal fromPersistableBundle(@NonNull PersistableBundle in) { 282 Objects.requireNonNull(in, "PersistableBundle is null"); 283 284 int protocolId = in.getInt(PROTOCOL_ID_KEY); 285 switch (protocolId) { 286 case IkePayload.PROTOCOL_ID_IKE: 287 return IkeSaProposal.fromPersistableBundle(in); 288 case IkePayload.PROTOCOL_ID_ESP: 289 return ChildSaProposal.fromPersistableBundle(in); 290 default: 291 throw new IllegalArgumentException("Invalid protocol ID " + protocolId); 292 } 293 } 294 295 /** 296 * Serializes this object to a PersistableBundle 297 * 298 * @hide 299 */ 300 @NonNull toPersistableBundle()301 public PersistableBundle toPersistableBundle() { 302 final PersistableBundle result = new PersistableBundle(); 303 304 result.putInt(PROTOCOL_ID_KEY, mProtocolId); 305 306 PersistableBundle encryptionBundle = 307 PersistableBundleUtils.fromList( 308 Arrays.asList(mEncryptionAlgorithms), 309 EncryptionTransform::toPersistableBundle); 310 result.putPersistableBundle(ENCRYPT_ALGO_KEY, encryptionBundle); 311 312 int[] integrityAlgoIdArray = getIntegrityAlgorithms().stream().mapToInt(i -> i).toArray(); 313 result.putIntArray(INTEGRITY_ALGO_KEY, integrityAlgoIdArray); 314 315 int[] dhGroupArray = getDhGroups().stream().mapToInt(i -> i).toArray(); 316 result.putIntArray(DH_GROUP_KEY, dhGroupArray); 317 318 return result; 319 } 320 321 /** 322 * Check if the current SaProposal from the SA responder is consistent with the selected 323 * reqProposal from the SA initiator. 324 * 325 * <p>As per RFC 7296, The accepted cryptographic suite MUST contain exactly one transform of 326 * each type included in the proposal. But for interoperability reason, IKE library allows 327 * exceptions when the accepted suite or the request proposal has a NONE value transform. 328 * Currently only IntegrityTransform and DhGroupTransform have NONE value transform ID defined. 329 * 330 * @param reqProposal selected SaProposal from SA initiator 331 * @return if current SaProposal from SA responder is consistent with the selected reqProposal 332 * from SA initiator. 333 * @hide 334 */ isNegotiatedFrom(SaProposal reqProposal)335 public boolean isNegotiatedFrom(SaProposal reqProposal) { 336 return this.mProtocolId == reqProposal.mProtocolId 337 && isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms) 338 && isIntegrityTransformSelectedFrom( 339 mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms) 340 && isDhGroupTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups); 341 } 342 343 /** 344 * Check if the response transform can be selected from the request transforms 345 * 346 * <p>Package private 347 */ isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom)348 static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) { 349 // If the selected proposal has multiple transforms with the same type, the responder MUST 350 // choose a single one. 351 if ((selected.length > 1) || (selected.length == 0) != (selectFrom.length == 0)) { 352 return false; 353 } 354 355 if (selected.length == 0) return true; 356 357 return Arrays.asList(selectFrom).contains(selected[0]); 358 } 359 360 /** 361 * Check if the response integrity transform can be selected from the request integrity 362 * transforms. 363 * 364 * <p>For interoperability reason, it is allowed to do not include integrity transform in the 365 * response proposal when the request proposal has a NONE value integrity transform; and it is 366 * also allowed to have a NONE value integrity transform when the request proposal does not have 367 * integrity transforms. 368 */ isIntegrityTransformSelectedFrom( IntegrityTransform[] selected, IntegrityTransform[] selectFrom)369 private static boolean isIntegrityTransformSelectedFrom( 370 IntegrityTransform[] selected, IntegrityTransform[] selectFrom) { 371 if (selected.length == 0) { 372 selected = new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)}; 373 } 374 if (selectFrom.length == 0) { 375 selectFrom = 376 new IntegrityTransform[] {new IntegrityTransform(INTEGRITY_ALGORITHM_NONE)}; 377 } 378 return isTransformSelectedFrom(selected, selectFrom); 379 } 380 381 /** 382 * Check if the response DH group can be selected from the request DH groups 383 * 384 * <p>For interoperability reason, it is allowed to do not include DH group in the response 385 * proposal when the request proposal has a NONE value DH group; and it is also allowed to have 386 * a NONE value DH group when the request proposal does not have DH groups. 387 */ isDhGroupTransformSelectedFrom( DhGroupTransform[] selected, DhGroupTransform[] selectFrom)388 private static boolean isDhGroupTransformSelectedFrom( 389 DhGroupTransform[] selected, DhGroupTransform[] selectFrom) { 390 if (selected.length == 0) { 391 selected = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)}; 392 } 393 if (selectFrom.length == 0) { 394 selectFrom = new DhGroupTransform[] {new DhGroupTransform(DH_GROUP_NONE)}; 395 } 396 return isTransformSelectedFrom(selected, selectFrom); 397 } 398 399 /** @hide */ 400 @IkePayload.ProtocolId getProtocolId()401 public int getProtocolId() { 402 return mProtocolId; 403 } 404 405 /** 406 * Gets all proposed encryption algorithms 407 * 408 * @return A list of Pairs, with the IANA-defined ID for the proposed encryption algorithm as 409 * the first item, and the key length (in bits) as the second. 410 */ 411 @NonNull getEncryptionAlgorithms()412 public List<Pair<Integer, Integer>> getEncryptionAlgorithms() { 413 final List<Pair<Integer, Integer>> result = new ArrayList<>(); 414 for (EncryptionTransform transform : mEncryptionAlgorithms) { 415 result.add(new Pair(transform.id, transform.getSpecifiedKeyLength())); 416 } 417 return result; 418 } 419 420 /** 421 * Gets all proposed integrity algorithms 422 * 423 * @return A list of the IANA-defined IDs for the proposed integrity algorithms 424 */ 425 @NonNull getIntegrityAlgorithms()426 public List<Integer> getIntegrityAlgorithms() { 427 final List<Integer> result = new ArrayList<>(); 428 for (Transform transform : mIntegrityAlgorithms) { 429 result.add(transform.id); 430 } 431 return result; 432 } 433 434 /** 435 * Gets all proposed Diffie-Hellman groups 436 * 437 * @return A list of the IANA-defined IDs for the proposed Diffie-Hellman groups 438 */ 439 @NonNull getDhGroups()440 public List<Integer> getDhGroups() { 441 final List<Integer> result = new ArrayList<>(); 442 for (Transform transform : mDhGroups) { 443 result.add(transform.id); 444 } 445 return result; 446 } 447 448 /** @hide */ getEncryptionTransforms()449 public EncryptionTransform[] getEncryptionTransforms() { 450 return mEncryptionAlgorithms; 451 } 452 453 /** @hide */ getIntegrityTransforms()454 public IntegrityTransform[] getIntegrityTransforms() { 455 return mIntegrityAlgorithms; 456 } 457 458 /** @hide */ getDhGroupTransforms()459 public DhGroupTransform[] getDhGroupTransforms() { 460 return mDhGroups; 461 } 462 463 /** @hide */ getAllTransformsAsList()464 protected List<Transform> getAllTransformsAsList() { 465 List<Transform> transformList = new LinkedList<>(); 466 467 transformList.addAll(Arrays.asList(mEncryptionAlgorithms)); 468 transformList.addAll(Arrays.asList(mIntegrityAlgorithms)); 469 transformList.addAll(Arrays.asList(mDhGroups)); 470 471 return transformList; 472 } 473 474 /** 475 * Return all SA Transforms in this SaProposal to be encoded for building an outbound IKE 476 * message. 477 * 478 * <p>This method should be called by only IKE library. 479 * 480 * @return Array of Transforms to be encoded. 481 * @hide 482 */ getAllTransforms()483 public abstract Transform[] getAllTransforms(); 484 485 /** 486 * This class is an abstract Builder for building a SaProposal. 487 * 488 * @hide 489 */ 490 protected abstract static class Builder { 491 protected static final String ERROR_TAG = "Invalid SA Proposal: "; 492 493 // Use LinkedHashSet to ensure uniqueness and that ordering is maintained. 494 protected final LinkedHashSet<EncryptionTransform> mProposedEncryptAlgos = 495 new LinkedHashSet<>(); 496 protected final LinkedHashSet<PrfTransform> mProposedPrfs = new LinkedHashSet<>(); 497 protected final LinkedHashSet<IntegrityTransform> mProposedIntegrityAlgos = 498 new LinkedHashSet<>(); 499 protected final LinkedHashSet<DhGroupTransform> mProposedDhGroups = new LinkedHashSet<>(); 500 501 protected boolean mHasAead = false; 502 isAead(@ncryptionAlgorithm int algorithm)503 protected static boolean isAead(@EncryptionAlgorithm int algorithm) { 504 switch (algorithm) { 505 case ENCRYPTION_ALGORITHM_3DES: 506 // Fall through 507 case ENCRYPTION_ALGORITHM_AES_CBC: 508 // Fall through 509 case ENCRYPTION_ALGORITHM_AES_CTR: 510 return false; 511 case ENCRYPTION_ALGORITHM_AES_GCM_8: 512 // Fall through 513 case ENCRYPTION_ALGORITHM_AES_GCM_12: 514 // Fall through 515 case ENCRYPTION_ALGORITHM_AES_GCM_16: 516 // Fall through 517 case ENCRYPTION_ALGORITHM_CHACHA20_POLY1305: 518 return true; 519 default: 520 // Won't hit here. 521 throw new IllegalArgumentException("Unsupported Encryption Algorithm."); 522 } 523 } 524 buildEncryptAlgosOrThrow()525 protected EncryptionTransform[] buildEncryptAlgosOrThrow() { 526 if (mProposedEncryptAlgos.isEmpty()) { 527 throw new IllegalArgumentException( 528 ERROR_TAG + "Encryption algorithm must be proposed."); 529 } 530 531 return mProposedEncryptAlgos.toArray( 532 new EncryptionTransform[mProposedEncryptAlgos.size()]); 533 } 534 validateAndAddEncryptAlgo( @ncryptionAlgorithm int algorithm, int keyLength, boolean isChild)535 protected void validateAndAddEncryptAlgo( 536 @EncryptionAlgorithm int algorithm, int keyLength, boolean isChild) { 537 // Construct EncryptionTransform and validate proposed algorithm during 538 // construction. 539 EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength); 540 541 // For Child SA algorithm, check if that is supported by IPsec 542 if (isChild 543 && !ChildSaProposal.getSupportedEncryptionAlgorithms().contains(algorithm)) { 544 throw new IllegalArgumentException("Unsupported encryption algorithm " + algorithm); 545 } 546 547 // Validate that only one mode encryption algorithm has been proposed. 548 boolean isCurrentAead = isAead(algorithm); 549 if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) { 550 throw new IllegalArgumentException( 551 ERROR_TAG 552 + "Proposal cannot has both normal ciphers " 553 + "and combined-mode ciphers."); 554 } 555 if (isCurrentAead) mHasAead = true; 556 557 mProposedEncryptAlgos.add(encryptionTransform); 558 } 559 validateAndAddIntegrityAlgo( @ntegrityAlgorithm int algorithm, boolean isChild)560 protected void validateAndAddIntegrityAlgo( 561 @IntegrityAlgorithm int algorithm, boolean isChild) { 562 // For Child SA algorithm, check if that is supported by IPsec 563 if (isChild && !ChildSaProposal.getSupportedIntegrityAlgorithms().contains(algorithm)) { 564 throw new IllegalArgumentException("Unsupported integrity algorithm " + algorithm); 565 } 566 567 // Construct IntegrityTransform and validate proposed algorithm during 568 // construction. 569 mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm)); 570 } 571 addDh(@hGroup int dhGroup)572 protected void addDh(@DhGroup int dhGroup) { 573 // Construct DhGroupTransform and validate proposed dhGroup during 574 // construction. 575 mProposedDhGroups.add(new DhGroupTransform(dhGroup)); 576 } 577 } 578 579 /** @hide */ 580 @Override 581 @NonNull toString()582 public String toString() { 583 StringBuilder sb = new StringBuilder(); 584 585 sb.append(IkePayload.getProtocolTypeString(mProtocolId)).append(": "); 586 587 int len = getAllTransforms().length; 588 for (int i = 0; i < len; i++) { 589 sb.append(getAllTransforms()[i].toString()); 590 if (i < len - 1) sb.append("|"); 591 } 592 593 return sb.toString(); 594 } 595 596 @Override hashCode()597 public int hashCode() { 598 return Objects.hash( 599 mProtocolId, 600 Arrays.hashCode(mEncryptionAlgorithms), 601 Arrays.hashCode(mIntegrityAlgorithms), 602 Arrays.hashCode(mDhGroups)); 603 } 604 605 @Override equals(Object o)606 public boolean equals(Object o) { 607 if (!(o instanceof SaProposal)) { 608 return false; 609 } 610 611 SaProposal other = (SaProposal) o; 612 613 return mProtocolId == other.mProtocolId 614 && Arrays.equals(mEncryptionAlgorithms, other.mEncryptionAlgorithms) 615 && Arrays.equals(mIntegrityAlgorithms, other.mIntegrityAlgorithms) 616 && Arrays.equals(mDhGroups, other.mDhGroups); 617 } 618 619 /** @hide */ getKeySet(SparseArray array)620 protected static Set<Integer> getKeySet(SparseArray array) { 621 Set<Integer> result = new HashSet<>(); 622 for (int i = 0; i < array.size(); i++) { 623 result.add(array.keyAt(i)); 624 } 625 626 return result; 627 } 628 629 /** Returns supported DH groups for IKE and Child SA proposal negotiation. */ 630 @NonNull getSupportedDhGroups()631 public static Set<Integer> getSupportedDhGroups() { 632 final Set<Integer> supportedSet = new HashSet<>(); 633 for (int dh : getKeySet(SUPPORTED_DH_GROUP_TO_STR)) { 634 if (dh == DH_GROUP_CURVE_25519 && !SdkLevel.isAtLeastS()) { 635 continue; 636 } else { 637 supportedSet.add(dh); 638 } 639 } 640 return supportedSet; 641 } 642 643 /** 644 * Return the encryption algorithm as a String. 645 * 646 * @hide 647 */ getEncryptionAlgorithmString(int algorithm)648 public static String getEncryptionAlgorithmString(int algorithm) { 649 if (SUPPORTED_ENCRYPTION_ALGO_TO_STR.contains(algorithm)) { 650 return SUPPORTED_ENCRYPTION_ALGO_TO_STR.get(algorithm); 651 } 652 return "ENC_Unknown_" + algorithm; 653 } 654 655 /** 656 * Return the pseudorandom function as a String. 657 * 658 * @hide 659 */ getPseudorandomFunctionString(int algorithm)660 public static String getPseudorandomFunctionString(int algorithm) { 661 if (SUPPORTED_PRF_TO_STR.contains(algorithm)) { 662 return SUPPORTED_PRF_TO_STR.get(algorithm); 663 } 664 return "PRF_Unknown_" + algorithm; 665 } 666 667 /** 668 * Return the integrity algorithm as a String. 669 * 670 * @hide 671 */ getIntegrityAlgorithmString(int algorithm)672 public static String getIntegrityAlgorithmString(int algorithm) { 673 if (SUPPORTED_INTEGRITY_ALGO_TO_STR.contains(algorithm)) { 674 return SUPPORTED_INTEGRITY_ALGO_TO_STR.get(algorithm); 675 } 676 return "AUTH_Unknown_" + algorithm; 677 } 678 679 /** 680 * Return Diffie-Hellman Group as a String. 681 * 682 * @hide 683 */ getDhGroupString(int dhGroup)684 public static String getDhGroupString(int dhGroup) { 685 if (SUPPORTED_DH_GROUP_TO_STR.contains(dhGroup)) { 686 return SUPPORTED_DH_GROUP_TO_STR.get(dhGroup); 687 } 688 return "DH_Unknown_" + dhGroup; 689 } 690 } 691