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