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