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.IkeException.wrapAsIkeException; 21 22 import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY; 23 import static com.android.internal.net.ipsec.ike.message.IkePayload.PayloadType; 24 25 import android.annotation.IntDef; 26 import android.annotation.Nullable; 27 import android.net.ipsec.ike.exceptions.IkeException; 28 import android.net.ipsec.ike.exceptions.IkeProtocolException; 29 import android.net.ipsec.ike.exceptions.InvalidMessageIdException; 30 import android.net.ipsec.ike.exceptions.InvalidSyntaxException; 31 import android.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException; 32 import android.util.Pair; 33 import android.util.SparseArray; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.net.ipsec.ike.SaRecord.IkeSaRecord; 37 import com.android.internal.net.ipsec.ike.crypto.IkeCipher; 38 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; 39 import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NotifyType; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 import java.nio.BufferUnderflowException; 44 import java.nio.ByteBuffer; 45 import java.security.GeneralSecurityException; 46 import java.security.Provider; 47 import java.security.Security; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.HashSet; 51 import java.util.LinkedList; 52 import java.util.List; 53 import java.util.Set; 54 55 /** 56 * IkeMessage represents an IKE message. 57 * 58 * <p>It contains all attributes and provides methods for encoding, decoding, encrypting and 59 * decrypting. 60 * 61 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3">RFC 7296, Internet Key Exchange 62 * Protocol Version 2 (IKEv2)</a> 63 */ 64 public final class IkeMessage { 65 private static final String TAG = "IkeMessage"; 66 67 private static IIkeMessageHelper sIkeMessageHelper = new IkeMessageHelper(); 68 69 // Currently use HarmonyJSSE as TrustManager provider 70 static final Provider TRUST_MANAGER_PROVIDER = Security.getProvider("HarmonyJSSE"); 71 72 // Payload types in this set may be included multiple times within an IKE message. All other 73 // payload types can be included at most once. 74 private static final Set<Integer> REPEATABLE_PAYLOAD_TYPES = new HashSet<>(); 75 76 static { 77 REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_CERT); 78 REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_CERT_REQUEST); 79 REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_NOTIFY); 80 REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_DELETE); 81 REPEATABLE_PAYLOAD_TYPES.add(IkePayload.PAYLOAD_TYPE_VENDOR); 82 } 83 84 // IKE exchange subtypes describe the specific function of a IKE request/response exchange. It 85 // helps IKE and Child Session to process message according to the subtype specific rules. 86 @Retention(RetentionPolicy.SOURCE) 87 @IntDef({ 88 IKE_EXCHANGE_SUBTYPE_INVALID, 89 IKE_EXCHANGE_SUBTYPE_IKE_INIT, 90 IKE_EXCHANGE_SUBTYPE_IKE_AUTH, 91 IKE_EXCHANGE_SUBTYPE_DELETE_IKE, 92 IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, 93 IKE_EXCHANGE_SUBTYPE_REKEY_IKE, 94 IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, 95 IKE_EXCHANGE_SUBTYPE_GENERIC_INFO 96 }) 97 public @interface IkeExchangeSubType {} 98 99 public static final int IKE_EXCHANGE_SUBTYPE_INVALID = 0; 100 public static final int IKE_EXCHANGE_SUBTYPE_IKE_INIT = 1; 101 public static final int IKE_EXCHANGE_SUBTYPE_IKE_AUTH = 2; 102 public static final int IKE_EXCHANGE_SUBTYPE_CREATE_CHILD = 3; 103 public static final int IKE_EXCHANGE_SUBTYPE_DELETE_IKE = 4; 104 public static final int IKE_EXCHANGE_SUBTYPE_DELETE_CHILD = 5; 105 public static final int IKE_EXCHANGE_SUBTYPE_REKEY_IKE = 6; 106 public static final int IKE_EXCHANGE_SUBTYPE_REKEY_CHILD = 7; 107 public static final int IKE_EXCHANGE_SUBTYPE_GENERIC_INFO = 8; 108 109 private static final SparseArray<String> EXCHANGE_SUBTYPE_TO_STRING; 110 111 static { 112 EXCHANGE_SUBTYPE_TO_STRING = new SparseArray<>(); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_INVALID, "Invalid")113 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_INVALID, "Invalid"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_IKE_INIT, "IKE INIT")114 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_IKE_INIT, "IKE INIT"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_IKE_AUTH, "IKE AUTH")115 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_IKE_AUTH, "IKE AUTH"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_CREATE_CHILD, "Create Child")116 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_CREATE_CHILD, "Create Child"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_DELETE_IKE, "Delete IKE")117 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_DELETE_IKE, "Delete IKE"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, "Delete Child")118 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_DELETE_CHILD, "Delete Child"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_REKEY_IKE, "Rekey IKE")119 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_REKEY_IKE, "Rekey IKE"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, "Rekey Child")120 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, "Rekey Child"); EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_GENERIC_INFO, "Generic Info")121 EXCHANGE_SUBTYPE_TO_STRING.put(IKE_EXCHANGE_SUBTYPE_GENERIC_INFO, "Generic Info"); 122 } 123 124 public final IkeHeader ikeHeader; 125 public final List<IkePayload> ikePayloadList = new ArrayList<>(); 126 /** 127 * Construct an instance of IkeMessage. It is called by decode or for building outbound message. 128 * 129 * @param header the header of this IKE message 130 * @param payloadList the list of decoded IKE payloads in this IKE message 131 */ IkeMessage(IkeHeader header, List<IkePayload> payloadList)132 public IkeMessage(IkeHeader header, List<IkePayload> payloadList) { 133 ikeHeader = header; 134 ikePayloadList.addAll(payloadList); 135 } 136 137 /** 138 * Get security provider for X509TrustManager to do certificate validation. 139 * 140 * <p>Use JSSEProvdier as the default security provider. 141 * 142 * @return the provider for X509TrustManager 143 */ getTrustManagerProvider()144 public static Provider getTrustManagerProvider() { 145 return TRUST_MANAGER_PROVIDER; 146 } 147 148 /** 149 * Decode unencrypted IKE message body and create an instance of IkeMessage. 150 * 151 * <p>This method catches all RuntimeException during decoding incoming IKE packet. 152 * 153 * @param expectedMsgId the expected message ID to validate against. 154 * @param header the IKE header that is decoded but not validated. 155 * @param inputPacket the byte array contains the whole IKE message. 156 * @return the decoding result. 157 */ decode(int expectedMsgId, IkeHeader header, byte[] inputPacket)158 public static DecodeResult decode(int expectedMsgId, IkeHeader header, byte[] inputPacket) { 159 return sIkeMessageHelper.decode(expectedMsgId, header, inputPacket); 160 } 161 162 /** 163 * Decrypt and decode encrypted IKE message body and create an instance of IkeMessage. 164 * 165 * @param expectedMsgId the expected message ID to validate against. 166 * @param integrityMac the negotiated integrity algorithm. 167 * @param decryptCipher the negotiated encryption algorithm. 168 * @param ikeSaRecord ikeSaRecord where this packet is sent on. 169 * @param ikeHeader header of IKE packet. 170 * @param packet IKE packet as a byte array. 171 * @param collectedFragments previously received IKE fragments. 172 * @return the decoding result. 173 */ decode( int expectedMsgId, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, IkeSaRecord ikeSaRecord, IkeHeader ikeHeader, byte[] packet, DecodeResultPartial collectedFragments)174 public static DecodeResult decode( 175 int expectedMsgId, 176 @Nullable IkeMacIntegrity integrityMac, 177 IkeCipher decryptCipher, 178 IkeSaRecord ikeSaRecord, 179 IkeHeader ikeHeader, 180 byte[] packet, 181 DecodeResultPartial collectedFragments) { 182 return sIkeMessageHelper.decode( 183 expectedMsgId, 184 integrityMac, 185 decryptCipher, 186 ikeSaRecord, 187 ikeHeader, 188 packet, 189 collectedFragments); 190 } 191 decodePayloadList( @ayloadType int firstPayloadType, boolean isResp, byte[] unencryptedPayloads)192 private static List<IkePayload> decodePayloadList( 193 @PayloadType int firstPayloadType, boolean isResp, byte[] unencryptedPayloads) 194 throws IkeProtocolException { 195 ByteBuffer inputBuffer = ByteBuffer.wrap(unencryptedPayloads); 196 int currentPayloadType = firstPayloadType; 197 // For supported payload 198 List<IkePayload> supportedPayloadList = new LinkedList<>(); 199 // For unsupported critical payload 200 List<Integer> unsupportedCriticalPayloadList = new LinkedList<>(); 201 202 // For marking the existence of supported payloads in this message. 203 HashSet<Integer> supportedTypesFoundSet = new HashSet<>(); 204 205 StringBuilder logPayloadsSb = new StringBuilder(); 206 logPayloadsSb.append("Decoded payloads [ "); 207 208 while (currentPayloadType != IkePayload.PAYLOAD_TYPE_NO_NEXT) { 209 Pair<IkePayload, Integer> pair = 210 IkePayloadFactory.getIkePayload(currentPayloadType, isResp, inputBuffer); 211 IkePayload payload = pair.first; 212 logPayloadsSb.append(payload.getTypeString()).append(" "); 213 214 if (!(payload instanceof IkeUnsupportedPayload)) { 215 int type = payload.payloadType; 216 if (!supportedTypesFoundSet.add(type) && !REPEATABLE_PAYLOAD_TYPES.contains(type)) { 217 throw new InvalidSyntaxException( 218 "It is not allowed to have multiple payloads with payload type: " 219 + type); 220 } 221 222 supportedPayloadList.add(payload); 223 } else if (payload.isCritical) { 224 unsupportedCriticalPayloadList.add(payload.payloadType); 225 } 226 // Simply ignore unsupported uncritical payload. 227 228 currentPayloadType = pair.second; 229 } 230 231 logPayloadsSb.append("]"); 232 getIkeLog().d("IkeMessage", logPayloadsSb.toString()); 233 234 if (inputBuffer.remaining() > 0) { 235 throw new InvalidSyntaxException( 236 "Malformed IKE Payload: Unexpected bytes at the end of packet."); 237 } 238 239 if (unsupportedCriticalPayloadList.size() > 0) { 240 throw new UnsupportedCriticalPayloadException(unsupportedCriticalPayloadList); 241 } 242 243 // TODO: Verify that for all status notification payloads, only 244 // NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP and NOTIFY_TYPE_IPCOMP_SUPPORTED can be included 245 // multiple times in a request message. There is not a clear number restriction for 246 // error notification payloads. 247 248 return supportedPayloadList; 249 } 250 251 /** 252 * Encode unencrypted IKE message. 253 * 254 * @return encoded IKE message in byte array. 255 */ encode()256 public byte[] encode() { 257 return sIkeMessageHelper.encode(this); 258 } 259 260 /** 261 * Encrypt and encode packet. 262 * 263 * @param integrityMac the negotiated integrity algorithm. 264 * @param encryptCipher the negotiated encryption algortihm. 265 * @param ikeSaRecord the ikeSaRecord where this packet is sent on. 266 * @param supportFragment if IKE fragmentation is supported 267 * @param fragSize the maximum size of IKE fragment 268 * @return encoded IKE message in byte array. 269 */ encryptAndEncode( @ullable IkeMacIntegrity integrityMac, IkeCipher encryptCipher, IkeSaRecord ikeSaRecord, boolean supportFragment, int fragSize)270 public byte[][] encryptAndEncode( 271 @Nullable IkeMacIntegrity integrityMac, 272 IkeCipher encryptCipher, 273 IkeSaRecord ikeSaRecord, 274 boolean supportFragment, 275 int fragSize) { 276 return sIkeMessageHelper.encryptAndEncode( 277 integrityMac, encryptCipher, ikeSaRecord, this, supportFragment, fragSize); 278 } 279 280 /** 281 * Encode all payloads to a byte array. 282 * 283 * @return byte array contains all encoded payloads 284 */ encodePayloads()285 private byte[] encodePayloads() { 286 StringBuilder logPayloadsSb = new StringBuilder(); 287 logPayloadsSb.append("Generating payloads [ "); 288 289 int payloadLengthSum = 0; 290 for (IkePayload payload : ikePayloadList) { 291 payloadLengthSum += payload.getPayloadLength(); 292 logPayloadsSb.append(payload.getTypeString()).append(" "); 293 } 294 logPayloadsSb.append("]"); 295 getIkeLog().d("IkeMessage", logPayloadsSb.toString()); 296 297 if (ikePayloadList.isEmpty()) return new byte[0]; 298 299 ByteBuffer byteBuffer = ByteBuffer.allocate(payloadLengthSum); 300 for (int i = 0; i < ikePayloadList.size() - 1; i++) { 301 ikePayloadList 302 .get(i) 303 .encodeToByteBuffer(ikePayloadList.get(i + 1).payloadType, byteBuffer); 304 } 305 ikePayloadList 306 .get(ikePayloadList.size() - 1) 307 .encodeToByteBuffer(IkePayload.PAYLOAD_TYPE_NO_NEXT, byteBuffer); 308 309 return byteBuffer.array(); 310 } 311 312 /** Package */ 313 @VisibleForTesting attachEncodedHeader(byte[] encodedIkeBody)314 byte[] attachEncodedHeader(byte[] encodedIkeBody) { 315 ByteBuffer outputBuffer = 316 ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH + encodedIkeBody.length); 317 ikeHeader.encodeToByteBuffer(outputBuffer, encodedIkeBody.length); 318 outputBuffer.put(encodedIkeBody); 319 return outputBuffer.array(); 320 } 321 322 /** 323 * Obtain all payloads with input payload type. 324 * 325 * <p>This method can be only applied to the payload types that can be included multiple times 326 * within an IKE message. 327 * 328 * @param payloadType the payloadType to look for. 329 * @param payloadClass the class of the desired payloads. 330 * @return a list of IkePayloads with the payloadType. 331 */ getPayloadListForType( @kePayload.PayloadType int payloadType, Class<T> payloadClass)332 public <T extends IkePayload> List<T> getPayloadListForType( 333 @IkePayload.PayloadType int payloadType, Class<T> payloadClass) { 334 // STOPSHIP: b/130190639 Notify user the error and close IKE session. 335 if (!REPEATABLE_PAYLOAD_TYPES.contains(payloadType)) { 336 throw new IllegalArgumentException( 337 "Received unexpected payloadType: " 338 + payloadType 339 + " that can be included at most once within an IKE message."); 340 } 341 342 return IkePayload.getPayloadListForTypeInProvidedList( 343 payloadType, payloadClass, ikePayloadList); 344 } 345 346 /** 347 * Obtain the payload with the input payload type. 348 * 349 * <p>This method can be only applied to the payload type that can be included at most once 350 * within an IKE message. 351 * 352 * @param payloadType the payloadType to look for. 353 * @param payloadClass the class of the desired payload. 354 * @return the IkePayload with the payloadType. 355 */ getPayloadForType( @kePayload.PayloadType int payloadType, Class<T> payloadClass)356 public <T extends IkePayload> T getPayloadForType( 357 @IkePayload.PayloadType int payloadType, Class<T> payloadClass) { 358 // STOPSHIP: b/130190639 Notify user the error and close IKE session. 359 if (REPEATABLE_PAYLOAD_TYPES.contains(payloadType)) { 360 throw new IllegalArgumentException( 361 "Received unexpected payloadType: " 362 + payloadType 363 + " that may be included multiple times within an IKE message."); 364 } 365 366 return IkePayload.getPayloadForTypeInProvidedList( 367 payloadType, payloadClass, ikePayloadList); 368 } 369 370 /** Returns if a notification payload with a specified type is included in this message. */ hasNotifyPayload(@otifyType int notifyType)371 public boolean hasNotifyPayload(@NotifyType int notifyType) { 372 for (IkeNotifyPayload notify : 373 this.getPayloadListForType(PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class)) { 374 if (notify.notifyType == notifyType) { 375 return true; 376 } 377 } 378 379 return false; 380 } 381 382 /** 383 * Checks if this Request IkeMessage was a DPD message 384 * 385 * <p>An IKE message is a DPD request iff the message was encrypted (has a SK payload) and there 386 * were no payloads within the SK payload (or outside the SK payload). 387 */ isDpdRequest()388 public boolean isDpdRequest() { 389 return !ikeHeader.isResponseMsg 390 && ikeHeader.exchangeType == IkeHeader.EXCHANGE_TYPE_INFORMATIONAL 391 && ikePayloadList.isEmpty() 392 && ikeHeader.nextPayloadType == IkePayload.PAYLOAD_TYPE_SK; 393 } 394 395 /** Returns the exchange sub type as a String */ getIkeExchangeSubTypeString(@keExchangeSubType int exchangeSubtype)396 public static String getIkeExchangeSubTypeString(@IkeExchangeSubType int exchangeSubtype) { 397 if (!EXCHANGE_SUBTYPE_TO_STRING.contains(exchangeSubtype)) { 398 throw new IllegalStateException("Unrecognized exchangeSubtype " + exchangeSubtype); 399 } 400 return EXCHANGE_SUBTYPE_TO_STRING.get(exchangeSubtype); 401 } 402 403 /** 404 * Gets IKE exchange subtype of an inbound IKE request message. 405 * 406 * <p>It is not allowed to obtain exchange subtype from an inbound response message for two 407 * reasons. Firstly, the exchange subtype of a response message is the same with its 408 * corresponding request message. Secondly, trying to get the exchange subtype from a response 409 * message will easily fail when the response message contains only error notification payloads. 410 */ 411 @IkeExchangeSubType getIkeExchangeSubType()412 public int getIkeExchangeSubType() { 413 if (ikeHeader.isResponseMsg) { 414 throw new IllegalStateException( 415 "IKE Exchange subtype unsupported for response messages."); 416 } 417 418 switch (ikeHeader.exchangeType) { 419 case IkeHeader.EXCHANGE_TYPE_IKE_SA_INIT: 420 return IKE_EXCHANGE_SUBTYPE_IKE_INIT; 421 case IkeHeader.EXCHANGE_TYPE_IKE_AUTH: 422 return IKE_EXCHANGE_SUBTYPE_IKE_AUTH; 423 case IkeHeader.EXCHANGE_TYPE_CREATE_CHILD_SA: 424 // It is guaranteed in the decoding process that SA Payload has at least one SA 425 // Proposal. Since Rekey IKE and Create Child (both initial creation and rekey 426 // creation) will cause a collision, although the RFC 7296 does not prohibit one SA 427 // Payload to contain both IKE proposals and Child proposals, containing two types 428 // does not make sense. IKE library will reply according to the first SA Proposal 429 // type and ignore the other type. 430 IkeSaPayload saPayload = 431 getPayloadForType(IkePayload.PAYLOAD_TYPE_SA, IkeSaPayload.class); 432 if (saPayload == null) { 433 return IKE_EXCHANGE_SUBTYPE_INVALID; 434 } 435 436 // If the received message has both SA(IKE) Payload and Notify-Rekey Payload, IKE 437 // library will treat it as a Rekey IKE request and ignore the Notify-Rekey 438 // Payload to provide better interoperability. 439 if (saPayload.proposalList.get(0).protocolId == IkePayload.PROTOCOL_ID_IKE) { 440 return IKE_EXCHANGE_SUBTYPE_REKEY_IKE; 441 } 442 443 // If a Notify-Rekey Payload is found, this message is for rekeying a Child SA. 444 List<IkeNotifyPayload> notifyPayloads = 445 getPayloadListForType( 446 IkePayload.PAYLOAD_TYPE_NOTIFY, IkeNotifyPayload.class); 447 448 // It is checked during decoding that there is at most one Rekey notification 449 // payload. 450 for (IkeNotifyPayload notifyPayload : notifyPayloads) { 451 if (notifyPayload.notifyType == IkeNotifyPayload.NOTIFY_TYPE_REKEY_SA) { 452 return IKE_EXCHANGE_SUBTYPE_REKEY_CHILD; 453 } 454 } 455 456 return IKE_EXCHANGE_SUBTYPE_CREATE_CHILD; 457 case IkeHeader.EXCHANGE_TYPE_INFORMATIONAL: 458 List<IkeDeletePayload> deletePayloads = 459 getPayloadListForType( 460 IkePayload.PAYLOAD_TYPE_DELETE, IkeDeletePayload.class); 461 462 // If no Delete payload was found, this request is a generic informational request. 463 if (deletePayloads.isEmpty()) return IKE_EXCHANGE_SUBTYPE_GENERIC_INFO; 464 465 // IKEv2 protocol does not clearly disallow to have both a Delete IKE payload and a 466 // Delete Child payload in one IKE message. In this case, IKE library will only 467 // respond to the Delete IKE payload. 468 for (IkeDeletePayload deletePayload : deletePayloads) { 469 if (deletePayload.protocolId == IkePayload.PROTOCOL_ID_IKE) { 470 return IKE_EXCHANGE_SUBTYPE_DELETE_IKE; 471 } 472 } 473 return IKE_EXCHANGE_SUBTYPE_DELETE_CHILD; 474 default: 475 throw new IllegalStateException( 476 "Unrecognized exchange type in the validated IKE header: " 477 + ikeHeader.exchangeType); 478 } 479 } 480 481 /** 482 * IIkeMessageHelper provides interface for decoding, encoding and processing IKE packet. 483 * 484 * <p>IkeMessageHelper exists so that the interface is injectable for testing. 485 */ 486 @VisibleForTesting 487 public interface IIkeMessageHelper { 488 /** 489 * Encode IKE message. 490 * 491 * @param ikeMessage message need to be encoded. 492 * @return encoded IKE message in byte array. 493 */ encode(IkeMessage ikeMessage)494 byte[] encode(IkeMessage ikeMessage); 495 496 /** 497 * Encrypt and encode IKE message. 498 * 499 * @param integrityMac the negotiated integrity algorithm. 500 * @param encryptCipher the negotiated encryption algortihm. 501 * @param ikeSaRecord the ikeSaRecord where this packet is sent on. 502 * @param ikeMessage message need to be encoded. * @param supportFragment if IKE 503 * fragmentation is supported. 504 * @param fragSize the maximum size of IKE fragment. 505 * @return encoded IKE message in byte array. 506 */ encryptAndEncode( @ullable IkeMacIntegrity integrityMac, IkeCipher encryptCipher, IkeSaRecord ikeSaRecord, IkeMessage ikeMessage, boolean supportFragment, int fragSize)507 byte[][] encryptAndEncode( 508 @Nullable IkeMacIntegrity integrityMac, 509 IkeCipher encryptCipher, 510 IkeSaRecord ikeSaRecord, 511 IkeMessage ikeMessage, 512 boolean supportFragment, 513 int fragSize); 514 515 // TODO: Return DecodeResult when decoding unencrypted message 516 /** 517 * Decode unencrypted packet. 518 * 519 * @param expectedMsgId the expected message ID to validate against. 520 * @param ikeHeader header of IKE packet. 521 * @param packet IKE packet as a byte array. 522 * @return the decoding result. 523 */ decode(int expectedMsgId, IkeHeader ikeHeader, byte[] packet)524 DecodeResult decode(int expectedMsgId, IkeHeader ikeHeader, byte[] packet); 525 526 /** 527 * Decrypt and decode packet. 528 * 529 * @param expectedMsgId the expected message ID to validate against. 530 * @param integrityMac the negotiated integrity algorithm. 531 * @param decryptCipher the negotiated encryption algorithm. 532 * @param ikeSaRecord ikeSaRecord where this packet is sent on. 533 * @param ikeHeader header of IKE packet. 534 * @param packet IKE packet as a byte array. 535 * @param collectedFragments previously received IKE fragments. 536 * @return the decoding result. 537 */ decode( int expectedMsgId, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, IkeSaRecord ikeSaRecord, IkeHeader ikeHeader, byte[] packet, DecodeResultPartial collectedFragments)538 DecodeResult decode( 539 int expectedMsgId, 540 @Nullable IkeMacIntegrity integrityMac, 541 IkeCipher decryptCipher, 542 IkeSaRecord ikeSaRecord, 543 IkeHeader ikeHeader, 544 byte[] packet, 545 DecodeResultPartial collectedFragments); 546 } 547 548 /** IkeMessageHelper provides methods for decoding, encoding and processing IKE packet. */ 549 public static final class IkeMessageHelper implements IIkeMessageHelper { 550 @Override encode(IkeMessage ikeMessage)551 public byte[] encode(IkeMessage ikeMessage) { 552 getIkeLog().d("IkeMessage", "Generating " + ikeMessage.ikeHeader.getBasicInfoString()); 553 554 byte[] encodedIkeBody = ikeMessage.encodePayloads(); 555 byte[] packet = ikeMessage.attachEncodedHeader(encodedIkeBody); 556 getIkeLog().d("IkeMessage", "Build a complete IKE message: " + getIkeLog().pii(packet)); 557 return packet; 558 } 559 560 @Override encryptAndEncode( @ullable IkeMacIntegrity integrityMac, IkeCipher encryptCipher, IkeSaRecord ikeSaRecord, IkeMessage ikeMessage, boolean supportFragment, int fragSize)561 public byte[][] encryptAndEncode( 562 @Nullable IkeMacIntegrity integrityMac, 563 IkeCipher encryptCipher, 564 IkeSaRecord ikeSaRecord, 565 IkeMessage ikeMessage, 566 boolean supportFragment, 567 int fragSize) { 568 getIkeLog().d("IkeMessage", "Generating " + ikeMessage.ikeHeader.getBasicInfoString()); 569 570 return encryptAndEncode( 571 ikeMessage.ikeHeader, 572 ikeMessage.ikePayloadList.isEmpty() 573 ? IkePayload.PAYLOAD_TYPE_NO_NEXT 574 : ikeMessage.ikePayloadList.get(0).payloadType, 575 ikeMessage.encodePayloads(), 576 integrityMac, 577 encryptCipher, 578 ikeSaRecord.getOutboundIntegrityKey(), 579 ikeSaRecord.getOutboundEncryptionKey(), 580 supportFragment, 581 fragSize); 582 } 583 584 @VisibleForTesting encryptAndEncode( IkeHeader ikeHeader, @PayloadType int firstInnerPayload, byte[] unencryptedPayloads, @Nullable IkeMacIntegrity integrityMac, IkeCipher encryptCipher, byte[] integrityKey, byte[] encryptionKey, boolean supportFragment, int fragSize)585 byte[][] encryptAndEncode( 586 IkeHeader ikeHeader, 587 @PayloadType int firstInnerPayload, 588 byte[] unencryptedPayloads, 589 @Nullable IkeMacIntegrity integrityMac, 590 IkeCipher encryptCipher, 591 byte[] integrityKey, 592 byte[] encryptionKey, 593 boolean supportFragment, 594 int fragSize) { 595 596 IkeSkPayload skPayload = 597 new IkeSkPayload( 598 ikeHeader, 599 firstInnerPayload, 600 unencryptedPayloads, 601 integrityMac, 602 encryptCipher, 603 integrityKey, 604 encryptionKey); 605 int msgLen = IkeHeader.IKE_HEADER_LENGTH + skPayload.getPayloadLength(); 606 607 // Build complete IKE message 608 if (!supportFragment || msgLen <= fragSize) { 609 byte[][] packetList = new byte[1][]; 610 packetList[0] = encodeHeaderAndBody(ikeHeader, skPayload, firstInnerPayload); 611 612 getIkeLog() 613 .d( 614 "IkeMessage", 615 "Build a complete IKE message: " + getIkeLog().pii(packetList[0])); 616 return packetList; 617 } 618 619 // Build IKE fragments 620 int dataLenPerPacket = 621 fragSize 622 - IkeHeader.IKE_HEADER_LENGTH 623 - IkePayload.GENERIC_HEADER_LENGTH 624 - IkeSkfPayload.SKF_HEADER_LEN 625 - encryptCipher.getIvLen() 626 - (integrityMac == null ? 0 : integrityMac.getChecksumLen()) 627 - encryptCipher.getBlockSize(); 628 629 // Caller of this method MUST validate fragSize is valid. 630 if (dataLenPerPacket <= 0) { 631 throw new IllegalArgumentException( 632 "Max fragment size is too small for an IKE fragment."); 633 } 634 635 int totalFragments = 636 (unencryptedPayloads.length + dataLenPerPacket - 1) / dataLenPerPacket; 637 IkeHeader skfHeader = ikeHeader.makeSkfHeaderFromSkHeader(); 638 byte[][] packetList = new byte[totalFragments][]; 639 640 ByteBuffer unencryptedDataBuffer = ByteBuffer.wrap(unencryptedPayloads); 641 for (int i = 0; i < totalFragments; i++) { 642 byte[] unencryptedData = 643 new byte[Math.min(dataLenPerPacket, unencryptedDataBuffer.remaining())]; 644 unencryptedDataBuffer.get(unencryptedData); 645 646 int fragNum = i + 1; // 1-based 647 648 int fragFirstInnerPayload = 649 i == 0 ? firstInnerPayload : IkePayload.PAYLOAD_TYPE_NO_NEXT; 650 IkeSkfPayload skfPayload = 651 new IkeSkfPayload( 652 skfHeader, 653 fragFirstInnerPayload, 654 unencryptedData, 655 integrityMac, 656 encryptCipher, 657 integrityKey, 658 encryptionKey, 659 fragNum, 660 totalFragments); 661 662 packetList[i] = encodeHeaderAndBody(skfHeader, skfPayload, fragFirstInnerPayload); 663 getIkeLog() 664 .d( 665 "IkeMessage", 666 "Build an IKE fragment (" 667 + (i + 1) 668 + "/" 669 + totalFragments 670 + "): " 671 + getIkeLog().pii(packetList[i])); 672 } 673 674 return packetList; 675 } 676 encodeHeaderAndBody( IkeHeader ikeHeader, IkeSkPayload skPayload, @PayloadType int firstInnerPayload)677 private byte[] encodeHeaderAndBody( 678 IkeHeader ikeHeader, IkeSkPayload skPayload, @PayloadType int firstInnerPayload) { 679 ByteBuffer outputBuffer = 680 ByteBuffer.allocate(IkeHeader.IKE_HEADER_LENGTH + skPayload.getPayloadLength()); 681 ikeHeader.encodeToByteBuffer(outputBuffer, skPayload.getPayloadLength()); 682 skPayload.encodeToByteBuffer(firstInnerPayload, outputBuffer); 683 return outputBuffer.array(); 684 } 685 686 @Override decode(int expectedMsgId, IkeHeader header, byte[] inputPacket)687 public DecodeResult decode(int expectedMsgId, IkeHeader header, byte[] inputPacket) { 688 try { 689 if (header.messageId != expectedMsgId) { 690 throw new InvalidMessageIdException(header.messageId); 691 } 692 693 header.validateMajorVersion(); 694 header.validateInboundHeader(inputPacket.length); 695 696 byte[] unencryptedPayloads = 697 Arrays.copyOfRange( 698 inputPacket, IkeHeader.IKE_HEADER_LENGTH, inputPacket.length); 699 List<IkePayload> supportedPayloadList = 700 decodePayloadList( 701 header.nextPayloadType, header.isResponseMsg, unencryptedPayloads); 702 return new DecodeResultOk( 703 new IkeMessage(header, supportedPayloadList), inputPacket); 704 } catch (NegativeArraySizeException | BufferUnderflowException e) { 705 // Invalid length error when parsing payload bodies. 706 return new DecodeResultUnprotectedError( 707 new InvalidSyntaxException("Malformed IKE Payload")); 708 } catch (IkeProtocolException e) { 709 return new DecodeResultUnprotectedError(e); 710 } 711 } 712 713 @Override decode( int expectedMsgId, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, IkeSaRecord ikeSaRecord, IkeHeader ikeHeader, byte[] packet, DecodeResultPartial collectedFragments)714 public DecodeResult decode( 715 int expectedMsgId, 716 @Nullable IkeMacIntegrity integrityMac, 717 IkeCipher decryptCipher, 718 IkeSaRecord ikeSaRecord, 719 IkeHeader ikeHeader, 720 byte[] packet, 721 DecodeResultPartial collectedFragments) { 722 return decode( 723 expectedMsgId, 724 ikeHeader, 725 packet, 726 integrityMac, 727 decryptCipher, 728 ikeSaRecord.getInboundIntegrityKey(), 729 ikeSaRecord.getInboundDecryptionKey(), 730 collectedFragments); 731 } 732 decode( int expectedMsgId, IkeHeader header, byte[] inputPacket, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, byte[] integrityKey, byte[] decryptionKey, DecodeResultPartial collectedFragments)733 private DecodeResult decode( 734 int expectedMsgId, 735 IkeHeader header, 736 byte[] inputPacket, 737 @Nullable IkeMacIntegrity integrityMac, 738 IkeCipher decryptCipher, 739 byte[] integrityKey, 740 byte[] decryptionKey, 741 DecodeResultPartial collectedFragments) { 742 if (header.nextPayloadType != IkePayload.PAYLOAD_TYPE_SK 743 && header.nextPayloadType != IkePayload.PAYLOAD_TYPE_SKF) { 744 return new DecodeResultUnprotectedError( 745 new InvalidSyntaxException("Message contains unprotected payloads")); 746 } 747 748 // Decrypt message and do authentication 749 Pair<IkeSkPayload, Integer> pair; 750 try { 751 pair = 752 decryptAndAuthenticate( 753 expectedMsgId, 754 header, 755 inputPacket, 756 integrityMac, 757 decryptCipher, 758 integrityKey, 759 decryptionKey); 760 } catch (IkeException e) { 761 if (collectedFragments == null) { 762 return new DecodeResultUnprotectedError(e); 763 } else { 764 getIkeLog() 765 .i( 766 TAG, 767 "Message authentication or decryption failed on received" 768 + " message. Discard it ", 769 e); 770 return collectedFragments; 771 } 772 } 773 774 // Handle IKE fragment 775 boolean isFragment = (header.nextPayloadType == IkePayload.PAYLOAD_TYPE_SKF); 776 boolean fragReassemblyStarted = (collectedFragments != null); 777 778 if (isFragment) { 779 getIkeLog() 780 .d( 781 TAG, 782 "Received an IKE fragment (" 783 + ((IkeSkfPayload) pair.first).fragmentNum 784 + "/" 785 + ((IkeSkfPayload) pair.first).totalFragments 786 + ")"); 787 } 788 789 // IKE fragment reassembly has started but a complete message was received. 790 if (!isFragment && fragReassemblyStarted) { 791 getIkeLog() 792 .w( 793 TAG, 794 "Received a complete IKE message while doing IKE fragment" 795 + " reassembly. Discard the newly received message."); 796 return collectedFragments; 797 } 798 799 byte[] firstPacket = inputPacket; 800 byte[] decryptedBytes = pair.first.getUnencryptedData(); 801 int firstPayloadType = pair.second; 802 803 // Received an IKE fragment 804 if (isFragment) { 805 validateFragmentHeader(header, inputPacket.length, collectedFragments); 806 807 // Add the recently received fragment to the reassembly queue. 808 DecodeResultPartial DecodeResultPartial = 809 processIkeFragment( 810 header, 811 inputPacket, 812 (IkeSkfPayload) (pair.first), 813 pair.second, 814 collectedFragments); 815 816 if (!DecodeResultPartial.isAllFragmentsReceived()) return DecodeResultPartial; 817 818 firstPayloadType = DecodeResultPartial.firstPayloadType; 819 decryptedBytes = DecodeResultPartial.reassembleAllFrags(); 820 firstPacket = DecodeResultPartial.firstFragBytes; 821 } 822 823 // Received or has reassembled a complete IKE message. Check if there is protocol error. 824 try { 825 // TODO: Log IKE header information and payload types 826 827 List<IkePayload> supportedPayloadList = 828 decodePayloadList(firstPayloadType, header.isResponseMsg, decryptedBytes); 829 830 header.validateInboundHeader(inputPacket.length); 831 return new DecodeResultOk( 832 new IkeMessage(header, supportedPayloadList), firstPacket); 833 } catch (NegativeArraySizeException | BufferUnderflowException e) { 834 // Invalid length error when parsing payload bodies. 835 return new DecodeResultProtectedError( 836 new InvalidSyntaxException("Malformed IKE Payload", e), firstPacket); 837 } catch (IkeProtocolException e) { 838 return new DecodeResultProtectedError(e, firstPacket); 839 } 840 } 841 decryptAndAuthenticate( int expectedMsgId, IkeHeader header, byte[] inputPacket, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, byte[] integrityKey, byte[] decryptionKey)842 private Pair<IkeSkPayload, Integer> decryptAndAuthenticate( 843 int expectedMsgId, 844 IkeHeader header, 845 byte[] inputPacket, 846 @Nullable IkeMacIntegrity integrityMac, 847 IkeCipher decryptCipher, 848 byte[] integrityKey, 849 byte[] decryptionKey) 850 throws IkeException { 851 852 try { 853 if (header.messageId != expectedMsgId) { 854 throw new InvalidMessageIdException(header.messageId); 855 } 856 857 header.validateMajorVersion(); 858 859 boolean isSkf = header.nextPayloadType == IkePayload.PAYLOAD_TYPE_SKF; 860 return IkePayloadFactory.getIkeSkPayload( 861 isSkf, 862 inputPacket, 863 integrityMac, 864 decryptCipher, 865 integrityKey, 866 decryptionKey); 867 } catch (NegativeArraySizeException | BufferUnderflowException e) { 868 throw new InvalidSyntaxException("Malformed IKE Payload", e); 869 } catch (GeneralSecurityException e) { 870 throw wrapAsIkeException(e); 871 } 872 } 873 validateFragmentHeader( IkeHeader fragIkeHeader, int packetLen, DecodeResultPartial collectedFragments)874 private void validateFragmentHeader( 875 IkeHeader fragIkeHeader, int packetLen, DecodeResultPartial collectedFragments) { 876 try { 877 fragIkeHeader.validateInboundHeader(packetLen); 878 } catch (IkeProtocolException e) { 879 getIkeLog() 880 .e( 881 TAG, 882 "Received an IKE fragment with invalid header. Will be handled when" 883 + " reassembly is done.", 884 e); 885 } 886 887 if (collectedFragments == null) return; 888 if (fragIkeHeader.exchangeType != collectedFragments.ikeHeader.exchangeType) { 889 getIkeLog() 890 .e( 891 TAG, 892 "Received an IKE fragment with different exchange type from" 893 + " previously collected fragments. Ignore it."); 894 } 895 } 896 processIkeFragment( IkeHeader header, byte[] inputPacket, IkeSkfPayload skf, int nextPayloadType, @Nullable DecodeResultPartial collectedFragments)897 private DecodeResultPartial processIkeFragment( 898 IkeHeader header, 899 byte[] inputPacket, 900 IkeSkfPayload skf, 901 int nextPayloadType, 902 @Nullable DecodeResultPartial collectedFragments) { 903 if (collectedFragments == null) { 904 return new DecodeResultPartial( 905 header, inputPacket, skf, nextPayloadType, collectedFragments); 906 } 907 908 if (skf.totalFragments > collectedFragments.collectedFragsList.length) { 909 getIkeLog() 910 .i( 911 TAG, 912 "Received IKE fragment has larger total fragments number. Discard" 913 + " all previously collected fragments"); 914 return new DecodeResultPartial( 915 header, inputPacket, skf, nextPayloadType, null /*collectedFragments*/); 916 } 917 918 if (skf.totalFragments < collectedFragments.collectedFragsList.length) { 919 getIkeLog() 920 .i( 921 TAG, 922 "Received IKE fragment has smaller total fragments number. Discard" 923 + " it."); 924 return collectedFragments; 925 } 926 927 if (collectedFragments.collectedFragsList[skf.fragmentNum - 1] != null) { 928 getIkeLog().i(TAG, "Received IKE fragment is a replay."); 929 return collectedFragments; 930 } 931 932 return new DecodeResultPartial( 933 header, inputPacket, skf, nextPayloadType, collectedFragments); 934 } 935 } 936 937 /** Status to describe the result of decoding an inbound IKE message. */ 938 @Retention(RetentionPolicy.SOURCE) 939 @IntDef({ 940 DECODE_STATUS_OK, 941 DECODE_STATUS_PARTIAL, 942 DECODE_STATUS_PROTECTED_ERROR, 943 DECODE_STATUS_UNPROTECTED_ERROR, 944 }) 945 public @interface DecodeStatus {} 946 947 /** 948 * Represents a message that has been successfully (decrypted and) decoded or reassembled from 949 * IKE fragments 950 */ 951 public static final int DECODE_STATUS_OK = 0; 952 /** Represents that reassembly process of IKE fragments has started but has not finished */ 953 public static final int DECODE_STATUS_PARTIAL = 1; 954 /** Represents a crypto protected message with correct message ID but has parsing error. */ 955 public static final int DECODE_STATUS_PROTECTED_ERROR = 2; 956 /** 957 * Represents an unencrypted message with parsing error, an encrypted message with 958 * authentication or decryption error, or any message with wrong message ID. 959 */ 960 public static final int DECODE_STATUS_UNPROTECTED_ERROR = 3; 961 962 /** This class represents common decoding result of an IKE message. */ 963 public abstract static class DecodeResult { 964 public final int status; 965 966 /** Construct an instance of DecodeResult. */ DecodeResult(int status)967 protected DecodeResult(int status) { 968 this.status = status; 969 } 970 } 971 972 /** This class represents an IKE message has been successfully (decrypted and) decoded. */ 973 public static class DecodeResultOk extends DecodeResult { 974 public final IkeMessage ikeMessage; 975 public final byte[] firstPacket; 976 DecodeResultOk(IkeMessage ikeMessage, byte[] firstPacket)977 public DecodeResultOk(IkeMessage ikeMessage, byte[] firstPacket) { 978 super(DECODE_STATUS_OK); 979 this.ikeMessage = ikeMessage; 980 this.firstPacket = firstPacket; 981 } 982 } 983 984 /** 985 * This class represents IKE fragments are being reassembled to build a complete IKE message. 986 * 987 * <p>All IKE fragments should have the same IKE headers, except for the message length. This 988 * class only stores the IKE header of the first arrived IKE fragment to represent the IKE 989 * header of the complete IKE message. In this way we can verify all subsequent fragments' 990 * headers against it. 991 * 992 * <p>The first payload type is only stored in the first fragment, as indicated in RFC 7383. So 993 * this class only stores the next payload type field taken from the first fragment. 994 */ 995 public static class DecodeResultPartial extends DecodeResult { 996 public final int firstPayloadType; 997 public final byte[] firstFragBytes; 998 public final IkeHeader ikeHeader; 999 public final byte[][] collectedFragsList; 1000 1001 /** 1002 * Construct an instance of DecodeResultPartial with collected fragments and the newly 1003 * received fragment. 1004 * 1005 * <p>The newly received fragment has been validated against collected fragments during 1006 * decoding that all fragments have the same total fragments number and the newly received 1007 * fragment is not a replay. 1008 */ DecodeResultPartial( IkeHeader ikeHeader, byte[] inputPacket, IkeSkfPayload skfPayload, int nextPayloadType, @Nullable DecodeResultPartial collectedFragments)1009 public DecodeResultPartial( 1010 IkeHeader ikeHeader, 1011 byte[] inputPacket, 1012 IkeSkfPayload skfPayload, 1013 int nextPayloadType, 1014 @Nullable DecodeResultPartial collectedFragments) { 1015 super(DECODE_STATUS_PARTIAL); 1016 1017 boolean isFirstFragment = 1 == skfPayload.fragmentNum; 1018 if (collectedFragments == null) { 1019 // First arrived IKE fragment 1020 this.ikeHeader = ikeHeader; 1021 this.firstPayloadType = 1022 isFirstFragment ? nextPayloadType : IkePayload.PAYLOAD_TYPE_NO_NEXT; 1023 this.firstFragBytes = isFirstFragment ? inputPacket : null; 1024 this.collectedFragsList = new byte[skfPayload.totalFragments][]; 1025 } else { 1026 this.ikeHeader = collectedFragments.ikeHeader; 1027 this.firstPayloadType = 1028 isFirstFragment ? nextPayloadType : collectedFragments.firstPayloadType; 1029 this.firstFragBytes = 1030 isFirstFragment ? inputPacket : collectedFragments.firstFragBytes; 1031 this.collectedFragsList = collectedFragments.collectedFragsList; 1032 } 1033 1034 this.collectedFragsList[skfPayload.fragmentNum - 1] = skfPayload.getUnencryptedData(); 1035 } 1036 1037 /** Return if all IKE fragments have been collected */ isAllFragmentsReceived()1038 public boolean isAllFragmentsReceived() { 1039 for (byte[] frag : collectedFragsList) { 1040 if (frag == null) return false; 1041 } 1042 return true; 1043 } 1044 1045 /** Reassemble all IKE fragments and return the unencrypted message body in byte array. */ reassembleAllFrags()1046 public byte[] reassembleAllFrags() { 1047 if (!isAllFragmentsReceived()) { 1048 throw new IllegalStateException("Not all fragments have been received"); 1049 } 1050 1051 int len = 0; 1052 for (byte[] frag : collectedFragsList) { 1053 len += frag.length; 1054 } 1055 1056 ByteBuffer buffer = ByteBuffer.allocate(len); 1057 for (byte[] frag : collectedFragsList) { 1058 buffer.put(frag); 1059 } 1060 1061 return buffer.array(); 1062 } 1063 } 1064 1065 /** 1066 * This class represents common information of error cases in decrypting and decoding message. 1067 */ 1068 public abstract static class DecodeResultError extends DecodeResult { 1069 public final IkeException ikeException; 1070 DecodeResultError(int status, IkeException ikeException)1071 protected DecodeResultError(int status, IkeException ikeException) { 1072 super(status); 1073 this.ikeException = ikeException; 1074 } 1075 } 1076 /** 1077 * This class represents that decoding errors have been found after the IKE message is 1078 * authenticated and decrypted. 1079 */ 1080 public static class DecodeResultProtectedError extends DecodeResultError { 1081 public final byte[] firstPacket; 1082 DecodeResultProtectedError(IkeException ikeException, byte[] firstPacket)1083 public DecodeResultProtectedError(IkeException ikeException, byte[] firstPacket) { 1084 super(DECODE_STATUS_PROTECTED_ERROR, ikeException); 1085 this.firstPacket = firstPacket; 1086 } 1087 } 1088 /** This class represents errors have been found during message authentication or decryption. */ 1089 public static class DecodeResultUnprotectedError extends DecodeResultError { DecodeResultUnprotectedError(IkeException ikeException)1090 public DecodeResultUnprotectedError(IkeException ikeException) { 1091 super(DECODE_STATUS_UNPROTECTED_ERROR, ikeException); 1092 } 1093 } 1094 1095 /** 1096 * For setting mocked IIkeMessageHelper for testing 1097 * 1098 * @param helper the mocked IIkeMessageHelper 1099 */ setIkeMessageHelper(IIkeMessageHelper helper)1100 public static void setIkeMessageHelper(IIkeMessageHelper helper) { 1101 sIkeMessageHelper = helper; 1102 } 1103 } 1104