1 /*
2  * Copyright (C) 2019 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 android.net.ipsec.ike.exceptions.IkeProtocolException;
20 
21 import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
22 
23 import java.nio.ByteBuffer;
24 
25 /**
26  * IkeDeletePayload represents a Delete Payload.
27  *
28  * <p>As instructed in RFC 7296, deletion of the IKE SA is indicated by a protocol ID of 1 (IKE) but
29  * no SPIs. Deletion of a Child SA will contain the IPsec protocol ID and SPIs of inbound IPsec
30  * packets. Since IKE library only supports negotiating Child SA using ESP, only the protocol ID of
31  * 3 (ESP) is used for deleting Child SA.
32  *
33  * The possible request/response pairs for deletion are as follows:
34  * - IKE SA deletion:
35  *     Incoming: INFORMATIONAL(DELETE(PROTO_IKE))
36  *     Outgoing: INFORMATIONAL()
37  *
38  * - ESP SA deletion:
39  *     Incoming: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_OUT))
40  *     Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_IN))
41  *
42  * - ESP SA simultaneous deletion:
43  *     Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_IN))
44  *     Incoming: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_OUT))
45  *     Outgoing: INFORMATIONAL() // Notice DELETE payload omitted
46  *
47  * - ESP SA simultaneous multi-deletion:
48  *     Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_IN))
49  *     Incoming: INFORMATIONAL(DELETE(PROTO_ESP, SPI_A_OUT, SPI_B_OUT))
50  *     Outgoing: INFORMATIONAL(DELETE(PROTO_ESP, SPI_B_IN)) // Notice SPI_A_OUT omitted
51  *
52  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.11">RFC 7296, Internet Key Exchange
53  *     Protocol Version 2 (IKEv2)</a>
54  */
55 public final class IkeDeletePayload extends IkeInformationalPayload {
56     private static final int DELETE_HEADER_LEN = 4;
57 
58     @ProtocolId public final int protocolId;
59     public final byte spiSize;
60     public final int numSpi;
61     public final int[] spisToDelete;
62 
63     /**
64      * Construct an instance of IkeDeletePayload from decoding inbound IKE packet.
65      *
66      * <p>NegativeArraySizeException and BufferUnderflowException will be caught in {@link
67      * IkeMessage}
68      *
69      * @param critical indicates if this payload is critical. Ignored in supported payload as
70      *     instructed by the RFC 7296.
71      * @param payloadBody payload body in byte array
72      * @throws IkeProtocolException if there is any error
73      */
IkeDeletePayload(boolean critical, byte[] payloadBody)74     IkeDeletePayload(boolean critical, byte[] payloadBody) throws IkeProtocolException {
75         super(PAYLOAD_TYPE_DELETE, critical);
76 
77         ByteBuffer inputBuffer = ByteBuffer.wrap(payloadBody);
78 
79         protocolId = Byte.toUnsignedInt(inputBuffer.get());
80         spiSize = inputBuffer.get();
81         numSpi = Short.toUnsignedInt(inputBuffer.getShort());
82         spisToDelete = new int[numSpi];
83 
84         switch (protocolId) {
85             case PROTOCOL_ID_IKE:
86                 // Delete payload for IKE SA must not include SPI.
87                 if (spiSize != SPI_LEN_NOT_INCLUDED
88                         || numSpi != 0
89                         || inputBuffer.remaining() != 0) {
90                     throw new InvalidSyntaxException("Invalid Delete IKE Payload.");
91                 }
92                 break;
93             case PROTOCOL_ID_ESP:
94                 // Delete payload for Child SA must include SPI
95                 if (spiSize != SPI_LEN_IPSEC
96                         || numSpi == 0
97                         || inputBuffer.remaining() != SPI_LEN_IPSEC * numSpi) {
98                     throw new InvalidSyntaxException("Invalid Delete Child Payload.");
99                 }
100 
101                 for (int i = 0; i < numSpi; i++) {
102                     spisToDelete[i] = inputBuffer.getInt();
103                 }
104                 break;
105             default:
106                 throw new InvalidSyntaxException("Unrecognized protocol in Delete Payload.");
107         }
108     }
109 
110     /**
111      * Constructor for an outbound IKE SA deletion payload.
112      *
113      * <p>This constructor takes no SPI, as IKE SAs are deleted by sending a delete payload within
114      * the negotiated session. As such, the SPIs are shared state that does not need to be sent.
115      */
IkeDeletePayload()116     public IkeDeletePayload() {
117         super(PAYLOAD_TYPE_DELETE, false);
118         protocolId = PROTOCOL_ID_IKE;
119         spiSize = SPI_LEN_NOT_INCLUDED;
120         numSpi = 0;
121         spisToDelete = new int[0];
122     }
123 
124     /**
125      * Constructor for an outbound Child SA deletion payload.
126      *
127      * @param spis array of SPIs of Child SAs to delete. Must contain at least one SPI.
128      */
IkeDeletePayload(int[] spis)129     public IkeDeletePayload(int[] spis) {
130         super(PAYLOAD_TYPE_DELETE, false);
131 
132         if (spis == null || spis.length < 1) {
133             throw new IllegalArgumentException("No SPIs provided");
134         }
135 
136         protocolId = PROTOCOL_ID_ESP;
137         spiSize = SPI_LEN_IPSEC;
138         numSpi = spis.length;
139         spisToDelete = spis;
140     }
141 
142     /**
143      * Encode Delete Payload to ByteBuffer.
144      *
145      * @param nextPayload type of payload that follows this payload.
146      * @param byteBuffer destination ByteBuffer that stores encoded payload.
147      */
148     @Override
encodeToByteBuffer(@ayloadType int nextPayload, ByteBuffer byteBuffer)149     protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
150         encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
151         byteBuffer.put((byte) protocolId).put(spiSize).putShort((short) numSpi);
152 
153         for (int toDelete : spisToDelete) {
154             byteBuffer.putInt(toDelete);
155         }
156     }
157 
158     /**
159      * Get entire payload length.
160      *
161      * @return entire payload length.
162      */
163     @Override
getPayloadLength()164     protected int getPayloadLength() {
165         return GENERIC_HEADER_LENGTH + DELETE_HEADER_LEN + spisToDelete.length * spiSize;
166     }
167 
168     /**
169      * Return the payload type as a String.
170      *
171      * @return the payload type as a String.
172      */
173     @Override
getTypeString()174     public String getTypeString() {
175         return "Del";
176     }
177 }
178