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