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 com.android.internal.net.ipsec.ike.message; 18 19 import static android.net.ipsec.ike.IkeManager.getIkeLog; 20 import static android.net.ipsec.ike.SaProposal.DhGroup; 21 import static android.net.ipsec.ike.SaProposal.EncryptionAlgorithm; 22 import static android.net.ipsec.ike.SaProposal.IntegrityAlgorithm; 23 import static android.net.ipsec.ike.SaProposal.PseudorandomFunction; 24 25 import android.annotation.IntDef; 26 import android.annotation.NonNull; 27 import android.net.IpSecManager.ResourceUnavailableException; 28 import android.net.IpSecManager.SecurityParameterIndex; 29 import android.net.IpSecManager.SpiUnavailableException; 30 import android.net.ipsec.ike.ChildSaProposal; 31 import android.net.ipsec.ike.IkeSaProposal; 32 import android.net.ipsec.ike.SaProposal; 33 import android.net.ipsec.ike.exceptions.IkeProtocolException; 34 import android.net.ipsec.ike.exceptions.InvalidSyntaxException; 35 import android.net.ipsec.ike.exceptions.NoValidProposalChosenException; 36 import android.os.PersistableBundle; 37 import android.util.ArraySet; 38 import android.util.Pair; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.net.ipsec.ike.utils.IkeSecurityParameterIndex; 42 import com.android.internal.net.ipsec.ike.utils.IkeSpiGenerator; 43 import com.android.internal.net.ipsec.ike.utils.IpSecSpiGenerator; 44 45 import java.io.IOException; 46 import java.lang.annotation.Retention; 47 import java.lang.annotation.RetentionPolicy; 48 import java.net.InetAddress; 49 import java.nio.ByteBuffer; 50 import java.util.ArrayList; 51 import java.util.LinkedList; 52 import java.util.List; 53 import java.util.Objects; 54 import java.util.Set; 55 56 /** 57 * IkeSaPayload represents a Security Association payload. It contains one or more {@link Proposal}. 58 * 59 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange 60 * Protocol Version 2 (IKEv2)</a> 61 */ 62 public final class IkeSaPayload extends IkePayload { 63 private static final String TAG = "IkeSaPayload"; 64 65 public final boolean isSaResponse; 66 public final List<Proposal> proposalList; 67 /** 68 * Construct an instance of IkeSaPayload for decoding an inbound packet. 69 * 70 * @param critical indicates if this payload is critical. Ignored in supported payload as 71 * instructed by the RFC 7296. 72 * @param isResp indicates if this payload is in a response message. 73 * @param payloadBody the encoded payload body in byte array. 74 */ IkeSaPayload(boolean critical, boolean isResp, byte[] payloadBody)75 IkeSaPayload(boolean critical, boolean isResp, byte[] payloadBody) throws IkeProtocolException { 76 super(IkePayload.PAYLOAD_TYPE_SA, critical); 77 78 ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); 79 proposalList = new LinkedList<>(); 80 while (inputBuffer.hasRemaining()) { 81 Proposal proposal = Proposal.readFrom(inputBuffer); 82 proposalList.add(proposal); 83 } 84 85 if (proposalList.isEmpty()) { 86 throw new InvalidSyntaxException("Found no SA Proposal in this SA Payload."); 87 } 88 89 // An SA response must have exactly one SA proposal. 90 if (isResp && proposalList.size() != 1) { 91 throw new InvalidSyntaxException( 92 "Expected only one negotiated proposal from SA response: " 93 + "Multiple negotiated proposals found."); 94 } 95 isSaResponse = isResp; 96 97 boolean firstIsIkeProposal = (proposalList.get(0).protocolId == PROTOCOL_ID_IKE); 98 for (int i = 1; i < proposalList.size(); i++) { 99 boolean isIkeProposal = (proposalList.get(i).protocolId == PROTOCOL_ID_IKE); 100 if (firstIsIkeProposal != isIkeProposal) { 101 getIkeLog() 102 .w(TAG, "Found both IKE proposals and Child proposals in this SA Payload."); 103 break; 104 } 105 } 106 107 getIkeLog().d(TAG, "Receive " + toString()); 108 } 109 110 /** Package private constructor for building a request for IKE SA initial creation or rekey */ 111 @VisibleForTesting IkeSaPayload( boolean isResp, byte spiSize, IkeSaProposal[] saProposals, IkeSpiGenerator ikeSpiGenerator, InetAddress localAddress)112 IkeSaPayload( 113 boolean isResp, 114 byte spiSize, 115 IkeSaProposal[] saProposals, 116 IkeSpiGenerator ikeSpiGenerator, 117 InetAddress localAddress) 118 throws IOException { 119 this(isResp, spiSize, localAddress); 120 121 if (saProposals.length < 1 || isResp && (saProposals.length > 1)) { 122 throw new IllegalArgumentException("Invalid SA payload."); 123 } 124 125 for (int i = 0; i < saProposals.length; i++) { 126 // Proposal number must start from 1. 127 proposalList.add( 128 IkeProposal.createIkeProposal( 129 (byte) (i + 1) /* number */, 130 spiSize, 131 saProposals[i], 132 ikeSpiGenerator, 133 localAddress)); 134 } 135 136 getIkeLog().d(TAG, "Generate " + toString()); 137 } 138 139 /** Package private constructor for building an response SA Payload for IKE SA rekeys. */ 140 @VisibleForTesting IkeSaPayload( boolean isResp, byte spiSize, byte proposalNumber, IkeSaProposal saProposal, IkeSpiGenerator ikeSpiGenerator, InetAddress localAddress)141 IkeSaPayload( 142 boolean isResp, 143 byte spiSize, 144 byte proposalNumber, 145 IkeSaProposal saProposal, 146 IkeSpiGenerator ikeSpiGenerator, 147 InetAddress localAddress) 148 throws IOException { 149 this(isResp, spiSize, localAddress); 150 151 proposalList.add( 152 IkeProposal.createIkeProposal( 153 proposalNumber /* number */, 154 spiSize, 155 saProposal, 156 ikeSpiGenerator, 157 localAddress)); 158 159 getIkeLog().d(TAG, "Generate " + toString()); 160 } 161 IkeSaPayload(boolean isResp, byte spiSize, InetAddress localAddress)162 private IkeSaPayload(boolean isResp, byte spiSize, InetAddress localAddress) 163 throws IOException { 164 super(IkePayload.PAYLOAD_TYPE_SA, false); 165 166 // TODO: Check that proposals.length <= 255 in IkeSessionParams and ChildSessionParams 167 isSaResponse = isResp; 168 169 // TODO: Allocate IKE SPI and pass to IkeProposal.createIkeProposal() 170 171 // ProposalList populated in other constructors 172 proposalList = new ArrayList<Proposal>(); 173 } 174 175 /** 176 * Package private constructor for building an outbound request SA Payload for Child SA 177 * negotiation. 178 */ 179 @VisibleForTesting IkeSaPayload( ChildSaProposal[] saProposals, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress)180 IkeSaPayload( 181 ChildSaProposal[] saProposals, 182 IpSecSpiGenerator ipSecSpiGenerator, 183 InetAddress localAddress) 184 throws SpiUnavailableException, ResourceUnavailableException { 185 this(false /* isResp */, ipSecSpiGenerator, localAddress); 186 187 if (saProposals.length < 1) { 188 throw new IllegalArgumentException("Invalid SA payload."); 189 } 190 191 // TODO: Check that saProposals.length <= 255 in IkeSessionParams and ChildSessionParams 192 193 for (int i = 0; i < saProposals.length; i++) { 194 // Proposal number must start from 1. 195 proposalList.add( 196 ChildProposal.createChildProposal( 197 (byte) (i + 1) /* number */, 198 saProposals[i], 199 ipSecSpiGenerator, 200 localAddress)); 201 } 202 203 getIkeLog().d(TAG, "Generate " + toString()); 204 } 205 206 /** 207 * Package private constructor for building an outbound response SA Payload for Child SA 208 * negotiation. 209 */ 210 @VisibleForTesting IkeSaPayload( byte proposalNumber, ChildSaProposal saProposal, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress)211 IkeSaPayload( 212 byte proposalNumber, 213 ChildSaProposal saProposal, 214 IpSecSpiGenerator ipSecSpiGenerator, 215 InetAddress localAddress) 216 throws SpiUnavailableException, ResourceUnavailableException { 217 this(true /* isResp */, ipSecSpiGenerator, localAddress); 218 219 proposalList.add( 220 ChildProposal.createChildProposal( 221 proposalNumber /* number */, saProposal, ipSecSpiGenerator, localAddress)); 222 223 getIkeLog().d(TAG, "Generate " + toString()); 224 } 225 226 /** Constructor for building an outbound SA Payload for Child SA negotiation. */ IkeSaPayload( boolean isResp, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress)227 private IkeSaPayload( 228 boolean isResp, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress) { 229 super(IkePayload.PAYLOAD_TYPE_SA, false); 230 231 isSaResponse = isResp; 232 233 // TODO: Allocate Child SPI and pass to ChildProposal.createChildProposal() 234 235 // ProposalList populated in other constructors 236 proposalList = new ArrayList<Proposal>(); 237 } 238 239 /** 240 * Construct an instance of IkeSaPayload for building an outbound IKE initial setup request. 241 * 242 * <p>According to RFC 7296, for an initial IKE SA negotiation, no SPI is included in SA 243 * Proposal. IKE library, as a client, only supports requesting this initial negotiation. 244 * 245 * @param saProposals the array of all SA Proposals. 246 */ createInitialIkeSaPayload(IkeSaProposal[] saProposals)247 public static IkeSaPayload createInitialIkeSaPayload(IkeSaProposal[] saProposals) 248 throws IOException { 249 return new IkeSaPayload( 250 false /* isResp */, 251 SPI_LEN_NOT_INCLUDED, 252 saProposals, 253 null /* ikeSpiGenerator unused */, 254 null /* localAddress unused */); 255 } 256 257 /** 258 * Construct an instance of IkeSaPayload for building an outbound request for Rekey IKE. 259 * 260 * @param saProposals the array of all IKE SA Proposals. 261 * @param ikeSpiGenerator the IKE SPI generator. 262 * @param localAddress the local address assigned on-device. 263 */ createRekeyIkeSaRequestPayload( IkeSaProposal[] saProposals, IkeSpiGenerator ikeSpiGenerator, InetAddress localAddress)264 public static IkeSaPayload createRekeyIkeSaRequestPayload( 265 IkeSaProposal[] saProposals, IkeSpiGenerator ikeSpiGenerator, InetAddress localAddress) 266 throws IOException { 267 return new IkeSaPayload( 268 false /* isResp */, SPI_LEN_IKE, saProposals, ikeSpiGenerator, localAddress); 269 } 270 271 /** 272 * Construct an instance of IkeSaPayload for building an outbound response for Rekey IKE. 273 * 274 * @param respProposalNumber the selected proposal's number. 275 * @param saProposal the expected selected IKE SA Proposal. 276 * @param ikeSpiGenerator the IKE SPI generator. 277 * @param localAddress the local address assigned on-device. 278 */ createRekeyIkeSaResponsePayload( byte respProposalNumber, IkeSaProposal saProposal, IkeSpiGenerator ikeSpiGenerator, InetAddress localAddress)279 public static IkeSaPayload createRekeyIkeSaResponsePayload( 280 byte respProposalNumber, 281 IkeSaProposal saProposal, 282 IkeSpiGenerator ikeSpiGenerator, 283 InetAddress localAddress) 284 throws IOException { 285 return new IkeSaPayload( 286 true /* isResp */, 287 SPI_LEN_IKE, 288 respProposalNumber, 289 saProposal, 290 ikeSpiGenerator, 291 localAddress); 292 } 293 294 /** 295 * Construct an instance of IkeSaPayload for building an outbound request for Child SA 296 * negotiation. 297 * 298 * @param saProposals the array of all Child SA Proposals. 299 * @param ipSecSpiGenerator the IPsec SPI generator. 300 * @param localAddress the local address assigned on-device. 301 * @throws ResourceUnavailableException if too many SPIs are currently allocated for this user. 302 */ createChildSaRequestPayload( ChildSaProposal[] saProposals, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress)303 public static IkeSaPayload createChildSaRequestPayload( 304 ChildSaProposal[] saProposals, 305 IpSecSpiGenerator ipSecSpiGenerator, 306 InetAddress localAddress) 307 throws SpiUnavailableException, ResourceUnavailableException { 308 309 return new IkeSaPayload(saProposals, ipSecSpiGenerator, localAddress); 310 } 311 312 /** 313 * Construct an instance of IkeSaPayload for building an outbound response for Child SA 314 * negotiation. 315 * 316 * @param respProposalNumber the selected proposal's number. 317 * @param saProposal the expected selected Child SA Proposal. 318 * @param ipSecSpiGenerator the IPsec SPI generator. 319 * @param localAddress the local address assigned on-device. 320 */ createChildSaResponsePayload( byte respProposalNumber, ChildSaProposal saProposal, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress)321 public static IkeSaPayload createChildSaResponsePayload( 322 byte respProposalNumber, 323 ChildSaProposal saProposal, 324 IpSecSpiGenerator ipSecSpiGenerator, 325 InetAddress localAddress) 326 throws SpiUnavailableException, ResourceUnavailableException { 327 return new IkeSaPayload(respProposalNumber, saProposal, ipSecSpiGenerator, localAddress); 328 } 329 330 /** 331 * Finds the proposal in this (request) payload that matches the response proposal. 332 * 333 * @param respProposal the Proposal to match against. 334 * @return the byte-value proposal number of the selected proposal 335 * @throws NoValidProposalChosenException if no matching proposal was found. 336 */ getNegotiatedProposalNumber(SaProposal respProposal)337 public byte getNegotiatedProposalNumber(SaProposal respProposal) 338 throws NoValidProposalChosenException { 339 for (int i = 0; i < proposalList.size(); i++) { 340 Proposal reqProposal = proposalList.get(i); 341 if (respProposal.isNegotiatedFrom(reqProposal.getSaProposal()) 342 && reqProposal.getSaProposal().getProtocolId() 343 == respProposal.getProtocolId()) { 344 return reqProposal.number; 345 } 346 } 347 throw new NoValidProposalChosenException("No remotely proposed protocol acceptable"); 348 } 349 350 /** 351 * Validate the IKE SA Payload pair (request/response) and return the IKE SA negotiation result. 352 * 353 * <p>Caller is able to extract the negotiated IKE SA Proposal from the response Proposal and 354 * the IKE SPI pair generated by both sides. 355 * 356 * <p>In a locally-initiated case all IKE SA proposals (from users in initial creation or from 357 * previously negotiated proposal in rekey creation) in the locally generated reqSaPayload have 358 * been validated during building and are unmodified. All Transform combinations in these SA 359 * proposals are valid for IKE SA negotiation. It means each IKE SA request proposal MUST have 360 * Encryption algorithms, DH group configurations and PRFs. Integrity algorithms can only be 361 * omitted when AEAD is used. 362 * 363 * <p>In a remotely-initiated case the locally generated respSaPayload has exactly one SA 364 * proposal. It is validated during building and are unmodified. This proposal has a valid 365 * Transform combination for an IKE SA and has at most one value for each Transform type. 366 * 367 * <p>The response IKE SA proposal is validated against one of the request IKE SA proposals. It 368 * is guaranteed that for each Transform type that the request proposal has provided options, 369 * the response proposal has exact one Transform value. 370 * 371 * @param reqSaPayload the request payload. 372 * @param respSaPayload the response payload. 373 * @param remoteAddress the address of the remote IKE peer. 374 * @return the Pair of selected IkeProposal in request and the IkeProposal in response. 375 * @throws NoValidProposalChosenException if the response SA Payload cannot be negotiated from 376 * the request SA Payload. 377 */ getVerifiedNegotiatedIkeProposalPair( IkeSaPayload reqSaPayload, IkeSaPayload respSaPayload, IkeSpiGenerator ikeSpiGenerator, InetAddress remoteAddress)378 public static Pair<IkeProposal, IkeProposal> getVerifiedNegotiatedIkeProposalPair( 379 IkeSaPayload reqSaPayload, 380 IkeSaPayload respSaPayload, 381 IkeSpiGenerator ikeSpiGenerator, 382 InetAddress remoteAddress) 383 throws NoValidProposalChosenException, IOException { 384 Pair<Proposal, Proposal> proposalPair = 385 getVerifiedNegotiatedProposalPair(reqSaPayload, respSaPayload); 386 IkeProposal reqProposal = (IkeProposal) proposalPair.first; 387 IkeProposal respProposal = (IkeProposal) proposalPair.second; 388 389 try { 390 // Allocate initiator's inbound SPI as needed for remotely initiated IKE SA creation 391 if (reqProposal.spiSize != SPI_NOT_INCLUDED 392 && reqProposal.getIkeSpiResource() == null) { 393 reqProposal.allocateResourceForRemoteIkeSpi(ikeSpiGenerator, remoteAddress); 394 } 395 // Allocate responder's inbound SPI as needed for locally initiated IKE SA creation 396 if (respProposal.spiSize != SPI_NOT_INCLUDED 397 && respProposal.getIkeSpiResource() == null) { 398 respProposal.allocateResourceForRemoteIkeSpi(ikeSpiGenerator, remoteAddress); 399 } 400 401 return new Pair(reqProposal, respProposal); 402 } catch (Exception e) { 403 reqProposal.releaseSpiResourceIfExists(); 404 respProposal.releaseSpiResourceIfExists(); 405 throw e; 406 } 407 } 408 409 /** 410 * Validate the SA Payload pair (request/response) and return the Child SA negotiation result. 411 * 412 * <p>Caller is able to extract the negotiated SA Proposal from the response Proposal and the 413 * IPsec SPI pair generated by both sides. 414 * 415 * <p>In a locally-initiated case all Child SA proposals (from users in initial creation or from 416 * previously negotiated proposal in rekey creation) in the locally generated reqSaPayload have 417 * been validated during building and are unmodified. All Transform combinations in these SA 418 * proposals are valid for Child SA negotiation. It means each request SA proposal MUST have 419 * Encryption algorithms and ESN configurations. 420 * 421 * <p>In a remotely-initiated case the locally generated respSapayload has exactly one SA 422 * proposal. It is validated during building and are unmodified. This proposal has a valid 423 * Transform combination for an Child SA and has at most one value for each Transform type. 424 * 425 * <p>The response Child SA proposal is validated against one of the request SA proposals. It is 426 * guaranteed that for each Transform type that the request proposal has provided options, the 427 * response proposal has exact one Transform value. 428 * 429 * @param reqSaPayload the request payload. 430 * @param respSaPayload the response payload. 431 * @param ipSecSpiGenerator the SPI generator to allocate SPI resource for the Proposal in this 432 * inbound SA Payload. 433 * @param remoteAddress the address of the remote IKE peer. 434 * @return the Pair of selected ChildProposal in the locally generated request and the 435 * ChildProposal in this response. 436 * @throws NoValidProposalChosenException if the response SA Payload cannot be negotiated from 437 * the request SA Payload. 438 * @throws ResourceUnavailableException if too many SPIs are currently allocated for this user. 439 * @throws SpiUnavailableException if the remotely generated SPI is in use. 440 */ getVerifiedNegotiatedChildProposalPair( IkeSaPayload reqSaPayload, IkeSaPayload respSaPayload, IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)441 public static Pair<ChildProposal, ChildProposal> getVerifiedNegotiatedChildProposalPair( 442 IkeSaPayload reqSaPayload, 443 IkeSaPayload respSaPayload, 444 IpSecSpiGenerator ipSecSpiGenerator, 445 InetAddress remoteAddress) 446 throws NoValidProposalChosenException, ResourceUnavailableException, 447 SpiUnavailableException { 448 Pair<Proposal, Proposal> proposalPair = 449 getVerifiedNegotiatedProposalPair(reqSaPayload, respSaPayload); 450 ChildProposal reqProposal = (ChildProposal) proposalPair.first; 451 ChildProposal respProposal = (ChildProposal) proposalPair.second; 452 453 try { 454 // Allocate initiator's inbound SPI as needed for remotely initiated Child SA creation 455 if (reqProposal.getChildSpiResource() == null) { 456 reqProposal.allocateResourceForRemoteChildSpi(ipSecSpiGenerator, remoteAddress); 457 } 458 // Allocate responder's inbound SPI as needed for locally initiated Child SA creation 459 if (respProposal.getChildSpiResource() == null) { 460 respProposal.allocateResourceForRemoteChildSpi(ipSecSpiGenerator, remoteAddress); 461 } 462 463 return new Pair(reqProposal, respProposal); 464 } catch (Exception e) { 465 reqProposal.releaseSpiResourceIfExists(); 466 respProposal.releaseSpiResourceIfExists(); 467 throw e; 468 } 469 } 470 getVerifiedNegotiatedProposalPair( IkeSaPayload reqSaPayload, IkeSaPayload respSaPayload)471 private static Pair<Proposal, Proposal> getVerifiedNegotiatedProposalPair( 472 IkeSaPayload reqSaPayload, IkeSaPayload respSaPayload) 473 throws NoValidProposalChosenException { 474 try { 475 // If negotiated proposal has an unrecognized Transform, throw an exception. 476 Proposal respProposal = respSaPayload.proposalList.get(0); 477 if (respProposal.hasUnrecognizedTransform) { 478 throw new NoValidProposalChosenException( 479 "Negotiated proposal has unrecognized Transform."); 480 } 481 482 // In SA request payload, the first proposal MUST be 1, and subsequent proposals MUST be 483 // one more than the previous proposal. In SA response payload, the negotiated proposal 484 // number MUST match the selected proposal number in SA request Payload. 485 int negotiatedProposalNum = respProposal.number; 486 List<Proposal> reqProposalList = reqSaPayload.proposalList; 487 if (negotiatedProposalNum < 1 || negotiatedProposalNum > reqProposalList.size()) { 488 throw new NoValidProposalChosenException( 489 "Negotiated proposal has invalid proposal number."); 490 } 491 492 Proposal reqProposal = reqProposalList.get(negotiatedProposalNum - 1); 493 if (!respProposal.isNegotiatedFrom(reqProposal)) { 494 throw new NoValidProposalChosenException("Invalid negotiated proposal."); 495 } 496 497 // In a locally-initiated creation, release locally generated SPIs in unselected request 498 // Proposals. In remotely-initiated SA creation, unused proposals do not have SPIs, and 499 // will silently succeed. 500 for (Proposal p : reqProposalList) { 501 if (reqProposal != p) p.releaseSpiResourceIfExists(); 502 } 503 504 return new Pair<Proposal, Proposal>(reqProposal, respProposal); 505 } catch (Exception e) { 506 // In a locally-initiated case, release all locally generated SPIs in the SA request 507 // payload. 508 for (Proposal p : reqSaPayload.proposalList) p.releaseSpiResourceIfExists(); 509 throw e; 510 } 511 } 512 513 @VisibleForTesting 514 interface TransformDecoder { decodeTransforms(int count, ByteBuffer inputBuffer)515 Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) throws IkeProtocolException; 516 } 517 518 /** 519 * Release IPsec SPI resources in the outbound Create Child request 520 * 521 * <p>This method is usually called when an IKE library fails to receive a Create Child response 522 * before it is terminated. It is also safe to call after the Create Child exchange has 523 * succeeded because the newly created IpSecTransform pair will hold the IPsec SPI resource. 524 */ releaseChildSpiResourcesIfExists()525 public void releaseChildSpiResourcesIfExists() { 526 for (Proposal proposal : proposalList) { 527 if (proposal instanceof ChildProposal) { 528 proposal.releaseSpiResourceIfExists(); 529 } 530 } 531 } 532 533 /** 534 * This class represents the common information of an IKE Proposal and a Child Proposal. 535 * 536 * <p>Proposal represents a set contains cryptographic algorithms and key generating materials. 537 * It contains multiple {@link Transform}. 538 * 539 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.1">RFC 7296, Internet Key 540 * Exchange Protocol Version 2 (IKEv2)</a> 541 * <p>Proposals with an unrecognized Protocol ID, containing an unrecognized Transform Type 542 * or lacking a necessary Transform Type shall be ignored when processing a received SA 543 * Payload. 544 */ 545 public abstract static class Proposal { 546 private static final byte LAST_PROPOSAL = 0; 547 private static final byte NOT_LAST_PROPOSAL = 2; 548 549 private static final int PROPOSAL_RESERVED_FIELD_LEN = 1; 550 private static final int PROPOSAL_HEADER_LEN = 8; 551 552 private static TransformDecoder sTransformDecoder = new TransformDecoderImpl(); 553 554 public final byte number; 555 /** All supported protocol will fall into {@link ProtocolId} */ 556 public final int protocolId; 557 558 public final byte spiSize; 559 public final long spi; 560 561 public final boolean hasUnrecognizedTransform; 562 563 @VisibleForTesting Proposal( byte number, int protocolId, byte spiSize, long spi, boolean hasUnrecognizedTransform)564 Proposal( 565 byte number, 566 int protocolId, 567 byte spiSize, 568 long spi, 569 boolean hasUnrecognizedTransform) { 570 this.number = number; 571 this.protocolId = protocolId; 572 this.spiSize = spiSize; 573 this.spi = spi; 574 this.hasUnrecognizedTransform = hasUnrecognizedTransform; 575 } 576 577 @VisibleForTesting readFrom(ByteBuffer inputBuffer)578 static Proposal readFrom(ByteBuffer inputBuffer) throws IkeProtocolException { 579 byte isLast = inputBuffer.get(); 580 if (isLast != LAST_PROPOSAL && isLast != NOT_LAST_PROPOSAL) { 581 throw new InvalidSyntaxException( 582 "Invalid value of Last Proposal Substructure: " + isLast); 583 } 584 // Skip RESERVED byte 585 inputBuffer.get(new byte[PROPOSAL_RESERVED_FIELD_LEN]); 586 587 int length = Short.toUnsignedInt(inputBuffer.getShort()); 588 byte number = inputBuffer.get(); 589 int protocolId = Byte.toUnsignedInt(inputBuffer.get()); 590 591 byte spiSize = inputBuffer.get(); 592 int transformCount = Byte.toUnsignedInt(inputBuffer.get()); 593 594 // TODO: Add check: spiSize must be 0 in initial IKE SA negotiation 595 // spiSize should be either 8 for IKE or 4 for IPsec. 596 long spi = SPI_NOT_INCLUDED; 597 switch (spiSize) { 598 case SPI_LEN_NOT_INCLUDED: 599 // No SPI attached for IKE initial exchange. 600 break; 601 case SPI_LEN_IPSEC: 602 spi = Integer.toUnsignedLong(inputBuffer.getInt()); 603 break; 604 case SPI_LEN_IKE: 605 spi = inputBuffer.getLong(); 606 break; 607 default: 608 throw new InvalidSyntaxException( 609 "Invalid value of spiSize in Proposal Substructure: " + spiSize); 610 } 611 612 Transform[] transformArray = 613 sTransformDecoder.decodeTransforms(transformCount, inputBuffer); 614 // TODO: Validate that sum of all Transforms' lengths plus Proposal header length equals 615 // to Proposal's length. 616 617 List<EncryptionTransform> encryptAlgoList = new LinkedList<>(); 618 List<PrfTransform> prfList = new LinkedList<>(); 619 List<IntegrityTransform> integAlgoList = new LinkedList<>(); 620 List<DhGroupTransform> dhGroupList = new LinkedList<>(); 621 List<EsnTransform> esnList = new LinkedList<>(); 622 623 boolean hasUnrecognizedTransform = false; 624 625 for (Transform transform : transformArray) { 626 switch (transform.type) { 627 case Transform.TRANSFORM_TYPE_ENCR: 628 encryptAlgoList.add((EncryptionTransform) transform); 629 break; 630 case Transform.TRANSFORM_TYPE_PRF: 631 prfList.add((PrfTransform) transform); 632 break; 633 case Transform.TRANSFORM_TYPE_INTEG: 634 integAlgoList.add((IntegrityTransform) transform); 635 break; 636 case Transform.TRANSFORM_TYPE_DH: 637 dhGroupList.add((DhGroupTransform) transform); 638 break; 639 case Transform.TRANSFORM_TYPE_ESN: 640 esnList.add((EsnTransform) transform); 641 break; 642 default: 643 hasUnrecognizedTransform = true; 644 } 645 } 646 647 if (protocolId == PROTOCOL_ID_IKE) { 648 IkeSaProposal saProposal = 649 new IkeSaProposal( 650 encryptAlgoList.toArray( 651 new EncryptionTransform[encryptAlgoList.size()]), 652 prfList.toArray(new PrfTransform[prfList.size()]), 653 integAlgoList.toArray(new IntegrityTransform[integAlgoList.size()]), 654 dhGroupList.toArray(new DhGroupTransform[dhGroupList.size()])); 655 return new IkeProposal(number, spiSize, spi, saProposal, hasUnrecognizedTransform); 656 } else { 657 ChildSaProposal saProposal = 658 new ChildSaProposal( 659 encryptAlgoList.toArray( 660 new EncryptionTransform[encryptAlgoList.size()]), 661 integAlgoList.toArray(new IntegrityTransform[integAlgoList.size()]), 662 dhGroupList.toArray(new DhGroupTransform[dhGroupList.size()]), 663 esnList.toArray(new EsnTransform[esnList.size()])); 664 return new ChildProposal(number, spi, saProposal, hasUnrecognizedTransform); 665 } 666 } 667 668 private static class TransformDecoderImpl implements TransformDecoder { 669 @Override decodeTransforms(int count, ByteBuffer inputBuffer)670 public Transform[] decodeTransforms(int count, ByteBuffer inputBuffer) 671 throws IkeProtocolException { 672 Transform[] transformArray = new Transform[count]; 673 for (int i = 0; i < count; i++) { 674 Transform transform = Transform.readFrom(inputBuffer); 675 transformArray[i] = transform; 676 } 677 return transformArray; 678 } 679 } 680 681 /** Package private method to set TransformDecoder for testing purposes */ 682 @VisibleForTesting setTransformDecoder(TransformDecoder decoder)683 static void setTransformDecoder(TransformDecoder decoder) { 684 sTransformDecoder = decoder; 685 } 686 687 /** Package private method to reset TransformDecoder */ 688 @VisibleForTesting resetTransformDecoder()689 static void resetTransformDecoder() { 690 sTransformDecoder = new TransformDecoderImpl(); 691 } 692 693 /** Package private */ isNegotiatedFrom(Proposal reqProposal)694 boolean isNegotiatedFrom(Proposal reqProposal) { 695 if (protocolId != reqProposal.protocolId || number != reqProposal.number) { 696 return false; 697 } 698 return getSaProposal().isNegotiatedFrom(reqProposal.getSaProposal()); 699 } 700 encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)701 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 702 Transform[] allTransforms = getSaProposal().getAllTransforms(); 703 byte isLastIndicator = isLast ? LAST_PROPOSAL : NOT_LAST_PROPOSAL; 704 705 byteBuffer 706 .put(isLastIndicator) 707 .put(new byte[PROPOSAL_RESERVED_FIELD_LEN]) 708 .putShort((short) getProposalLength()) 709 .put(number) 710 .put((byte) protocolId) 711 .put(spiSize) 712 .put((byte) allTransforms.length); 713 714 switch (spiSize) { 715 case SPI_LEN_NOT_INCLUDED: 716 // No SPI attached for IKE initial exchange. 717 break; 718 case SPI_LEN_IPSEC: 719 byteBuffer.putInt((int) spi); 720 break; 721 case SPI_LEN_IKE: 722 byteBuffer.putLong((long) spi); 723 break; 724 default: 725 throw new IllegalArgumentException( 726 "Invalid value of spiSize in Proposal Substructure: " + spiSize); 727 } 728 729 // Encode all Transform. 730 for (int i = 0; i < allTransforms.length; i++) { 731 // The last transform has the isLast flag set to true. 732 allTransforms[i].encodeToByteBuffer(i == allTransforms.length - 1, byteBuffer); 733 } 734 } 735 getProposalLength()736 protected int getProposalLength() { 737 int len = PROPOSAL_HEADER_LEN + spiSize; 738 739 Transform[] allTransforms = getSaProposal().getAllTransforms(); 740 for (Transform t : allTransforms) len += t.getTransformLength(); 741 return len; 742 } 743 744 @Override 745 @NonNull toString()746 public String toString() { 747 return "Proposal(" + number + ") " + getSaProposal().toString(); 748 } 749 750 /** Package private method for releasing SPI resource in this unselected Proposal. */ releaseSpiResourceIfExists()751 abstract void releaseSpiResourceIfExists(); 752 753 /** Package private method for getting SaProposal */ getSaProposal()754 abstract SaProposal getSaProposal(); 755 } 756 757 /** This class represents a Proposal for IKE SA negotiation. */ 758 public static final class IkeProposal extends Proposal { 759 private IkeSecurityParameterIndex mIkeSpiResource; 760 761 public final IkeSaProposal saProposal; 762 763 /** 764 * Construct IkeProposal from a decoded inbound message for IKE negotiation. 765 * 766 * <p>Package private 767 */ IkeProposal( byte number, byte spiSize, long spi, IkeSaProposal saProposal, boolean hasUnrecognizedTransform)768 IkeProposal( 769 byte number, 770 byte spiSize, 771 long spi, 772 IkeSaProposal saProposal, 773 boolean hasUnrecognizedTransform) { 774 super(number, PROTOCOL_ID_IKE, spiSize, spi, hasUnrecognizedTransform); 775 this.saProposal = saProposal; 776 } 777 778 /** Construct IkeProposal for an outbound message for IKE negotiation. */ IkeProposal( byte number, byte spiSize, IkeSecurityParameterIndex ikeSpiResource, IkeSaProposal saProposal)779 private IkeProposal( 780 byte number, 781 byte spiSize, 782 IkeSecurityParameterIndex ikeSpiResource, 783 IkeSaProposal saProposal) { 784 super( 785 number, 786 PROTOCOL_ID_IKE, 787 spiSize, 788 ikeSpiResource == null ? SPI_NOT_INCLUDED : ikeSpiResource.getSpi(), 789 false /* hasUnrecognizedTransform */); 790 mIkeSpiResource = ikeSpiResource; 791 this.saProposal = saProposal; 792 } 793 794 /** 795 * Construct IkeProposal for an outbound message for IKE negotiation. 796 * 797 * <p>Package private 798 */ 799 @VisibleForTesting createIkeProposal( byte number, byte spiSize, IkeSaProposal saProposal, IkeSpiGenerator ikeSpiGenerator, InetAddress localAddress)800 static IkeProposal createIkeProposal( 801 byte number, 802 byte spiSize, 803 IkeSaProposal saProposal, 804 IkeSpiGenerator ikeSpiGenerator, 805 InetAddress localAddress) 806 throws IOException { 807 // IKE_INIT uses SPI_LEN_NOT_INCLUDED, while rekeys use SPI_LEN_IKE 808 IkeSecurityParameterIndex spiResource = 809 (spiSize == SPI_LEN_NOT_INCLUDED 810 ? null 811 : ikeSpiGenerator.allocateSpi(localAddress)); 812 return new IkeProposal(number, spiSize, spiResource, saProposal); 813 } 814 815 /** Package private method for releasing SPI resource in this unselected Proposal. */ releaseSpiResourceIfExists()816 void releaseSpiResourceIfExists() { 817 // mIkeSpiResource is null when doing IKE initial exchanges. 818 if (mIkeSpiResource == null) return; 819 mIkeSpiResource.close(); 820 mIkeSpiResource = null; 821 } 822 823 /** 824 * Package private method for allocating SPI resource for a validated remotely generated IKE 825 * SA proposal. 826 */ allocateResourceForRemoteIkeSpi( IkeSpiGenerator ikeSpiGenerator, InetAddress remoteAddress)827 void allocateResourceForRemoteIkeSpi( 828 IkeSpiGenerator ikeSpiGenerator, InetAddress remoteAddress) throws IOException { 829 mIkeSpiResource = ikeSpiGenerator.allocateSpi(remoteAddress, spi); 830 } 831 832 @Override getSaProposal()833 public SaProposal getSaProposal() { 834 return saProposal; 835 } 836 837 /** 838 * Get the IKE SPI resource. 839 * 840 * @return the IKE SPI resource or null for IKE initial exchanges. 841 */ getIkeSpiResource()842 public IkeSecurityParameterIndex getIkeSpiResource() { 843 return mIkeSpiResource; 844 } 845 } 846 847 /** This class represents a Proposal for Child SA negotiation. */ 848 public static final class ChildProposal extends Proposal { 849 private SecurityParameterIndex mChildSpiResource; 850 851 public final ChildSaProposal saProposal; 852 853 /** 854 * Construct ChildProposal from a decoded inbound message for Child SA negotiation. 855 * 856 * <p>Package private 857 */ ChildProposal( byte number, long spi, ChildSaProposal saProposal, boolean hasUnrecognizedTransform)858 ChildProposal( 859 byte number, 860 long spi, 861 ChildSaProposal saProposal, 862 boolean hasUnrecognizedTransform) { 863 super( 864 number, 865 PROTOCOL_ID_ESP, 866 SPI_LEN_IPSEC, 867 spi, 868 hasUnrecognizedTransform); 869 this.saProposal = saProposal; 870 } 871 872 /** Construct ChildProposal for an outbound message for Child SA negotiation. */ ChildProposal( byte number, SecurityParameterIndex childSpiResource, ChildSaProposal saProposal)873 private ChildProposal( 874 byte number, SecurityParameterIndex childSpiResource, ChildSaProposal saProposal) { 875 super( 876 number, 877 PROTOCOL_ID_ESP, 878 SPI_LEN_IPSEC, 879 (long) childSpiResource.getSpi(), 880 false /* hasUnrecognizedTransform */); 881 mChildSpiResource = childSpiResource; 882 this.saProposal = saProposal; 883 } 884 885 /** 886 * Construct ChildProposal for an outbound message for Child SA negotiation. 887 * 888 * <p>Package private 889 */ 890 @VisibleForTesting createChildProposal( byte number, ChildSaProposal saProposal, IpSecSpiGenerator ipSecSpiGenerator, InetAddress localAddress)891 static ChildProposal createChildProposal( 892 byte number, 893 ChildSaProposal saProposal, 894 IpSecSpiGenerator ipSecSpiGenerator, 895 InetAddress localAddress) 896 throws SpiUnavailableException, ResourceUnavailableException { 897 return new ChildProposal( 898 number, ipSecSpiGenerator.allocateSpi(localAddress), saProposal); 899 } 900 901 /** Package private method for releasing SPI resource in this unselected Proposal. */ releaseSpiResourceIfExists()902 void releaseSpiResourceIfExists() { 903 if (mChildSpiResource == null) return; 904 905 mChildSpiResource.close(); 906 mChildSpiResource = null; 907 } 908 909 /** 910 * Package private method for allocating SPI resource for a validated remotely generated 911 * Child SA proposal. 912 */ allocateResourceForRemoteChildSpi( IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress)913 void allocateResourceForRemoteChildSpi( 914 IpSecSpiGenerator ipSecSpiGenerator, InetAddress remoteAddress) 915 throws ResourceUnavailableException, SpiUnavailableException { 916 mChildSpiResource = ipSecSpiGenerator.allocateSpi(remoteAddress, (int) spi); 917 } 918 919 @Override getSaProposal()920 public SaProposal getSaProposal() { 921 return saProposal; 922 } 923 924 /** 925 * Get the IPsec SPI resource. 926 * 927 * @return the IPsec SPI resource. 928 */ getChildSpiResource()929 public SecurityParameterIndex getChildSpiResource() { 930 return mChildSpiResource; 931 } 932 } 933 934 @VisibleForTesting 935 interface AttributeDecoder { decodeAttributes(int length, ByteBuffer inputBuffer)936 List<Attribute> decodeAttributes(int length, ByteBuffer inputBuffer) 937 throws IkeProtocolException; 938 } 939 940 /** 941 * Transform is an abstract base class that represents the common information for all Transform 942 * types. It may contain one or more {@link Attribute}. 943 * 944 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 945 * Exchange Protocol Version 2 (IKEv2)</a> 946 * <p>Transforms with unrecognized Transform ID or containing unrecognized Attribute Type 947 * shall be ignored when processing received SA payload. 948 */ 949 public abstract static class Transform { 950 951 @Retention(RetentionPolicy.SOURCE) 952 @IntDef({ 953 TRANSFORM_TYPE_ENCR, 954 TRANSFORM_TYPE_PRF, 955 TRANSFORM_TYPE_INTEG, 956 TRANSFORM_TYPE_DH, 957 TRANSFORM_TYPE_ESN 958 }) 959 public @interface TransformType {} 960 961 public static final int TRANSFORM_TYPE_ENCR = 1; 962 public static final int TRANSFORM_TYPE_PRF = 2; 963 public static final int TRANSFORM_TYPE_INTEG = 3; 964 public static final int TRANSFORM_TYPE_DH = 4; 965 public static final int TRANSFORM_TYPE_ESN = 5; 966 967 private static final byte LAST_TRANSFORM = 0; 968 private static final byte NOT_LAST_TRANSFORM = 3; 969 970 // Length of reserved field of a Transform. 971 private static final int TRANSFORM_RESERVED_FIELD_LEN = 1; 972 973 // Length of the Transform that with no Attribute. 974 protected static final int BASIC_TRANSFORM_LEN = 8; 975 976 // TODO: Add constants for supported algorithms 977 978 private static AttributeDecoder sAttributeDecoder = new AttributeDecoderImpl(); 979 980 // Only supported type falls into {@link TransformType} 981 public final int type; 982 public final int id; 983 public final boolean isSupported; 984 985 /** Construct an instance of Transform for building an outbound packet. */ Transform(int type, int id)986 protected Transform(int type, int id) { 987 this.type = type; 988 this.id = id; 989 if (!isSupportedTransformId(id)) { 990 throw new IllegalArgumentException( 991 "Unsupported " + getTransformTypeString() + " Algorithm ID: " + id); 992 } 993 this.isSupported = true; 994 } 995 996 /** Construct an instance of Transform for decoding an inbound packet. */ Transform(int type, int id, List<Attribute> attributeList)997 protected Transform(int type, int id, List<Attribute> attributeList) { 998 this.type = type; 999 this.id = id; 1000 this.isSupported = 1001 isSupportedTransformId(id) && !hasUnrecognizedAttribute(attributeList); 1002 } 1003 1004 @VisibleForTesting readFrom(ByteBuffer inputBuffer)1005 static Transform readFrom(ByteBuffer inputBuffer) throws IkeProtocolException { 1006 byte isLast = inputBuffer.get(); 1007 if (isLast != LAST_TRANSFORM && isLast != NOT_LAST_TRANSFORM) { 1008 throw new InvalidSyntaxException( 1009 "Invalid value of Last Transform Substructure: " + isLast); 1010 } 1011 1012 // Skip RESERVED byte 1013 inputBuffer.get(new byte[TRANSFORM_RESERVED_FIELD_LEN]); 1014 1015 int length = Short.toUnsignedInt(inputBuffer.getShort()); 1016 int type = Byte.toUnsignedInt(inputBuffer.get()); 1017 1018 // Skip RESERVED byte 1019 inputBuffer.get(new byte[TRANSFORM_RESERVED_FIELD_LEN]); 1020 1021 int id = Short.toUnsignedInt(inputBuffer.getShort()); 1022 1023 // Decode attributes 1024 List<Attribute> attributeList = sAttributeDecoder.decodeAttributes(length, inputBuffer); 1025 1026 validateAttributeUniqueness(attributeList); 1027 1028 switch (type) { 1029 case TRANSFORM_TYPE_ENCR: 1030 return new EncryptionTransform(id, attributeList); 1031 case TRANSFORM_TYPE_PRF: 1032 return new PrfTransform(id, attributeList); 1033 case TRANSFORM_TYPE_INTEG: 1034 return new IntegrityTransform(id, attributeList); 1035 case TRANSFORM_TYPE_DH: 1036 return new DhGroupTransform(id, attributeList); 1037 case TRANSFORM_TYPE_ESN: 1038 return new EsnTransform(id, attributeList); 1039 default: 1040 return new UnrecognizedTransform(type, id, attributeList); 1041 } 1042 } 1043 1044 private static class AttributeDecoderImpl implements AttributeDecoder { 1045 @Override decodeAttributes(int length, ByteBuffer inputBuffer)1046 public List<Attribute> decodeAttributes(int length, ByteBuffer inputBuffer) 1047 throws IkeProtocolException { 1048 List<Attribute> list = new LinkedList<>(); 1049 int parsedLength = BASIC_TRANSFORM_LEN; 1050 while (parsedLength < length) { 1051 Pair<Attribute, Integer> pair = Attribute.readFrom(inputBuffer); 1052 parsedLength += pair.second; // Increase parsedLength by the Atrribute length 1053 list.add(pair.first); 1054 } 1055 // TODO: Validate that parsedLength equals to length. 1056 return list; 1057 } 1058 } 1059 1060 /** Package private method to set AttributeDecoder for testing purpose */ 1061 @VisibleForTesting setAttributeDecoder(AttributeDecoder decoder)1062 static void setAttributeDecoder(AttributeDecoder decoder) { 1063 sAttributeDecoder = decoder; 1064 } 1065 1066 /** Package private method to reset AttributeDecoder */ 1067 @VisibleForTesting resetAttributeDecoder()1068 static void resetAttributeDecoder() { 1069 sAttributeDecoder = new AttributeDecoderImpl(); 1070 } 1071 1072 // Throw InvalidSyntaxException if there are multiple Attributes of the same type validateAttributeUniqueness(List<Attribute> attributeList)1073 private static void validateAttributeUniqueness(List<Attribute> attributeList) 1074 throws IkeProtocolException { 1075 Set<Integer> foundTypes = new ArraySet<>(); 1076 for (Attribute attr : attributeList) { 1077 if (!foundTypes.add(attr.type)) { 1078 throw new InvalidSyntaxException( 1079 "There are multiple Attributes of the same type. "); 1080 } 1081 } 1082 } 1083 1084 // Check if there is Attribute with unrecognized type. hasUnrecognizedAttribute(List<Attribute> attributeList)1085 protected abstract boolean hasUnrecognizedAttribute(List<Attribute> attributeList); 1086 1087 // Check if this Transform ID is supported. isSupportedTransformId(int id)1088 protected abstract boolean isSupportedTransformId(int id); 1089 1090 // Encode Transform to a ByteBuffer. encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1091 protected abstract void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer); 1092 1093 // Get entire Transform length. getTransformLength()1094 protected abstract int getTransformLength(); 1095 encodeBasicTransformToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1096 protected void encodeBasicTransformToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1097 byte isLastIndicator = isLast ? LAST_TRANSFORM : NOT_LAST_TRANSFORM; 1098 byteBuffer 1099 .put(isLastIndicator) 1100 .put(new byte[TRANSFORM_RESERVED_FIELD_LEN]) 1101 .putShort((short) getTransformLength()) 1102 .put((byte) type) 1103 .put(new byte[TRANSFORM_RESERVED_FIELD_LEN]) 1104 .putShort((short) id); 1105 } 1106 1107 /** 1108 * Get Tranform Type as a String. 1109 * 1110 * @return Tranform Type as a String. 1111 */ getTransformTypeString()1112 public abstract String getTransformTypeString(); 1113 1114 // TODO: Add abstract getTransformIdString() to return specific algorithm/dhGroup name 1115 } 1116 1117 /** 1118 * EncryptionTransform represents an encryption algorithm. It may contain an Atrribute 1119 * specifying the key length. 1120 * 1121 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 1122 * Exchange Protocol Version 2 (IKEv2)</a> 1123 */ 1124 public static final class EncryptionTransform extends Transform { 1125 public static final int KEY_LEN_UNSPECIFIED = 0; 1126 1127 private static final String ID_KEY = "id"; 1128 private static final String SPECIFIED_KEY_LEN_KEY = "mSpecifiedKeyLength"; 1129 1130 // When using encryption algorithm with variable-length keys, mSpecifiedKeyLength MUST be 1131 // set and a KeyLengthAttribute MUST be attached. Otherwise, mSpecifiedKeyLength MUST NOT be 1132 // set and KeyLengthAttribute MUST NOT be attached. 1133 private final int mSpecifiedKeyLength; 1134 1135 /** 1136 * Contruct an instance of EncryptionTransform with fixed key length for building an 1137 * outbound packet. 1138 * 1139 * @param id the IKE standard Transform ID. 1140 */ EncryptionTransform(@ncryptionAlgorithm int id)1141 public EncryptionTransform(@EncryptionAlgorithm int id) { 1142 this(id, KEY_LEN_UNSPECIFIED); 1143 } 1144 1145 /** 1146 * Contruct an instance of EncryptionTransform with variable key length for building an 1147 * outbound packet. 1148 * 1149 * @param id the IKE standard Transform ID. 1150 * @param specifiedKeyLength the specified key length of this encryption algorithm. 1151 */ EncryptionTransform(@ncryptionAlgorithm int id, int specifiedKeyLength)1152 public EncryptionTransform(@EncryptionAlgorithm int id, int specifiedKeyLength) { 1153 super(Transform.TRANSFORM_TYPE_ENCR, id); 1154 1155 mSpecifiedKeyLength = specifiedKeyLength; 1156 try { 1157 validateKeyLength(); 1158 } catch (InvalidSyntaxException e) { 1159 throw new IllegalArgumentException(e); 1160 } 1161 } 1162 1163 /** Constructs this object by deserializing a PersistableBundle */ fromPersistableBundle(@onNull PersistableBundle in)1164 public static EncryptionTransform fromPersistableBundle(@NonNull PersistableBundle in) { 1165 Objects.requireNonNull(in, "PersistableBundle is null"); 1166 return new EncryptionTransform(in.getInt(ID_KEY), in.getInt(SPECIFIED_KEY_LEN_KEY)); 1167 } 1168 1169 /** Serializes this object to a PersistableBundle */ toPersistableBundle()1170 public PersistableBundle toPersistableBundle() { 1171 final PersistableBundle result = new PersistableBundle(); 1172 result.putInt(ID_KEY, id); 1173 result.putInt(SPECIFIED_KEY_LEN_KEY, mSpecifiedKeyLength); 1174 1175 return result; 1176 } 1177 1178 /** 1179 * Contruct an instance of EncryptionTransform for decoding an inbound packet. 1180 * 1181 * @param id the IKE standard Transform ID. 1182 * @param attributeList the decoded list of Attribute. 1183 * @throws InvalidSyntaxException for syntax error. 1184 */ EncryptionTransform(int id, List<Attribute> attributeList)1185 protected EncryptionTransform(int id, List<Attribute> attributeList) 1186 throws InvalidSyntaxException { 1187 super(Transform.TRANSFORM_TYPE_ENCR, id, attributeList); 1188 if (!isSupported) { 1189 mSpecifiedKeyLength = KEY_LEN_UNSPECIFIED; 1190 } else { 1191 if (attributeList.size() == 0) { 1192 mSpecifiedKeyLength = KEY_LEN_UNSPECIFIED; 1193 } else { 1194 KeyLengthAttribute attr = getKeyLengthAttribute(attributeList); 1195 mSpecifiedKeyLength = attr.keyLength; 1196 } 1197 validateKeyLength(); 1198 } 1199 } 1200 1201 /** 1202 * Get the specified key length. 1203 * 1204 * @return the specified key length. 1205 */ getSpecifiedKeyLength()1206 public int getSpecifiedKeyLength() { 1207 return mSpecifiedKeyLength; 1208 } 1209 1210 @Override hashCode()1211 public int hashCode() { 1212 return Objects.hash(type, id, mSpecifiedKeyLength); 1213 } 1214 1215 @Override equals(Object o)1216 public boolean equals(Object o) { 1217 if (!(o instanceof EncryptionTransform)) return false; 1218 1219 EncryptionTransform other = (EncryptionTransform) o; 1220 return (type == other.type 1221 && id == other.id 1222 && mSpecifiedKeyLength == other.mSpecifiedKeyLength); 1223 } 1224 1225 @Override isSupportedTransformId(int id)1226 protected boolean isSupportedTransformId(int id) { 1227 return IkeSaProposal.getSupportedEncryptionAlgorithms().contains(id) 1228 || ChildSaProposal.getSupportedEncryptionAlgorithms().contains(id); 1229 } 1230 1231 @Override hasUnrecognizedAttribute(List<Attribute> attributeList)1232 protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) { 1233 for (Attribute attr : attributeList) { 1234 if (attr instanceof UnrecognizedAttribute) { 1235 return true; 1236 } 1237 } 1238 return false; 1239 } 1240 getKeyLengthAttribute(List<Attribute> attributeList)1241 private KeyLengthAttribute getKeyLengthAttribute(List<Attribute> attributeList) { 1242 for (Attribute attr : attributeList) { 1243 if (attr.type == Attribute.ATTRIBUTE_TYPE_KEY_LENGTH) { 1244 return (KeyLengthAttribute) attr; 1245 } 1246 } 1247 throw new IllegalArgumentException("Cannot find Attribute with Key Length type"); 1248 } 1249 validateKeyLength()1250 private void validateKeyLength() throws InvalidSyntaxException { 1251 switch (id) { 1252 case SaProposal.ENCRYPTION_ALGORITHM_3DES: 1253 /* fall through */ 1254 case SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305: 1255 if (mSpecifiedKeyLength != KEY_LEN_UNSPECIFIED) { 1256 throw new InvalidSyntaxException( 1257 "Must not set Key Length value for this " 1258 + getTransformTypeString() 1259 + " Algorithm ID: " 1260 + id); 1261 } 1262 return; 1263 case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC: 1264 /* fall through */ 1265 case SaProposal.ENCRYPTION_ALGORITHM_AES_CTR: 1266 /* fall through */ 1267 case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8: 1268 /* fall through */ 1269 case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12: 1270 /* fall through */ 1271 case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16: 1272 if (mSpecifiedKeyLength == KEY_LEN_UNSPECIFIED) { 1273 throw new InvalidSyntaxException( 1274 "Must set Key Length value for this " 1275 + getTransformTypeString() 1276 + " Algorithm ID: " 1277 + id); 1278 } 1279 if (mSpecifiedKeyLength != SaProposal.KEY_LEN_AES_128 1280 && mSpecifiedKeyLength != SaProposal.KEY_LEN_AES_192 1281 && mSpecifiedKeyLength != SaProposal.KEY_LEN_AES_256) { 1282 throw new InvalidSyntaxException( 1283 "Invalid key length for this " 1284 + getTransformTypeString() 1285 + " Algorithm ID: " 1286 + id); 1287 } 1288 return; 1289 default: 1290 // Won't hit here. 1291 throw new IllegalArgumentException( 1292 "Unrecognized Encryption Algorithm ID: " + id); 1293 } 1294 } 1295 1296 @Override encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1297 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1298 encodeBasicTransformToByteBuffer(isLast, byteBuffer); 1299 1300 if (mSpecifiedKeyLength != KEY_LEN_UNSPECIFIED) { 1301 new KeyLengthAttribute(mSpecifiedKeyLength).encodeToByteBuffer(byteBuffer); 1302 } 1303 } 1304 1305 @Override getTransformLength()1306 protected int getTransformLength() { 1307 int len = BASIC_TRANSFORM_LEN; 1308 1309 if (mSpecifiedKeyLength != KEY_LEN_UNSPECIFIED) { 1310 len += new KeyLengthAttribute(mSpecifiedKeyLength).getAttributeLength(); 1311 } 1312 1313 return len; 1314 } 1315 1316 @Override getTransformTypeString()1317 public String getTransformTypeString() { 1318 return "Encryption Algorithm"; 1319 } 1320 1321 @Override 1322 @NonNull toString()1323 public String toString() { 1324 if (isSupported) { 1325 return SaProposal.getEncryptionAlgorithmString(id) 1326 + "(" 1327 + getSpecifiedKeyLength() 1328 + ")"; 1329 } else { 1330 return "ENCR(" + id + ")"; 1331 } 1332 } 1333 } 1334 1335 /** 1336 * PrfTransform represents an pseudorandom function. 1337 * 1338 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 1339 * Exchange Protocol Version 2 (IKEv2)</a> 1340 */ 1341 public static final class PrfTransform extends Transform { 1342 /** 1343 * Contruct an instance of PrfTransform for building an outbound packet. 1344 * 1345 * @param id the IKE standard Transform ID. 1346 */ PrfTransform(@seudorandomFunction int id)1347 public PrfTransform(@PseudorandomFunction int id) { 1348 super(Transform.TRANSFORM_TYPE_PRF, id); 1349 } 1350 1351 /** 1352 * Contruct an instance of PrfTransform for decoding an inbound packet. 1353 * 1354 * @param id the IKE standard Transform ID. 1355 * @param attributeList the decoded list of Attribute. 1356 * @throws InvalidSyntaxException for syntax error. 1357 */ PrfTransform(int id, List<Attribute> attributeList)1358 protected PrfTransform(int id, List<Attribute> attributeList) 1359 throws InvalidSyntaxException { 1360 super(Transform.TRANSFORM_TYPE_PRF, id, attributeList); 1361 } 1362 1363 @Override hashCode()1364 public int hashCode() { 1365 return Objects.hash(type, id); 1366 } 1367 1368 @Override equals(Object o)1369 public boolean equals(Object o) { 1370 if (!(o instanceof PrfTransform)) return false; 1371 1372 PrfTransform other = (PrfTransform) o; 1373 return (type == other.type && id == other.id); 1374 } 1375 1376 @Override isSupportedTransformId(int id)1377 protected boolean isSupportedTransformId(int id) { 1378 return IkeSaProposal.getSupportedPseudorandomFunctions().contains(id); 1379 } 1380 1381 @Override hasUnrecognizedAttribute(List<Attribute> attributeList)1382 protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) { 1383 return !attributeList.isEmpty(); 1384 } 1385 1386 @Override encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1387 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1388 encodeBasicTransformToByteBuffer(isLast, byteBuffer); 1389 } 1390 1391 @Override getTransformLength()1392 protected int getTransformLength() { 1393 return BASIC_TRANSFORM_LEN; 1394 } 1395 1396 @Override getTransformTypeString()1397 public String getTransformTypeString() { 1398 return "Pseudorandom Function"; 1399 } 1400 1401 @Override 1402 @NonNull toString()1403 public String toString() { 1404 if (isSupported) { 1405 return SaProposal.getPseudorandomFunctionString(id); 1406 } else { 1407 return "PRF(" + id + ")"; 1408 } 1409 } 1410 } 1411 1412 /** 1413 * IntegrityTransform represents an integrity algorithm. 1414 * 1415 * <p>Proposing integrity algorithm for ESP SA is optional. Omitting the IntegrityTransform is 1416 * equivalent to including it with a value of NONE. When multiple integrity algorithms are 1417 * provided, choosing any of them are acceptable. 1418 * 1419 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 1420 * Exchange Protocol Version 2 (IKEv2)</a> 1421 */ 1422 public static final class IntegrityTransform extends Transform { 1423 /** 1424 * Contruct an instance of IntegrityTransform for building an outbound packet. 1425 * 1426 * @param id the IKE standard Transform ID. 1427 */ IntegrityTransform(@ntegrityAlgorithm int id)1428 public IntegrityTransform(@IntegrityAlgorithm int id) { 1429 super(Transform.TRANSFORM_TYPE_INTEG, id); 1430 } 1431 1432 /** 1433 * Contruct an instance of IntegrityTransform for decoding an inbound packet. 1434 * 1435 * @param id the IKE standard Transform ID. 1436 * @param attributeList the decoded list of Attribute. 1437 * @throws InvalidSyntaxException for syntax error. 1438 */ IntegrityTransform(int id, List<Attribute> attributeList)1439 protected IntegrityTransform(int id, List<Attribute> attributeList) 1440 throws InvalidSyntaxException { 1441 super(Transform.TRANSFORM_TYPE_INTEG, id, attributeList); 1442 } 1443 1444 @Override hashCode()1445 public int hashCode() { 1446 return Objects.hash(type, id); 1447 } 1448 1449 @Override equals(Object o)1450 public boolean equals(Object o) { 1451 if (!(o instanceof IntegrityTransform)) return false; 1452 1453 IntegrityTransform other = (IntegrityTransform) o; 1454 return (type == other.type && id == other.id); 1455 } 1456 1457 @Override isSupportedTransformId(int id)1458 protected boolean isSupportedTransformId(int id) { 1459 return IkeSaProposal.getSupportedIntegrityAlgorithms().contains(id) 1460 || ChildSaProposal.getSupportedIntegrityAlgorithms().contains(id); 1461 } 1462 1463 @Override hasUnrecognizedAttribute(List<Attribute> attributeList)1464 protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) { 1465 return !attributeList.isEmpty(); 1466 } 1467 1468 @Override encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1469 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1470 encodeBasicTransformToByteBuffer(isLast, byteBuffer); 1471 } 1472 1473 @Override getTransformLength()1474 protected int getTransformLength() { 1475 return BASIC_TRANSFORM_LEN; 1476 } 1477 1478 @Override getTransformTypeString()1479 public String getTransformTypeString() { 1480 return "Integrity Algorithm"; 1481 } 1482 1483 @Override 1484 @NonNull toString()1485 public String toString() { 1486 if (isSupported) { 1487 return SaProposal.getIntegrityAlgorithmString(id); 1488 } else { 1489 return "AUTH(" + id + ")"; 1490 } 1491 } 1492 } 1493 1494 /** 1495 * DhGroupTransform represents a Diffie-Hellman Group 1496 * 1497 * <p>Proposing DH group for non-first Child SA is optional. Omitting the DhGroupTransform is 1498 * equivalent to including it with a value of NONE. When multiple DH groups are provided, 1499 * choosing any of them are acceptable. 1500 * 1501 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 1502 * Exchange Protocol Version 2 (IKEv2)</a> 1503 */ 1504 public static final class DhGroupTransform extends Transform { 1505 /** 1506 * Contruct an instance of DhGroupTransform for building an outbound packet. 1507 * 1508 * @param id the IKE standard Transform ID. 1509 */ DhGroupTransform(@hGroup int id)1510 public DhGroupTransform(@DhGroup int id) { 1511 super(Transform.TRANSFORM_TYPE_DH, id); 1512 } 1513 1514 /** 1515 * Contruct an instance of DhGroupTransform for decoding an inbound packet. 1516 * 1517 * @param id the IKE standard Transform ID. 1518 * @param attributeList the decoded list of Attribute. 1519 * @throws InvalidSyntaxException for syntax error. 1520 */ DhGroupTransform(int id, List<Attribute> attributeList)1521 protected DhGroupTransform(int id, List<Attribute> attributeList) 1522 throws InvalidSyntaxException { 1523 super(Transform.TRANSFORM_TYPE_DH, id, attributeList); 1524 } 1525 1526 @Override hashCode()1527 public int hashCode() { 1528 return Objects.hash(type, id); 1529 } 1530 1531 @Override equals(Object o)1532 public boolean equals(Object o) { 1533 if (!(o instanceof DhGroupTransform)) return false; 1534 1535 DhGroupTransform other = (DhGroupTransform) o; 1536 return (type == other.type && id == other.id); 1537 } 1538 1539 @Override isSupportedTransformId(int id)1540 protected boolean isSupportedTransformId(int id) { 1541 return SaProposal.getSupportedDhGroups().contains(id); 1542 } 1543 1544 @Override hasUnrecognizedAttribute(List<Attribute> attributeList)1545 protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) { 1546 return !attributeList.isEmpty(); 1547 } 1548 1549 @Override encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1550 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1551 encodeBasicTransformToByteBuffer(isLast, byteBuffer); 1552 } 1553 1554 @Override getTransformLength()1555 protected int getTransformLength() { 1556 return BASIC_TRANSFORM_LEN; 1557 } 1558 1559 @Override getTransformTypeString()1560 public String getTransformTypeString() { 1561 return "Diffie-Hellman Group"; 1562 } 1563 1564 @Override 1565 @NonNull toString()1566 public String toString() { 1567 if (isSupported) { 1568 return SaProposal.getDhGroupString(id); 1569 } else { 1570 return "DH(" + id + ")"; 1571 } 1572 } 1573 } 1574 1575 /** 1576 * EsnTransform represents ESN policy that indicates if IPsec SA uses tranditional 32-bit 1577 * sequence numbers or extended(64-bit) sequence numbers. 1578 * 1579 * <p>Currently IKE library only supports negotiating IPsec SA that do not use extended sequence 1580 * numbers. The Transform ID of EsnTransform in outbound packets is not user configurable. 1581 * 1582 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key 1583 * Exchange Protocol Version 2 (IKEv2)</a> 1584 */ 1585 public static final class EsnTransform extends Transform { 1586 @Retention(RetentionPolicy.SOURCE) 1587 @IntDef({ESN_POLICY_NO_EXTENDED, ESN_POLICY_EXTENDED}) 1588 public @interface EsnPolicy {} 1589 1590 public static final int ESN_POLICY_NO_EXTENDED = 0; 1591 public static final int ESN_POLICY_EXTENDED = 1; 1592 1593 /** 1594 * Construct an instance of EsnTransform indicates using no-extended sequence numbers for 1595 * building an outbound packet. 1596 */ EsnTransform()1597 public EsnTransform() { 1598 super(Transform.TRANSFORM_TYPE_ESN, ESN_POLICY_NO_EXTENDED); 1599 } 1600 1601 /** 1602 * Contruct an instance of EsnTransform for decoding an inbound packet. 1603 * 1604 * @param id the IKE standard Transform ID. 1605 * @param attributeList the decoded list of Attribute. 1606 * @throws InvalidSyntaxException for syntax error. 1607 */ EsnTransform(int id, List<Attribute> attributeList)1608 protected EsnTransform(int id, List<Attribute> attributeList) 1609 throws InvalidSyntaxException { 1610 super(Transform.TRANSFORM_TYPE_ESN, id, attributeList); 1611 } 1612 1613 @Override hashCode()1614 public int hashCode() { 1615 return Objects.hash(type, id); 1616 } 1617 1618 @Override equals(Object o)1619 public boolean equals(Object o) { 1620 if (!(o instanceof EsnTransform)) return false; 1621 1622 EsnTransform other = (EsnTransform) o; 1623 return (type == other.type && id == other.id); 1624 } 1625 1626 @Override isSupportedTransformId(int id)1627 protected boolean isSupportedTransformId(int id) { 1628 return (id == ESN_POLICY_NO_EXTENDED || id == ESN_POLICY_EXTENDED); 1629 } 1630 1631 @Override hasUnrecognizedAttribute(List<Attribute> attributeList)1632 protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) { 1633 return !attributeList.isEmpty(); 1634 } 1635 1636 @Override encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1637 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1638 encodeBasicTransformToByteBuffer(isLast, byteBuffer); 1639 } 1640 1641 @Override getTransformLength()1642 protected int getTransformLength() { 1643 return BASIC_TRANSFORM_LEN; 1644 } 1645 1646 @Override getTransformTypeString()1647 public String getTransformTypeString() { 1648 return "Extended Sequence Numbers"; 1649 } 1650 1651 @Override 1652 @NonNull toString()1653 public String toString() { 1654 if (id == ESN_POLICY_NO_EXTENDED) { 1655 return "ESN_No_Extended"; 1656 } 1657 return "ESN_Extended"; 1658 } 1659 } 1660 1661 /** 1662 * UnrecognizedTransform represents a Transform with unrecognized Transform Type. 1663 * 1664 * <p>Proposals containing an UnrecognizedTransform should be ignored. 1665 */ 1666 protected static final class UnrecognizedTransform extends Transform { UnrecognizedTransform(int type, int id, List<Attribute> attributeList)1667 protected UnrecognizedTransform(int type, int id, List<Attribute> attributeList) { 1668 super(type, id, attributeList); 1669 } 1670 1671 @Override isSupportedTransformId(int id)1672 protected boolean isSupportedTransformId(int id) { 1673 return false; 1674 } 1675 1676 @Override hasUnrecognizedAttribute(List<Attribute> attributeList)1677 protected boolean hasUnrecognizedAttribute(List<Attribute> attributeList) { 1678 return !attributeList.isEmpty(); 1679 } 1680 1681 @Override encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer)1682 protected void encodeToByteBuffer(boolean isLast, ByteBuffer byteBuffer) { 1683 throw new UnsupportedOperationException( 1684 "It is not supported to encode a Transform with" + getTransformTypeString()); 1685 } 1686 1687 @Override getTransformLength()1688 protected int getTransformLength() { 1689 throw new UnsupportedOperationException( 1690 "It is not supported to get length of a Transform with " 1691 + getTransformTypeString()); 1692 } 1693 1694 /** 1695 * Return Tranform Type of Unrecognized Transform as a String. 1696 * 1697 * @return Tranform Type of Unrecognized Transform as a String. 1698 */ 1699 @Override getTransformTypeString()1700 public String getTransformTypeString() { 1701 return "Unrecognized Transform Type."; 1702 } 1703 } 1704 1705 /** 1706 * Attribute is an abtract base class for completing the specification of some {@link 1707 * Transform}. 1708 * 1709 * <p>Attribute is either in Type/Value format or Type/Length/Value format. For TV format, 1710 * Attribute length is always 4 bytes containing value for 2 bytes. While for TLV format, 1711 * Attribute length is determined by length field. 1712 * 1713 * <p>Currently only Key Length type is supported 1714 * 1715 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.5">RFC 7296, Internet Key 1716 * Exchange Protocol Version 2 (IKEv2)</a> 1717 */ 1718 public abstract static class Attribute { 1719 @Retention(RetentionPolicy.SOURCE) 1720 @IntDef({ATTRIBUTE_TYPE_KEY_LENGTH}) 1721 public @interface AttributeType {} 1722 1723 // Support only one Attribute type: Key Length. Should use Type/Value format. 1724 public static final int ATTRIBUTE_TYPE_KEY_LENGTH = 14; 1725 1726 // Mask to extract the left most AF bit to indicate Attribute Format. 1727 private static final int ATTRIBUTE_FORMAT_MASK = 0x8000; 1728 // Mask to extract 15 bits after the AF bit to indicate Attribute Type. 1729 private static final int ATTRIBUTE_TYPE_MASK = 0x7fff; 1730 1731 // Package private mask to indicate that Type-Value (TV) Attribute Format is used. 1732 static final int ATTRIBUTE_FORMAT_TV = ATTRIBUTE_FORMAT_MASK; 1733 1734 // Package private 1735 static final int TV_ATTRIBUTE_VALUE_LEN = 2; 1736 static final int TV_ATTRIBUTE_TOTAL_LEN = 4; 1737 static final int TVL_ATTRIBUTE_HEADER_LEN = TV_ATTRIBUTE_TOTAL_LEN; 1738 1739 // Only Key Length type belongs to AttributeType 1740 public final int type; 1741 1742 /** Construct an instance of an Attribute when decoding message. */ Attribute(int type)1743 protected Attribute(int type) { 1744 this.type = type; 1745 } 1746 1747 @VisibleForTesting readFrom(ByteBuffer inputBuffer)1748 static Pair<Attribute, Integer> readFrom(ByteBuffer inputBuffer) 1749 throws IkeProtocolException { 1750 short formatAndType = inputBuffer.getShort(); 1751 int format = formatAndType & ATTRIBUTE_FORMAT_MASK; 1752 int type = formatAndType & ATTRIBUTE_TYPE_MASK; 1753 1754 int length = 0; 1755 byte[] value = new byte[0]; 1756 if (format == ATTRIBUTE_FORMAT_TV) { 1757 // Type/Value format 1758 length = TV_ATTRIBUTE_TOTAL_LEN; 1759 value = new byte[TV_ATTRIBUTE_VALUE_LEN]; 1760 } else { 1761 // Type/Length/Value format 1762 if (type == ATTRIBUTE_TYPE_KEY_LENGTH) { 1763 throw new InvalidSyntaxException("Wrong format in Transform Attribute"); 1764 } 1765 1766 length = Short.toUnsignedInt(inputBuffer.getShort()); 1767 int valueLen = length - TVL_ATTRIBUTE_HEADER_LEN; 1768 // IkeMessage will catch exception if valueLen is negative. 1769 value = new byte[valueLen]; 1770 } 1771 1772 inputBuffer.get(value); 1773 1774 switch (type) { 1775 case ATTRIBUTE_TYPE_KEY_LENGTH: 1776 return new Pair(new KeyLengthAttribute(value), length); 1777 default: 1778 return new Pair(new UnrecognizedAttribute(type, value), length); 1779 } 1780 } 1781 1782 // Encode Attribute to a ByteBuffer. encodeToByteBuffer(ByteBuffer byteBuffer)1783 protected abstract void encodeToByteBuffer(ByteBuffer byteBuffer); 1784 1785 // Get entire Attribute length. getAttributeLength()1786 protected abstract int getAttributeLength(); 1787 } 1788 1789 /** KeyLengthAttribute represents a Key Length type Attribute */ 1790 public static final class KeyLengthAttribute extends Attribute { 1791 public final int keyLength; 1792 KeyLengthAttribute(byte[] value)1793 protected KeyLengthAttribute(byte[] value) { 1794 this(Short.toUnsignedInt(ByteBuffer.wrap(value).getShort())); 1795 } 1796 KeyLengthAttribute(int keyLength)1797 protected KeyLengthAttribute(int keyLength) { 1798 super(ATTRIBUTE_TYPE_KEY_LENGTH); 1799 this.keyLength = keyLength; 1800 } 1801 1802 @Override encodeToByteBuffer(ByteBuffer byteBuffer)1803 protected void encodeToByteBuffer(ByteBuffer byteBuffer) { 1804 byteBuffer 1805 .putShort((short) (ATTRIBUTE_FORMAT_TV | ATTRIBUTE_TYPE_KEY_LENGTH)) 1806 .putShort((short) keyLength); 1807 } 1808 1809 @Override getAttributeLength()1810 protected int getAttributeLength() { 1811 return TV_ATTRIBUTE_TOTAL_LEN; 1812 } 1813 } 1814 1815 /** 1816 * UnrecognizedAttribute represents a Attribute with unrecoginzed Attribute Type. 1817 * 1818 * <p>Transforms containing UnrecognizedAttribute should be ignored. 1819 */ 1820 protected static final class UnrecognizedAttribute extends Attribute { UnrecognizedAttribute(int type, byte[] value)1821 protected UnrecognizedAttribute(int type, byte[] value) { 1822 super(type); 1823 } 1824 1825 @Override encodeToByteBuffer(ByteBuffer byteBuffer)1826 protected void encodeToByteBuffer(ByteBuffer byteBuffer) { 1827 throw new UnsupportedOperationException( 1828 "It is not supported to encode an unrecognized Attribute."); 1829 } 1830 1831 @Override getAttributeLength()1832 protected int getAttributeLength() { 1833 throw new UnsupportedOperationException( 1834 "It is not supported to get length of an unrecognized Attribute."); 1835 } 1836 } 1837 1838 /** 1839 * Encode SA payload to ByteBUffer. 1840 * 1841 * @param nextPayload type of payload that follows this payload. 1842 * @param byteBuffer destination ByteBuffer that stores encoded payload. 1843 */ 1844 @Override encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)1845 protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { 1846 encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); 1847 1848 for (int i = 0; i < proposalList.size(); i++) { 1849 // The last proposal has the isLast flag set to true. 1850 proposalList.get(i).encodeToByteBuffer(i == proposalList.size() - 1, byteBuffer); 1851 } 1852 } 1853 1854 /** 1855 * Get entire payload length. 1856 * 1857 * @return entire payload length. 1858 */ 1859 @Override getPayloadLength()1860 protected int getPayloadLength() { 1861 int len = GENERIC_HEADER_LENGTH; 1862 1863 for (Proposal p : proposalList) len += p.getProposalLength(); 1864 1865 return len; 1866 } 1867 1868 /** 1869 * Return the payload type as a String. 1870 * 1871 * @return the payload type as a String. 1872 */ 1873 @Override getTypeString()1874 public String getTypeString() { 1875 return "SA"; 1876 } 1877 1878 @Override 1879 @NonNull toString()1880 public String toString() { 1881 StringBuilder sb = new StringBuilder(); 1882 if (isSaResponse) { 1883 sb.append("SA Response: "); 1884 } else { 1885 sb.append("SA Request: "); 1886 } 1887 1888 int len = proposalList.size(); 1889 for (int i = 0; i < len; i++) { 1890 sb.append(proposalList.get(i).toString()); 1891 if (i < len - 1) sb.append(", "); 1892 } 1893 1894 return sb.toString(); 1895 } 1896 } 1897