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