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