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.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
21 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_CHILD_SA_NOT_FOUND;
22 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED;
23 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
24 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_IKE_SPI;
25 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD;
26 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MAJOR_VERSION;
27 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_MESSAGE_ID;
28 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SELECTORS;
29 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INVALID_SYNTAX;
30 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS;
31 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
32 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED;
33 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
34 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE;
35 import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD;
36 
37 import android.annotation.IntDef;
38 import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
39 import android.net.ipsec.ike.exceptions.ChildSaNotFoundException;
40 import android.net.ipsec.ike.exceptions.FailedCpRequiredException;
41 import android.net.ipsec.ike.exceptions.IkeProtocolException;
42 import android.net.ipsec.ike.exceptions.InternalAddressFailureException;
43 import android.net.ipsec.ike.exceptions.InvalidIkeSpiException;
44 import android.net.ipsec.ike.exceptions.InvalidKeException;
45 import android.net.ipsec.ike.exceptions.InvalidMajorVersionException;
46 import android.net.ipsec.ike.exceptions.InvalidMessageIdException;
47 import android.net.ipsec.ike.exceptions.InvalidSelectorsException;
48 import android.net.ipsec.ike.exceptions.InvalidSyntaxException;
49 import android.net.ipsec.ike.exceptions.NoAdditionalSasException;
50 import android.net.ipsec.ike.exceptions.NoValidProposalChosenException;
51 import android.net.ipsec.ike.exceptions.SinglePairRequiredException;
52 import android.net.ipsec.ike.exceptions.TemporaryFailureException;
53 import android.net.ipsec.ike.exceptions.TsUnacceptableException;
54 import android.net.ipsec.ike.exceptions.UnrecognizedIkeProtocolException;
55 import android.net.ipsec.ike.exceptions.UnsupportedCriticalPayloadException;
56 import android.util.ArraySet;
57 import android.util.SparseArray;
58 
59 import java.lang.annotation.Retention;
60 import java.lang.annotation.RetentionPolicy;
61 import java.net.InetAddress;
62 import java.nio.ByteBuffer;
63 import java.security.MessageDigest;
64 import java.security.NoSuchAlgorithmException;
65 import java.security.ProviderException;
66 import java.util.Set;
67 
68 /**
69  * IkeNotifyPayload represents a Notify Payload.
70  *
71  * <p>As instructed by RFC 7296, for IKE SA concerned Notify Payload, Protocol ID and SPI Size must
72  * be zero. Unrecognized notify message type must be ignored but should be logged.
73  *
74  * <p>Notification types that smaller or equal than ERROR_NOTIFY_TYPE_MAX are error types. The rest
75  * of them are status types.
76  *
77  * <p>Critical bit for this payload must be ignored in received packet and must not be set in
78  * outbound packet.
79  *
80  * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol
81  *     Version 2 (IKEv2)</a>
82  */
83 public final class IkeNotifyPayload extends IkeInformationalPayload {
84     private static final String TAG = IkeNotifyPayload.class.getSimpleName();
85 
86     @Retention(RetentionPolicy.SOURCE)
87     @IntDef({
88         NOTIFY_TYPE_INITIAL_CONTACT,
89         NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE,
90         NOTIFY_TYPE_IPCOMP_SUPPORTED,
91         NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP,
92         NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP,
93         NOTIFY_TYPE_USE_TRANSPORT_MODE,
94         NOTIFY_TYPE_REKEY_SA,
95         NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED,
96         NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION,
97         NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED,
98         NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS
99     })
100     public @interface NotifyType {}
101 
102     /**
103      * Indicates that the sender supports INITIAL CONTACT functionality for the IKE Session. Only
104      * allowed in the request of first IKE_AUTH exchange Note: Currently IKE only supports sending
105      * this payload & will ignore the received payload
106      */
107     public static final int NOTIFY_TYPE_INITIAL_CONTACT = 16384;
108     /**
109      * Indicates that the responder has narrowed the proposed Traffic Selectors but other Traffic
110      * Selectors would also have been acceptable. Only allowed in the response for negotiating a
111      * Child SA.
112      */
113     public static final int NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE = 16386;
114     /**
115      * Indicates a willingness by its sender to use IPComp on this Child SA. Only allowed in the
116      * request/response for negotiating a Child SA.
117      */
118     public static final int NOTIFY_TYPE_IPCOMP_SUPPORTED = 16387;
119     /**
120      * Used for detecting if the IKE initiator is behind a NAT. Only allowed in the request/response
121      * of IKE_SA_INIT exchange.
122      */
123     public static final int NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP = 16388;
124     /**
125      * Used for detecting if the IKE responder is behind a NAT. Only allowed in the request/response
126      * of IKE_SA_INIT exchange.
127      */
128     public static final int NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP = 16389;
129     /**
130      * Might be sent by the IKE responder in an IKE_SA_INIT response, to prevent DoS Attacks. If
131      * receiving it, IKE client MUST retry IKE_SA_INIT request with the same associated data.
132      */
133     public static final int NOTIFY_TYPE_COOKIE = 16390;
134     /**
135      * Indicates a willingness by its sender to use transport mode rather than tunnel mode on this
136      * Child SA. Only allowed in the request/response for negotiating a Child SA.
137      */
138     public static final int NOTIFY_TYPE_USE_TRANSPORT_MODE = 16391;
139     /**
140      * Used for rekeying a Child SA or an IKE SA. Only allowed in the request/response of
141      * CREATE_CHILD_SA exchange.
142      */
143     public static final int NOTIFY_TYPE_REKEY_SA = 16393;
144     /**
145      * Indicates that the sender will not accept packets that contain TFC padding over the Child SA
146      * being negotiated. Only allowed in the request/response for negotiating a Child SA.
147      */
148     public static final int NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED = 16394;
149     /**
150      * Indicates that the sender supports MOBIKE functionality for the IKE Session. Only allowed in
151      * the request/response of IKE_AUTH exchange.
152      */
153     public static final int NOTIFY_TYPE_MOBIKE_SUPPORTED = 16396;
154     /**
155      * Used for notifying the Responder that an address change has occurred during a MOBIKE-enabled
156      * IKE Session. Only allowed in Informational exchanges sent after the IKE_AUTH exchange has
157      * finished.
158      */
159     public static final int NOTIFY_TYPE_UPDATE_SA_ADDRESSES = 16400;
160 
161     /**
162      * Used in any INFORMATIONAL request for return routability check purposes when performing
163      * MOBIKE.
164      */
165     public static final int NOTIFY_TYPE_COOKIE2 = 16401;
166 
167     /** Indicates that the sender prefers to use only eap based authentication */
168     public static final int NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION = 16417;
169 
170     /** Indicates that the sender supports IKE fragmentation. */
171     public static final int NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED = 16430;
172 
173     /**
174      * Indicates that the sender supports GENERIC_DIGITAL_SIGNATURE authentication payloads.
175      *
176      * <p>See RFC 7427 - Signature Authentication in the Internet Key Exchange Version 2 (IKEv2) for
177      * more details
178      */
179     public static final int NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS = 16431;
180 
181     private static final int NOTIFY_HEADER_LEN = 4;
182     /** @hide */
183     public static final int ERROR_NOTIFY_TYPE_MAX = 16383;
184 
185     private static final String NAT_DETECTION_DIGEST_ALGORITHM = "SHA-1";
186 
187     private static final int COOKIE_DATA_LEN_MIN = 1;
188     private static final int COOKIE_DATA_LEN_MAX = 64;
189 
190     private static final int COOKIE2_DATA_LEN_MIN = 8;
191     private static final int COOKIE2_DATA_LEN_MAX = 64;
192 
193     private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA;
194     private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA;
195 
196     private static final SparseArray<String> NOTIFY_TYPE_TO_STRING;
197 
198     static {
199         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA = new ArraySet<>();
200         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_INVALID_SELECTORS);
201         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_CHILD_SA_NOT_FOUND);
202         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(NOTIFY_TYPE_REKEY_SA);
203     }
204 
205     static {
206         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA = new ArraySet<>();
207         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN);
208         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD);
209         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(
210                 IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED);
211         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS);
212         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(
213                 IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE);
214         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED);
215         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE);
216 
217         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE);
218         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_IPCOMP_SUPPORTED);
219         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_USE_TRANSPORT_MODE);
220         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED);
221     }
222 
223     static {
224         NOTIFY_TYPE_TO_STRING = new SparseArray<>();
NOTIFY_TYPE_TO_STRING.put( ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload")225         NOTIFY_TYPE_TO_STRING.put(
226                 ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_IKE_SPI, "Invalid IKE SPI")227         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_IKE_SPI, "Invalid IKE SPI");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MAJOR_VERSION, "Invalid major version")228         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MAJOR_VERSION, "Invalid major version");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax")229         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MESSAGE_ID, "Invalid message ID")230         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MESSAGE_ID, "Invalid message ID");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_PROPOSAL_CHOSEN, "No proposal chosen")231         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_PROPOSAL_CHOSEN, "No proposal chosen");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_KE_PAYLOAD, "Invalid KE payload")232         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_KE_PAYLOAD, "Invalid KE payload");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed")233         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_SINGLE_PAIR_REQUIRED, "Single pair required")234         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_SINGLE_PAIR_REQUIRED, "Single pair required");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_ADDITIONAL_SAS, "No additional SAs")235         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_NO_ADDITIONAL_SAS, "No additional SAs");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, "Internal address failure")236         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, "Internal address failure");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_FAILED_CP_REQUIRED, "Failed CP required")237         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_FAILED_CP_REQUIRED, "Failed CP required");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable")238         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors")239         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TEMPORARY_FAILURE, "Temporary failure")240         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TEMPORARY_FAILURE, "Temporary failure");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_CHILD_SA_NOT_FOUND, "Child SA not found")241         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_CHILD_SA_NOT_FOUND, "Child SA not found");
242 
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible")243         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_IPCOMP_SUPPORTED, "IPCOMP supported")244         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_IPCOMP_SUPPORTED, "IPCOMP supported");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, "NAT detection source IP")245         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP, "NAT detection source IP");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP")246         NOTIFY_TYPE_TO_STRING.put(
247                 NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE, "COOKIE")248         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE, "COOKIE");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode")249         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA")250         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TFC Padding not supported")251         NOTIFY_TYPE_TO_STRING.put(
252                 NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TFC Padding not supported");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_MOBIKE_SUPPORTED, "MOBIKE supported")253         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_MOBIKE_SUPPORTED, "MOBIKE supported");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_UPDATE_SA_ADDRESSES, "UPDATE_SA_ADDRESSES")254         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_UPDATE_SA_ADDRESSES, "UPDATE_SA_ADDRESSES");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE2, "COOKIE2")255         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE2, "COOKIE2");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION, "EAP-only Authentication")256         NOTIFY_TYPE_TO_STRING.put(
257                 NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION, "EAP-only Authentication");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported")258         NOTIFY_TYPE_TO_STRING.put(
259                 NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported")260         NOTIFY_TYPE_TO_STRING.put(
261                 NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported");
262     }
263 
264     public final int protocolId;
265     public final byte spiSize;
266     public final int notifyType;
267     public final int spi;
268     public final byte[] notifyData;
269 
270     /**
271      * Construct an instance of IkeNotifyPayload in the context of IkePayloadFactory
272      *
273      * @param critical indicates if this payload is critical. Ignored in supported payload as
274      *     instructed by the RFC 7296.
275      * @param payloadBody payload body in byte array
276      * @throws IkeProtocolException if there is any error
277      */
IkeNotifyPayload(boolean isCritical, byte[] payloadBody)278     IkeNotifyPayload(boolean isCritical, byte[] payloadBody) throws IkeProtocolException {
279         super(PAYLOAD_TYPE_NOTIFY, isCritical);
280 
281         ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
282 
283         protocolId = Byte.toUnsignedInt(inputBuffer.get());
284         spiSize = inputBuffer.get();
285         notifyType = Short.toUnsignedInt(inputBuffer.getShort());
286 
287         // Validate syntax of spiSize, protocolId and notifyType.
288         // Reference: <https://tools.ietf.org/html/rfc7296#page-100>
289         if (spiSize == SPI_LEN_IPSEC) {
290             // For message concerning existing Child SA
291             validateNotifyPayloadForExistingChildSa();
292             spi = inputBuffer.getInt();
293 
294         } else if (spiSize == SPI_LEN_NOT_INCLUDED) {
295             // For message concerning IKE SA or for new Child SA that to be negotiated.
296             validateNotifyPayloadForIkeAndNewChild();
297             spi = SPI_NOT_INCLUDED;
298 
299         } else {
300             throw new InvalidSyntaxException("Invalid SPI Size: " + spiSize);
301         }
302 
303         notifyData = new byte[payloadBody.length - NOTIFY_HEADER_LEN - spiSize];
304         inputBuffer.get(notifyData);
305     }
306 
validateNotifyPayloadForExistingChildSa()307     private void validateNotifyPayloadForExistingChildSa() throws InvalidSyntaxException {
308         if (protocolId != PROTOCOL_ID_AH && protocolId != PROTOCOL_ID_ESP) {
309             throw new InvalidSyntaxException(
310                     "Expected Procotol ID AH(2) or ESP(3): Protocol ID is " + protocolId);
311         }
312 
313         if (!VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.contains(notifyType)) {
314             throw new InvalidSyntaxException(
315                     "Expected Notify Type for existing Child SA: Notify Type is " + notifyType);
316         }
317     }
318 
validateNotifyPayloadForIkeAndNewChild()319     private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException {
320         if (protocolId != PROTOCOL_ID_UNSET) {
321             getIkeLog().w(TAG, "Expected Procotol ID unset: Protocol ID is " + protocolId);
322         }
323 
324         if (notifyType == ERROR_TYPE_INVALID_SELECTORS
325                 || notifyType == ERROR_TYPE_CHILD_SA_NOT_FOUND) {
326             throw new InvalidSyntaxException(
327                     "Expected Notify Type concerning IKE SA or new Child SA under negotiation"
328                             + ": Notify Type is "
329                             + notifyType);
330         }
331     }
332 
333     /**
334      * Generate NAT DETECTION notification data.
335      *
336      * <p>This method calculates NAT DETECTION notification data which is a SHA-1 digest of the IKE
337      * initiator's SPI, IKE responder's SPI, IP address and port. Source address and port should be
338      * used for generating NAT_DETECTION_SOURCE_IP data. Destination address and port should be used
339      * for generating NAT_DETECTION_DESTINATION_IP data. Here "source" and "destination" mean the
340      * direction of this IKE message.
341      *
342      * @param initiatorIkeSpi the SPI of IKE initiator
343      * @param responderIkeSpi the SPI of IKE responder
344      * @param ipAddress the IP address
345      * @param port the port
346      * @return the generated NAT DETECTION notification data as a byte array.
347      */
generateNatDetectionData( long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port)348     public static byte[] generateNatDetectionData(
349             long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port) {
350         byte[] rawIpAddr = ipAddress.getAddress();
351 
352         ByteBuffer byteBuffer =
353                 ByteBuffer.allocate(2 * SPI_LEN_IKE + rawIpAddr.length + IP_PORT_LEN);
354         byteBuffer
355                 .putLong(initiatorIkeSpi)
356                 .putLong(responderIkeSpi)
357                 .put(rawIpAddr)
358                 .putShort((short) port);
359 
360         try {
361             MessageDigest natDetectionDataDigest =
362                     MessageDigest.getInstance(NAT_DETECTION_DIGEST_ALGORITHM);
363             return natDetectionDataDigest.digest(byteBuffer.array());
364         } catch (NoSuchAlgorithmException e) {
365             throw new ProviderException(
366                     "Failed to obtain algorithm :" + NAT_DETECTION_DIGEST_ALGORITHM, e);
367         }
368     }
369 
handleCookieAndGenerateCopy( IkeNotifyPayload cookie2Notify, int minLen, int maxLen)370     private static IkeNotifyPayload handleCookieAndGenerateCopy(
371             IkeNotifyPayload cookie2Notify, int minLen, int maxLen) throws InvalidSyntaxException {
372         byte[] notifyData = cookie2Notify.notifyData;
373         if (notifyData.length < minLen || notifyData.length > maxLen) {
374             String cookieType =
375                     cookie2Notify.notifyType == NOTIFY_TYPE_COOKIE2 ? "COOKIE2" : "COOKIE";
376             throw new InvalidSyntaxException(
377                     "Invalid "
378                             + cookieType
379                             + " notification data with length "
380                             + notifyData.length);
381         }
382 
383         return new IkeNotifyPayload(cookie2Notify.notifyType, notifyData);
384     }
385 
386     /** Validate inbound Cookie in IKE_INIT response and build a Cookie notify payload in request */
handleCookieAndGenerateCopy(IkeNotifyPayload cookieNotify)387     public static IkeNotifyPayload handleCookieAndGenerateCopy(IkeNotifyPayload cookieNotify)
388             throws InvalidSyntaxException {
389         return handleCookieAndGenerateCopy(cookieNotify, COOKIE_DATA_LEN_MIN, COOKIE_DATA_LEN_MAX);
390     }
391 
392     /** Validate inbound Cookie2 request and build a response Cookie2 notify payload */
handleCookie2AndGenerateCopy(IkeNotifyPayload cookie2Notify)393     public static IkeNotifyPayload handleCookie2AndGenerateCopy(IkeNotifyPayload cookie2Notify)
394             throws InvalidSyntaxException {
395         return handleCookieAndGenerateCopy(
396                 cookie2Notify, COOKIE2_DATA_LEN_MIN, COOKIE2_DATA_LEN_MAX);
397     }
398 
399     /**
400      * Encode Notify payload to ByteBuffer.
401      *
402      * @param nextPayload type of payload that follows this payload.
403      * @param byteBuffer destination ByteBuffer that stores encoded payload.
404      */
405     @Override
encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)406     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
407         encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
408         byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) notifyType);
409         if (spiSize == SPI_LEN_IPSEC) {
410             byteBuffer.putInt(spi);
411         }
412         byteBuffer.put(notifyData);
413     }
414 
415     /**
416      * Get entire payload length.
417      *
418      * @return entire payload length.
419      */
420     @Override
getPayloadLength()421     protected int getPayloadLength() {
422         return GENERIC_HEADER_LENGTH + NOTIFY_HEADER_LEN + spiSize + notifyData.length;
423     }
424 
IkeNotifyPayload( @rotocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData)425     protected IkeNotifyPayload(
426             @ProtocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData) {
427         super(PAYLOAD_TYPE_NOTIFY, false);
428         this.protocolId = protocolId;
429         this.spiSize = spiSize;
430         this.spi = spi;
431         this.notifyType = notifyType;
432         this.notifyData = notifyData;
433     }
434 
435     /**
436      * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be
437      * negotiated with associated notification data.
438      *
439      * @param notifyType the notify type concerning IKE SA
440      * @param notifytData status or error data transmitted. Values for this field are notify type
441      *     specific.
442      */
IkeNotifyPayload(int notifyType, byte[] notifyData)443     public IkeNotifyPayload(int notifyType, byte[] notifyData) {
444         this(PROTOCOL_ID_UNSET, SPI_LEN_NOT_INCLUDED, SPI_NOT_INCLUDED, notifyType, notifyData);
445         try {
446             validateNotifyPayloadForIkeAndNewChild();
447         } catch (InvalidSyntaxException e) {
448             throw new IllegalArgumentException(e);
449         }
450     }
451 
452     /**
453      * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be
454      * negotiated without additional notification data.
455      *
456      * @param notifyType the notify type concerning IKE SA
457      */
IkeNotifyPayload(int notifyType)458     public IkeNotifyPayload(int notifyType) {
459         this(notifyType, new byte[0]);
460     }
461 
462     /**
463      * Construct IkeNotifyPayload concerning existing Child SA
464      *
465      * @param notifyType the notify type concerning Child SA
466      * @param notifytData status or error data transmitted. Values for this field are notify type
467      *     specific.
468      */
IkeNotifyPayload( @rotocolId int protocolId, int spi, int notifyType, byte[] notifyData)469     public IkeNotifyPayload(
470             @ProtocolId int protocolId, int spi, int notifyType, byte[] notifyData) {
471         this(protocolId, SPI_LEN_IPSEC, spi, notifyType, notifyData);
472         try {
473             validateNotifyPayloadForExistingChildSa();
474         } catch (InvalidSyntaxException e) {
475             throw new IllegalArgumentException(e);
476         }
477     }
478 
479     /**
480      * Indicates if this is an error notification payload.
481      *
482      * @return if this is an error notification payload.
483      */
isErrorNotify()484     public boolean isErrorNotify() {
485         return notifyType <= ERROR_NOTIFY_TYPE_MAX;
486     }
487 
488     /**
489      * Indicates if this is an notification for a new Child SA negotiation.
490      *
491      * <p>This notification may provide additional configuration information for negotiating a new
492      * Child SA or is an error notification of the Child SA negotiation failure.
493      *
494      * @return if this is an notification for a new Child SA negotiation.
495      */
isNewChildSaNotify()496     public boolean isNewChildSaNotify() {
497         return VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.contains(notifyType);
498     }
499 
500     /**
501      * Validate error data and build IkeProtocolException for this error notification.
502      *
503      * @return the IkeProtocolException that represents this error.
504      * @throws InvalidSyntaxException if error data has invalid size.
505      */
validateAndBuildIkeException()506     public IkeProtocolException validateAndBuildIkeException() throws InvalidSyntaxException {
507         if (!isErrorNotify()) {
508             throw new IllegalArgumentException(
509                     "Do not support building IkeException for a non-error notificaton. Notify"
510                             + " type: "
511                             + notifyType);
512         }
513 
514         try {
515             switch (notifyType) {
516                 case ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD:
517                     return new UnsupportedCriticalPayloadException(notifyData);
518                 case ERROR_TYPE_INVALID_IKE_SPI:
519                     return new InvalidIkeSpiException(notifyData);
520                 case ERROR_TYPE_INVALID_MAJOR_VERSION:
521                     return new InvalidMajorVersionException(notifyData);
522                 case ERROR_TYPE_INVALID_SYNTAX:
523                     return new InvalidSyntaxException(notifyData);
524                 case ERROR_TYPE_INVALID_MESSAGE_ID:
525                     return new InvalidMessageIdException(notifyData);
526                 case ERROR_TYPE_NO_PROPOSAL_CHOSEN:
527                     return new NoValidProposalChosenException(notifyData);
528                 case ERROR_TYPE_INVALID_KE_PAYLOAD:
529                     return new InvalidKeException(notifyData);
530                 case ERROR_TYPE_AUTHENTICATION_FAILED:
531                     return new AuthenticationFailedException(notifyData);
532                 case ERROR_TYPE_SINGLE_PAIR_REQUIRED:
533                     return new SinglePairRequiredException(notifyData);
534                 case ERROR_TYPE_NO_ADDITIONAL_SAS:
535                     return new NoAdditionalSasException(notifyData);
536                 case ERROR_TYPE_INTERNAL_ADDRESS_FAILURE:
537                     return new InternalAddressFailureException(notifyData);
538                 case ERROR_TYPE_FAILED_CP_REQUIRED:
539                     return new FailedCpRequiredException(notifyData);
540                 case ERROR_TYPE_TS_UNACCEPTABLE:
541                     return new TsUnacceptableException(notifyData);
542                 case ERROR_TYPE_INVALID_SELECTORS:
543                     return new InvalidSelectorsException(spi, notifyData);
544                 case ERROR_TYPE_TEMPORARY_FAILURE:
545                     return new TemporaryFailureException(notifyData);
546                 case ERROR_TYPE_CHILD_SA_NOT_FOUND:
547                     return new ChildSaNotFoundException(spi, notifyData);
548                 default:
549                     return new UnrecognizedIkeProtocolException(notifyType, notifyData);
550             }
551         } catch (IllegalArgumentException e) {
552             // Notification data length is invalid.
553             throw new InvalidSyntaxException(e);
554         }
555     }
556 
557     /**
558      * Return the payload type as a String.
559      *
560      * @return the payload type as a String.
561      */
562     @Override
getTypeString()563     public String getTypeString() {
564         String notifyTypeString = NOTIFY_TYPE_TO_STRING.get(notifyType);
565 
566         if (notifyTypeString == null) {
567             return "Notify(" + notifyType + ")";
568         }
569         return "Notify(" + notifyTypeString + ")";
570     }
571 }
572