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.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; 21 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND; 22 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED; 23 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE; 24 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_IKE_SPI; 25 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD; 26 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MAJOR_VERSION; 27 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MESSAGE_ID; 28 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SELECTORS; 29 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX; 30 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS; 31 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN; 32 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED; 33 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE; 34 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE; 35 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD; 36 37 import android.annotation.IntDef; 38 import android.net.ipsec.ike.exceptions.IkeProtocolException; 39 import android.util.ArraySet; 40 import android.util.SparseArray; 41 42 import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException; 43 import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException; 44 import com.android.internal.net.ipsec.ike.exceptions.InvalidMajorVersionException; 45 import com.android.internal.net.ipsec.ike.exceptions.InvalidMessageIdException; 46 import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; 47 import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException; 48 import com.android.internal.net.ipsec.ike.exceptions.TemporaryFailureException; 49 import com.android.internal.net.ipsec.ike.exceptions.TsUnacceptableException; 50 import com.android.internal.net.ipsec.ike.exceptions.UnrecognizedIkeProtocolException; 51 import com.android.internal.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; 52 53 import java.lang.annotation.Retention; 54 import java.lang.annotation.RetentionPolicy; 55 import java.net.InetAddress; 56 import java.nio.ByteBuffer; 57 import java.security.MessageDigest; 58 import java.security.NoSuchAlgorithmException; 59 import java.security.ProviderException; 60 import java.util.Set; 61 62 /** 63 * IkeNotifyPayload represents a Notify Payload. 64 * 65 * <p>As instructed by RFC 7296, for IKE SA concerned Notify Payload, Protocol ID and SPI Size must 66 * be zero. Unrecognized notify message type must be ignored but should be logged. 67 * 68 * <p>Notification types that smaller or equal than ERROR_NOTIFY_TYPE_MAX are error types. The rest 69 * of them are status types. 70 * 71 * <p>Critical bit for this payload must be ignored in received packet and must not be set in 72 * outbound packet. 73 * 74 * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol 75 * Version 2 (IKEv2)</a> 76 */ 77 public final class IkeNotifyPayload extends IkeInformationalPayload { 78 private static final String TAG = IkeNotifyPayload.class.getSimpleName(); 79 80 @Retention(RetentionPolicy.SOURCE) 81 @IntDef({ 82 NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, 83 NOTIFY_TYPE_IPCOMP_SUPPORTED, 84 NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, 85 NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, 86 NOTIFY_TYPE_USE_TRANSPORT_MODE, 87 NOTIFY_TYPE_REKEY_SA, 88 NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, 89 NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION, 90 NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, 91 NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS 92 }) 93 public @interface NotifyType {} 94 95 /** 96 * Indicates that the responder has narrowed the proposed Traffic Selectors but other Traffic 97 * Selectors would also have been acceptable. Only allowed in the response for negotiating a 98 * Child SA. 99 */ 100 public static final int NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE = 16386; 101 /** 102 * Indicates a willingness by its sender to use IPComp on this Child SA. Only allowed in the 103 * request/response for negotiating a Child SA. 104 */ 105 public static final int NOTIFY_TYPE_IPCOMP_SUPPORTED = 16387; 106 /** 107 * Used for detecting if the IKE initiator is behind a NAT. Only allowed in the request/response 108 * of IKE_SA_INIT exchange. 109 */ 110 public static final int NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP = 16388; 111 /** 112 * Used for detecting if the IKE responder is behind a NAT. Only allowed in the request/response 113 * of IKE_SA_INIT exchange. 114 */ 115 public static final int NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP = 16389; 116 /** 117 * Indicates a willingness by its sender to use transport mode rather than tunnel mode on this 118 * Child SA. Only allowed in the request/response for negotiating a Child SA. 119 */ 120 public static final int NOTIFY_TYPE_USE_TRANSPORT_MODE = 16391; 121 /** 122 * Used for rekeying a Child SA or an IKE SA. Only allowed in the request/response of 123 * CREATE_CHILD_SA exchange. 124 */ 125 public static final int NOTIFY_TYPE_REKEY_SA = 16393; 126 /** 127 * Indicates that the sender will not accept packets that contain TFC padding over the Child SA 128 * being negotiated. Only allowed in the request/response for negotiating a Child SA. 129 */ 130 public static final int NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED = 16394; 131 132 /** Indicates that the sender prefers to use only eap based authentication */ 133 public static final int NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION = 16417; 134 135 /** Indicates that the sender supports IKE fragmentation. */ 136 public static final int NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED = 16430; 137 138 /** 139 * Indicates that the sender supports GENERIC_DIGITAL_SIGNATURE authentication payloads. 140 * 141 * <p>See RFC 7427 - Signature Authentication in the Internet Key Exchange Version 2 (IKEv2) for 142 * more details 143 */ 144 public static final int NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS = 16431; 145 146 private static final int NOTIFY_HEADER_LEN = 4; 147 private static final int ERROR_NOTIFY_TYPE_MAX = 16383; 148 149 private static final String NAT_DETECTION_DIGEST_ALGORITHM = "SHA-1"; 150 151 private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA; 152 private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA; 153 154 private static final SparseArray<String> NOTIFY_TYPE_TO_STRING; 155 156 static { 157 VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA = new ArraySet<>(); 158 VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_INVALID_SELECTORS); 159 VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_CHILD_SA_NOT_FOUND); 160 VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(NOTIFY_TYPE_REKEY_SA); 161 } 162 163 static { 164 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA = new ArraySet<>(); 165 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN); 166 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD); 167 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add( 168 IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED); 169 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS); 170 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add( 171 IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE); 172 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED); 173 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE); 174 175 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE); 176 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_IPCOMP_SUPPORTED); 177 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_USE_TRANSPORT_MODE); 178 VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED); 179 } 180 181 static { 182 NOTIFY_TYPE_TO_STRING = new SparseArray<>(); NOTIFY_TYPE_TO_STRING.put( ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload")183 NOTIFY_TYPE_TO_STRING.put( 184 ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_IKE_SPI, "Invalid IKE SPI")185 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_IKE_SPI, "Invalid IKE SPI"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MAJOR_VERSION, "Invalid major version")186 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MAJOR_VERSION, "Invalid major version"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax")187 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MESSAGE_ID, "Invalid message ID")188 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MESSAGE_ID, "Invalid message ID"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_PROPOSAL_CHOSEN, "No proposal chosen")189 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_PROPOSAL_CHOSEN, "No proposal chosen"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_KE_PAYLOAD, "Invalid KE payload")190 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_KE_PAYLOAD, "Invalid KE payload"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed")191 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_SINGLE_PAIR_REQUIRED, "Single pair required")192 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_SINGLE_PAIR_REQUIRED, "Single pair required"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_ADDITIONAL_SAS, "No additional SAs")193 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_ADDITIONAL_SAS, "No additional SAs"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, "Internal address failure")194 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, "Internal address failure"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_FAILED_CP_REQUIRED, "Failed CP required")195 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_FAILED_CP_REQUIRED, "Failed CP required"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable")196 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors")197 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TEMPORARY_FAILURE, "Temporary failure")198 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TEMPORARY_FAILURE, "Temporary failure"); NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_CHILD_SA_NOT_FOUND, "Child SA not found")199 NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_CHILD_SA_NOT_FOUND, "Child SA not found"); 200 NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible")201 NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible"); NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_IPCOMP_SUPPORTED, "IPCOMP supported")202 NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_IPCOMP_SUPPORTED, "IPCOMP supported"); NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, "NAT detection source IP")203 NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, "NAT detection source IP"); NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP")204 NOTIFY_TYPE_TO_STRING.put( 205 NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP"); NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode")206 NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode"); NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA")207 NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA"); NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TCP Padding not supported")208 NOTIFY_TYPE_TO_STRING.put( 209 NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TCP Padding not supported"); NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported")210 NOTIFY_TYPE_TO_STRING.put( 211 NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported"); NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported")212 NOTIFY_TYPE_TO_STRING.put( 213 NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported"); 214 } 215 216 public final int protocolId; 217 public final byte spiSize; 218 public final int notifyType; 219 public final int spi; 220 public final byte[] notifyData; 221 222 /** 223 * Construct an instance of IkeNotifyPayload in the context of IkePayloadFactory 224 * 225 * @param critical indicates if this payload is critical. Ignored in supported payload as 226 * instructed by the RFC 7296. 227 * @param payloadBody payload body in byte array 228 * @throws IkeProtocolException if there is any error 229 */ IkeNotifyPayload(boolean isCritical, byte[] payloadBody)230 IkeNotifyPayload(boolean isCritical, byte[] payloadBody) throws IkeProtocolException { 231 super(PAYLOAD_TYPE_NOTIFY, isCritical); 232 233 ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody); 234 235 protocolId = Byte.toUnsignedInt(inputBuffer.get()); 236 spiSize = inputBuffer.get(); 237 notifyType = Short.toUnsignedInt(inputBuffer.getShort()); 238 239 // Validate syntax of spiSize, protocolId and notifyType. 240 // Reference: <https://tools.ietf.org/html/rfc7296#page-100> 241 if (spiSize == SPI_LEN_IPSEC) { 242 // For message concerning existing Child SA 243 validateNotifyPayloadForExistingChildSa(); 244 spi = inputBuffer.getInt(); 245 246 } else if (spiSize == SPI_LEN_NOT_INCLUDED) { 247 // For message concerning IKE SA or for new Child SA that to be negotiated. 248 validateNotifyPayloadForIkeAndNewChild(); 249 spi = SPI_NOT_INCLUDED; 250 251 } else { 252 throw new InvalidSyntaxException("Invalid SPI Size: " + spiSize); 253 } 254 255 notifyData = new byte[payloadBody.length - NOTIFY_HEADER_LEN - spiSize]; 256 inputBuffer.get(notifyData); 257 } 258 validateNotifyPayloadForExistingChildSa()259 private void validateNotifyPayloadForExistingChildSa() throws InvalidSyntaxException { 260 if (protocolId != PROTOCOL_ID_AH && protocolId != PROTOCOL_ID_ESP) { 261 throw new InvalidSyntaxException( 262 "Expected Procotol ID AH(2) or ESP(3): Protocol ID is " + protocolId); 263 } 264 265 if (!VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.contains(notifyType)) { 266 throw new InvalidSyntaxException( 267 "Expected Notify Type for existing Child SA: Notify Type is " + notifyType); 268 } 269 } 270 validateNotifyPayloadForIkeAndNewChild()271 private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException { 272 if (protocolId != PROTOCOL_ID_UNSET) { 273 getIkeLog().w(TAG, "Expected Procotol ID unset: Protocol ID is " + protocolId); 274 } 275 276 if (notifyType == ERROR_TYPE_INVALID_SELECTORS 277 || notifyType == ERROR_TYPE_CHILD_SA_NOT_FOUND) { 278 throw new InvalidSyntaxException( 279 "Expected Notify Type concerning IKE SA or new Child SA under negotiation" 280 + ": Notify Type is " 281 + notifyType); 282 } 283 } 284 285 /** 286 * Generate NAT DETECTION notification data. 287 * 288 * <p>This method calculates NAT DETECTION notification data which is a SHA-1 digest of the IKE 289 * initiator's SPI, IKE responder's SPI, IP address and port. Source address and port should be 290 * used for generating NAT_DETECTION_SOURCE_IP data. Destination address and port should be used 291 * for generating NAT_DETECTION_DESTINATION_IP data. Here "source" and "destination" mean the 292 * direction of this IKE message. 293 * 294 * @param initiatorIkeSpi the SPI of IKE initiator 295 * @param responderIkeSpi the SPI of IKE responder 296 * @param ipAddress the IP address 297 * @param port the port 298 * @return the generated NAT DETECTION notification data as a byte array. 299 */ generateNatDetectionData( long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port)300 public static byte[] generateNatDetectionData( 301 long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port) { 302 byte[] rawIpAddr = ipAddress.getAddress(); 303 304 ByteBuffer byteBuffer = 305 ByteBuffer.allocate(2 * SPI_LEN_IKE + rawIpAddr.length + IP_PORT_LEN); 306 byteBuffer 307 .putLong(initiatorIkeSpi) 308 .putLong(responderIkeSpi) 309 .put(rawIpAddr) 310 .putShort((short) port); 311 312 try { 313 MessageDigest natDetectionDataDigest = 314 MessageDigest.getInstance(NAT_DETECTION_DIGEST_ALGORITHM); 315 return natDetectionDataDigest.digest(byteBuffer.array()); 316 } catch (NoSuchAlgorithmException e) { 317 throw new ProviderException( 318 "Failed to obtain algorithm :" + NAT_DETECTION_DIGEST_ALGORITHM, e); 319 } 320 } 321 322 /** 323 * Encode Notify payload to ByteBuffer. 324 * 325 * @param nextPayload type of payload that follows this payload. 326 * @param byteBuffer destination ByteBuffer that stores encoded payload. 327 */ 328 @Override encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)329 protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { 330 encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer); 331 byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) notifyType); 332 if (spiSize == SPI_LEN_IPSEC) { 333 byteBuffer.putInt(spi); 334 } 335 byteBuffer.put(notifyData); 336 } 337 338 /** 339 * Get entire payload length. 340 * 341 * @return entire payload length. 342 */ 343 @Override getPayloadLength()344 protected int getPayloadLength() { 345 return GENERIC_HEADER_LENGTH + NOTIFY_HEADER_LEN + spiSize + notifyData.length; 346 } 347 IkeNotifyPayload( @rotocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData)348 protected IkeNotifyPayload( 349 @ProtocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData) { 350 super(PAYLOAD_TYPE_NOTIFY, false); 351 this.protocolId = protocolId; 352 this.spiSize = spiSize; 353 this.spi = spi; 354 this.notifyType = notifyType; 355 this.notifyData = notifyData; 356 } 357 358 /** 359 * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be 360 * negotiated with associated notification data. 361 * 362 * @param notifyType the notify type concerning IKE SA 363 * @param notifytData status or error data transmitted. Values for this field are notify type 364 * specific. 365 */ IkeNotifyPayload(int notifyType, byte[] notifyData)366 public IkeNotifyPayload(int notifyType, byte[] notifyData) { 367 this(PROTOCOL_ID_UNSET, SPI_LEN_NOT_INCLUDED, SPI_NOT_INCLUDED, notifyType, notifyData); 368 try { 369 validateNotifyPayloadForIkeAndNewChild(); 370 } catch (InvalidSyntaxException e) { 371 throw new IllegalArgumentException(e); 372 } 373 } 374 375 /** 376 * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be 377 * negotiated without additional notification data. 378 * 379 * @param notifyType the notify type concerning IKE SA 380 */ IkeNotifyPayload(int notifyType)381 public IkeNotifyPayload(int notifyType) { 382 this(notifyType, new byte[0]); 383 } 384 385 /** 386 * Construct IkeNotifyPayload concerning existing Child SA 387 * 388 * @param notifyType the notify type concerning Child SA 389 * @param notifytData status or error data transmitted. Values for this field are notify type 390 * specific. 391 */ IkeNotifyPayload( @rotocolId int protocolId, int spi, int notifyType, byte[] notifyData)392 public IkeNotifyPayload( 393 @ProtocolId int protocolId, int spi, int notifyType, byte[] notifyData) { 394 this(protocolId, SPI_LEN_IPSEC, spi, notifyType, notifyData); 395 try { 396 validateNotifyPayloadForExistingChildSa(); 397 } catch (InvalidSyntaxException e) { 398 throw new IllegalArgumentException(e); 399 } 400 } 401 402 /** 403 * Indicates if this is an error notification payload. 404 * 405 * @return if this is an error notification payload. 406 */ isErrorNotify()407 public boolean isErrorNotify() { 408 return notifyType <= ERROR_NOTIFY_TYPE_MAX; 409 } 410 411 /** 412 * Indicates if this is an notification for a new Child SA negotiation. 413 * 414 * <p>This notification may provide additional configuration information for negotiating a new 415 * Child SA or is an error notification of the Child SA negotiation failure. 416 * 417 * @return if this is an notification for a new Child SA negotiation. 418 */ isNewChildSaNotify()419 public boolean isNewChildSaNotify() { 420 return VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.contains(notifyType); 421 } 422 423 /** 424 * Validate error data and build IkeProtocolException for this error notification. 425 * 426 * @return the IkeProtocolException that represents this error. 427 * @throws InvalidSyntaxException if error data has invalid size. 428 */ validateAndBuildIkeException()429 public IkeProtocolException validateAndBuildIkeException() throws InvalidSyntaxException { 430 if (!isErrorNotify()) { 431 throw new IllegalArgumentException( 432 "Do not support building IkeException for a non-error notificaton. Notify" 433 + " type: " 434 + notifyType); 435 } 436 437 try { 438 switch (notifyType) { 439 case ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD: 440 return new UnsupportedCriticalPayloadException(notifyData); 441 case ERROR_TYPE_INVALID_MAJOR_VERSION: 442 return new InvalidMajorVersionException(notifyData); 443 case ERROR_TYPE_INVALID_SYNTAX: 444 return new InvalidSyntaxException(notifyData); 445 case ERROR_TYPE_INVALID_MESSAGE_ID: 446 return new InvalidMessageIdException(notifyData); 447 case ERROR_TYPE_NO_PROPOSAL_CHOSEN: 448 return new NoValidProposalChosenException(notifyData); 449 case ERROR_TYPE_INVALID_KE_PAYLOAD: 450 return new InvalidKeException(notifyData); 451 case ERROR_TYPE_AUTHENTICATION_FAILED: 452 return new AuthenticationFailedException(notifyData); 453 case ERROR_TYPE_TS_UNACCEPTABLE: 454 return new TsUnacceptableException(notifyData); 455 case ERROR_TYPE_TEMPORARY_FAILURE: 456 return new TemporaryFailureException(notifyData); 457 default: 458 return new UnrecognizedIkeProtocolException(notifyType, notifyData); 459 } 460 } catch (IllegalArgumentException e) { 461 // Notification data length is invalid. 462 throw new InvalidSyntaxException(e); 463 } 464 } 465 466 /** 467 * Return the payload type as a String. 468 * 469 * @return the payload type as a String. 470 */ 471 @Override getTypeString()472 public String getTypeString() { 473 String notifyTypeString = NOTIFY_TYPE_TO_STRING.get(notifyType); 474 475 if (notifyTypeString == null) { 476 return "Notify(" + notifyType + ")"; 477 } 478 return "Notify(" + notifyTypeString + ")"; 479 } 480 } 481