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