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