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