• 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.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     private static final int ERROR_NOTIFY_TYPE_MAX = 16383;
183 
184     private static final String NAT_DETECTION_DIGEST_ALGORITHM = "SHA-1";
185 
186     private static final int COOKIE_DATA_LEN_MIN = 1;
187     private static final int COOKIE_DATA_LEN_MAX = 64;
188 
189     private static final int COOKIE2_DATA_LEN_MIN = 8;
190     private static final int COOKIE2_DATA_LEN_MAX = 64;
191 
192     private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA;
193     private static final Set<Integer> VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA;
194 
195     private static final SparseArray<String> NOTIFY_TYPE_TO_STRING;
196 
197     static {
198         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA = new ArraySet<>();
199         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_INVALID_SELECTORS);
200         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(ERROR_TYPE_CHILD_SA_NOT_FOUND);
201         VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.add(NOTIFY_TYPE_REKEY_SA);
202     }
203 
204     static {
205         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA = new ArraySet<>();
206         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN);
207         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD);
208         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(
209                 IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED);
210         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_NO_ADDITIONAL_SAS);
211         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(
212                 IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE);
213         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED);
214         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE);
215 
216         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE);
217         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_IPCOMP_SUPPORTED);
218         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_USE_TRANSPORT_MODE);
219         VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.add(NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED);
220     }
221 
222     static {
223         NOTIFY_TYPE_TO_STRING = new SparseArray<>();
NOTIFY_TYPE_TO_STRING.put( ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload")224         NOTIFY_TYPE_TO_STRING.put(
225                 ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD, "Unsupported critical payload");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_IKE_SPI, "Invalid IKE SPI")226         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")227         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MAJOR_VERSION, "Invalid major version");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax")228         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SYNTAX, "Invalid syntax");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_MESSAGE_ID, "Invalid message ID")229         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")230         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")231         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_KE_PAYLOAD, "Invalid KE payload");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed")232         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_AUTHENTICATION_FAILED, "Authentication failed");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_SINGLE_PAIR_REQUIRED, "Single pair required")233         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")234         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")235         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")236         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_FAILED_CP_REQUIRED, "Failed CP required");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable")237         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TS_UNACCEPTABLE, "TS unacceptable");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors")238         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_INVALID_SELECTORS, "Invalid selectors");
NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_TEMPORARY_FAILURE, "Temporary failure")239         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")240         NOTIFY_TYPE_TO_STRING.put(ERROR_TYPE_CHILD_SA_NOT_FOUND, "Child SA not found");
241 
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible")242         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_ADDITIONAL_TS_POSSIBLE, "Additional TS possible");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_IPCOMP_SUPPORTED, "IPCOMP supported")243         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")244         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")245         NOTIFY_TYPE_TO_STRING.put(
246                 NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP, "NAT detection destination IP");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE, "COOKIE")247         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE, "COOKIE");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode")248         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_USE_TRANSPORT_MODE, "Use transport mode");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_REKEY_SA, "Rekey SA")249         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")250         NOTIFY_TYPE_TO_STRING.put(
251                 NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TFC Padding not supported");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_MOBIKE_SUPPORTED, "MOBIKE supported")252         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_MOBIKE_SUPPORTED, "MOBIKE supported");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_UPDATE_SA_ADDRESSES, "UPDATE_SA_ADDRESSES")253         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_UPDATE_SA_ADDRESSES, "UPDATE_SA_ADDRESSES");
NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE2, "COOKIE2")254         NOTIFY_TYPE_TO_STRING.put(NOTIFY_TYPE_COOKIE2, "COOKIE2");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported")255         NOTIFY_TYPE_TO_STRING.put(
256                 NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported");
NOTIFY_TYPE_TO_STRING.put( NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported")257         NOTIFY_TYPE_TO_STRING.put(
258                 NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported");
259     }
260 
261     public final int protocolId;
262     public final byte spiSize;
263     public final int notifyType;
264     public final int spi;
265     public final byte[] notifyData;
266 
267     /**
268      * Construct an instance of IkeNotifyPayload in the context of IkePayloadFactory
269      *
270      * @param critical indicates if this payload is critical. Ignored in supported payload as
271      *     instructed by the RFC 7296.
272      * @param payloadBody payload body in byte array
273      * @throws IkeProtocolException if there is any error
274      */
IkeNotifyPayload(boolean isCritical, byte[] payloadBody)275     IkeNotifyPayload(boolean isCritical, byte[] payloadBody) throws IkeProtocolException {
276         super(PAYLOAD_TYPE_NOTIFY, isCritical);
277 
278         ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
279 
280         protocolId = Byte.toUnsignedInt(inputBuffer.get());
281         spiSize = inputBuffer.get();
282         notifyType = Short.toUnsignedInt(inputBuffer.getShort());
283 
284         // Validate syntax of spiSize, protocolId and notifyType.
285         // Reference: <https://tools.ietf.org/html/rfc7296#page-100>
286         if (spiSize == SPI_LEN_IPSEC) {
287             // For message concerning existing Child SA
288             validateNotifyPayloadForExistingChildSa();
289             spi = inputBuffer.getInt();
290 
291         } else if (spiSize == SPI_LEN_NOT_INCLUDED) {
292             // For message concerning IKE SA or for new Child SA that to be negotiated.
293             validateNotifyPayloadForIkeAndNewChild();
294             spi = SPI_NOT_INCLUDED;
295 
296         } else {
297             throw new InvalidSyntaxException("Invalid SPI Size: " + spiSize);
298         }
299 
300         notifyData = new byte[payloadBody.length - NOTIFY_HEADER_LEN - spiSize];
301         inputBuffer.get(notifyData);
302     }
303 
validateNotifyPayloadForExistingChildSa()304     private void validateNotifyPayloadForExistingChildSa() throws InvalidSyntaxException {
305         if (protocolId != PROTOCOL_ID_AH && protocolId != PROTOCOL_ID_ESP) {
306             throw new InvalidSyntaxException(
307                     "Expected Procotol ID AH(2) or ESP(3): Protocol ID is " + protocolId);
308         }
309 
310         if (!VALID_NOTIFY_TYPES_FOR_EXISTING_CHILD_SA.contains(notifyType)) {
311             throw new InvalidSyntaxException(
312                     "Expected Notify Type for existing Child SA: Notify Type is " + notifyType);
313         }
314     }
315 
validateNotifyPayloadForIkeAndNewChild()316     private void validateNotifyPayloadForIkeAndNewChild() throws InvalidSyntaxException {
317         if (protocolId != PROTOCOL_ID_UNSET) {
318             getIkeLog().w(TAG, "Expected Procotol ID unset: Protocol ID is " + protocolId);
319         }
320 
321         if (notifyType == ERROR_TYPE_INVALID_SELECTORS
322                 || notifyType == ERROR_TYPE_CHILD_SA_NOT_FOUND) {
323             throw new InvalidSyntaxException(
324                     "Expected Notify Type concerning IKE SA or new Child SA under negotiation"
325                             + ": Notify Type is "
326                             + notifyType);
327         }
328     }
329 
330     /**
331      * Generate NAT DETECTION notification data.
332      *
333      * <p>This method calculates NAT DETECTION notification data which is a SHA-1 digest of the IKE
334      * initiator's SPI, IKE responder's SPI, IP address and port. Source address and port should be
335      * used for generating NAT_DETECTION_SOURCE_IP data. Destination address and port should be used
336      * for generating NAT_DETECTION_DESTINATION_IP data. Here "source" and "destination" mean the
337      * direction of this IKE message.
338      *
339      * @param initiatorIkeSpi the SPI of IKE initiator
340      * @param responderIkeSpi the SPI of IKE responder
341      * @param ipAddress the IP address
342      * @param port the port
343      * @return the generated NAT DETECTION notification data as a byte array.
344      */
generateNatDetectionData( long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port)345     public static byte[] generateNatDetectionData(
346             long initiatorIkeSpi, long responderIkeSpi, InetAddress ipAddress, int port) {
347         byte[] rawIpAddr = ipAddress.getAddress();
348 
349         ByteBuffer byteBuffer =
350                 ByteBuffer.allocate(2 * SPI_LEN_IKE + rawIpAddr.length + IP_PORT_LEN);
351         byteBuffer
352                 .putLong(initiatorIkeSpi)
353                 .putLong(responderIkeSpi)
354                 .put(rawIpAddr)
355                 .putShort((short) port);
356 
357         try {
358             MessageDigest natDetectionDataDigest =
359                     MessageDigest.getInstance(NAT_DETECTION_DIGEST_ALGORITHM);
360             return natDetectionDataDigest.digest(byteBuffer.array());
361         } catch (NoSuchAlgorithmException e) {
362             throw new ProviderException(
363                     "Failed to obtain algorithm :" + NAT_DETECTION_DIGEST_ALGORITHM, e);
364         }
365     }
366 
handleCookieAndGenerateCopy( IkeNotifyPayload cookie2Notify, int minLen, int maxLen)367     private static IkeNotifyPayload handleCookieAndGenerateCopy(
368             IkeNotifyPayload cookie2Notify, int minLen, int maxLen) throws InvalidSyntaxException {
369         byte[] notifyData = cookie2Notify.notifyData;
370         if (notifyData.length < minLen || notifyData.length > maxLen) {
371             String cookieType =
372                     cookie2Notify.notifyType == NOTIFY_TYPE_COOKIE2 ? "COOKIE2" : "COOKIE";
373             throw new InvalidSyntaxException(
374                     "Invalid "
375                             + cookieType
376                             + " notification data with length "
377                             + notifyData.length);
378         }
379 
380         return new IkeNotifyPayload(cookie2Notify.notifyType, notifyData);
381     }
382 
383     /** Validate inbound Cookie in IKE_INIT response and build a Cookie notify payload in request */
handleCookieAndGenerateCopy(IkeNotifyPayload cookieNotify)384     public static IkeNotifyPayload handleCookieAndGenerateCopy(IkeNotifyPayload cookieNotify)
385             throws InvalidSyntaxException {
386         return handleCookieAndGenerateCopy(cookieNotify, COOKIE_DATA_LEN_MIN, COOKIE_DATA_LEN_MAX);
387     }
388 
389     /** Validate inbound Cookie2 request and build a response Cookie2 notify payload */
handleCookie2AndGenerateCopy(IkeNotifyPayload cookie2Notify)390     public static IkeNotifyPayload handleCookie2AndGenerateCopy(IkeNotifyPayload cookie2Notify)
391             throws InvalidSyntaxException {
392         return handleCookieAndGenerateCopy(
393                 cookie2Notify, COOKIE2_DATA_LEN_MIN, COOKIE2_DATA_LEN_MAX);
394     }
395 
396     /**
397      * Encode Notify payload to ByteBuffer.
398      *
399      * @param nextPayload type of payload that follows this payload.
400      * @param byteBuffer destination ByteBuffer that stores encoded payload.
401      */
402     @Override
encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)403     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
404         encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
405         byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) notifyType);
406         if (spiSize == SPI_LEN_IPSEC) {
407             byteBuffer.putInt(spi);
408         }
409         byteBuffer.put(notifyData);
410     }
411 
412     /**
413      * Get entire payload length.
414      *
415      * @return entire payload length.
416      */
417     @Override
getPayloadLength()418     protected int getPayloadLength() {
419         return GENERIC_HEADER_LENGTH + NOTIFY_HEADER_LEN + spiSize + notifyData.length;
420     }
421 
IkeNotifyPayload( @rotocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData)422     protected IkeNotifyPayload(
423             @ProtocolId int protocolId, byte spiSize, int spi, int notifyType, byte[] notifyData) {
424         super(PAYLOAD_TYPE_NOTIFY, false);
425         this.protocolId = protocolId;
426         this.spiSize = spiSize;
427         this.spi = spi;
428         this.notifyType = notifyType;
429         this.notifyData = notifyData;
430     }
431 
432     /**
433      * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be
434      * negotiated with associated notification data.
435      *
436      * @param notifyType the notify type concerning IKE SA
437      * @param notifytData status or error data transmitted. Values for this field are notify type
438      *     specific.
439      */
IkeNotifyPayload(int notifyType, byte[] notifyData)440     public IkeNotifyPayload(int notifyType, byte[] notifyData) {
441         this(PROTOCOL_ID_UNSET, SPI_LEN_NOT_INCLUDED, SPI_NOT_INCLUDED, notifyType, notifyData);
442         try {
443             validateNotifyPayloadForIkeAndNewChild();
444         } catch (InvalidSyntaxException e) {
445             throw new IllegalArgumentException(e);
446         }
447     }
448 
449     /**
450      * Construct IkeNotifyPayload concerning either an IKE SA, or Child SA that is going to be
451      * negotiated without additional notification data.
452      *
453      * @param notifyType the notify type concerning IKE SA
454      */
IkeNotifyPayload(int notifyType)455     public IkeNotifyPayload(int notifyType) {
456         this(notifyType, new byte[0]);
457     }
458 
459     /**
460      * Construct IkeNotifyPayload concerning existing Child SA
461      *
462      * @param notifyType the notify type concerning Child SA
463      * @param notifytData status or error data transmitted. Values for this field are notify type
464      *     specific.
465      */
IkeNotifyPayload( @rotocolId int protocolId, int spi, int notifyType, byte[] notifyData)466     public IkeNotifyPayload(
467             @ProtocolId int protocolId, int spi, int notifyType, byte[] notifyData) {
468         this(protocolId, SPI_LEN_IPSEC, spi, notifyType, notifyData);
469         try {
470             validateNotifyPayloadForExistingChildSa();
471         } catch (InvalidSyntaxException e) {
472             throw new IllegalArgumentException(e);
473         }
474     }
475 
476     /**
477      * Indicates if this is an error notification payload.
478      *
479      * @return if this is an error notification payload.
480      */
isErrorNotify()481     public boolean isErrorNotify() {
482         return notifyType <= ERROR_NOTIFY_TYPE_MAX;
483     }
484 
485     /**
486      * Indicates if this is an notification for a new Child SA negotiation.
487      *
488      * <p>This notification may provide additional configuration information for negotiating a new
489      * Child SA or is an error notification of the Child SA negotiation failure.
490      *
491      * @return if this is an notification for a new Child SA negotiation.
492      */
isNewChildSaNotify()493     public boolean isNewChildSaNotify() {
494         return VALID_NOTIFY_TYPES_FOR_NEW_CHILD_SA.contains(notifyType);
495     }
496 
497     /**
498      * Validate error data and build IkeProtocolException for this error notification.
499      *
500      * @return the IkeProtocolException that represents this error.
501      * @throws InvalidSyntaxException if error data has invalid size.
502      */
validateAndBuildIkeException()503     public IkeProtocolException validateAndBuildIkeException() throws InvalidSyntaxException {
504         if (!isErrorNotify()) {
505             throw new IllegalArgumentException(
506                     "Do not support building IkeException for a non-error notificaton. Notify"
507                             + " type: "
508                             + notifyType);
509         }
510 
511         try {
512             switch (notifyType) {
513                 case ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD:
514                     return new UnsupportedCriticalPayloadException(notifyData);
515                 case ERROR_TYPE_INVALID_IKE_SPI:
516                     return new InvalidIkeSpiException(notifyData);
517                 case ERROR_TYPE_INVALID_MAJOR_VERSION:
518                     return new InvalidMajorVersionException(notifyData);
519                 case ERROR_TYPE_INVALID_SYNTAX:
520                     return new InvalidSyntaxException(notifyData);
521                 case ERROR_TYPE_INVALID_MESSAGE_ID:
522                     return new InvalidMessageIdException(notifyData);
523                 case ERROR_TYPE_NO_PROPOSAL_CHOSEN:
524                     return new NoValidProposalChosenException(notifyData);
525                 case ERROR_TYPE_INVALID_KE_PAYLOAD:
526                     return new InvalidKeException(notifyData);
527                 case ERROR_TYPE_AUTHENTICATION_FAILED:
528                     return new AuthenticationFailedException(notifyData);
529                 case ERROR_TYPE_SINGLE_PAIR_REQUIRED:
530                     return new SinglePairRequiredException(notifyData);
531                 case ERROR_TYPE_NO_ADDITIONAL_SAS:
532                     return new NoAdditionalSasException(notifyData);
533                 case ERROR_TYPE_INTERNAL_ADDRESS_FAILURE:
534                     return new InternalAddressFailureException(notifyData);
535                 case ERROR_TYPE_FAILED_CP_REQUIRED:
536                     return new FailedCpRequiredException(notifyData);
537                 case ERROR_TYPE_TS_UNACCEPTABLE:
538                     return new TsUnacceptableException(notifyData);
539                 case ERROR_TYPE_INVALID_SELECTORS:
540                     return new InvalidSelectorsException(spi, notifyData);
541                 case ERROR_TYPE_TEMPORARY_FAILURE:
542                     return new TemporaryFailureException(notifyData);
543                 case ERROR_TYPE_CHILD_SA_NOT_FOUND:
544                     return new ChildSaNotFoundException(spi, notifyData);
545                 default:
546                     return new UnrecognizedIkeProtocolException(notifyType, notifyData);
547             }
548         } catch (IllegalArgumentException e) {
549             // Notification data length is invalid.
550             throw new InvalidSyntaxException(e);
551         }
552     }
553 
554     /**
555      * Return the payload type as a String.
556      *
557      * @return the payload type as a String.
558      */
559     @Override
getTypeString()560     public String getTypeString() {
561         String notifyTypeString = NOTIFY_TYPE_TO_STRING.get(notifyType);
562 
563         if (notifyTypeString == null) {
564             return "Notify(" + notifyType + ")";
565         }
566         return "Notify(" + notifyTypeString + ")";
567     }
568 }
569