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