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