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 com.android.internal.net.ipsec.ike.message.IkePayload.PayloadType;
20 
21 import android.annotation.IntDef;
22 import android.net.ipsec.ike.exceptions.IkeProtocolException;
23 import android.util.SparseArray;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.net.ipsec.ike.exceptions.InvalidMajorVersionException;
27 import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.nio.ByteBuffer;
32 
33 /**
34  * IkeHeader represents an IKE message header. It contains all header attributes and provide methods
35  * for encoding and decoding it.
36  *
37  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.1">RFC 7296, Internet Key Exchange
38  *     Protocol Version 2 (IKEv2)</a>
39  */
40 public final class IkeHeader {
41     // TODO: b/122838549 Change IkeHeader to static inner class of IkeMessage.
42     private static final byte IKE_HEADER_VERSION_INFO = (byte) 0x20;
43 
44     // Indicate whether this message is a response message
45     private static final byte IKE_HEADER_FLAG_IS_RESP_MSG = (byte) 0x20;
46     // Indicate whether this message is sent from the original IKE initiator
47     private static final byte IKE_HEADER_FLAG_FROM_IKE_INITIATOR = (byte) 0x08;
48 
49     private static final SparseArray<String> EXCHANGE_TYPE_TO_STRING;
50 
51     public static final int IKE_HEADER_LENGTH = 28;
52 
53     @Retention(RetentionPolicy.SOURCE)
54     @IntDef({
55         EXCHANGE_TYPE_IKE_SA_INIT,
56         EXCHANGE_TYPE_IKE_AUTH,
57         EXCHANGE_TYPE_CREATE_CHILD_SA,
58         EXCHANGE_TYPE_INFORMATIONAL
59     })
60     public @interface ExchangeType {}
61 
62     public static final int EXCHANGE_TYPE_IKE_SA_INIT = 34;
63     public static final int EXCHANGE_TYPE_IKE_AUTH = 35;
64     public static final int EXCHANGE_TYPE_CREATE_CHILD_SA = 36;
65     public static final int EXCHANGE_TYPE_INFORMATIONAL = 37;
66 
67     static {
68         EXCHANGE_TYPE_TO_STRING = new SparseArray<>();
EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_IKE_SA_INIT, "IKE INIT")69         EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_IKE_SA_INIT, "IKE INIT");
EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_IKE_AUTH, "IKE AUTH")70         EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_IKE_AUTH, "IKE AUTH");
EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_CREATE_CHILD_SA, "Create Child")71         EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_CREATE_CHILD_SA, "Create Child");
EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_INFORMATIONAL, "Informational")72         EXCHANGE_TYPE_TO_STRING.put(EXCHANGE_TYPE_INFORMATIONAL, "Informational");
73     }
74 
75     public final long ikeInitiatorSpi;
76     public final long ikeResponderSpi;
77     @PayloadType public final int nextPayloadType;
78     public final byte majorVersion;
79     public final byte minorVersion;
80     @ExchangeType public final int exchangeType;
81     public final boolean isResponseMsg;
82     public final boolean fromIkeInitiator;
83     public final int messageId;
84 
85     // Cannot assign encoded message length value for an outbound IKE message before it's encoded.
86     private static final int ENCODED_MESSAGE_LEN_UNAVAILABLE = -1;
87 
88     // mEncodedMessageLength is only set for an inbound IkeMessage. When building an outbound
89     // IkeMessage, message length is not set because message body length is unknown until it gets
90     // encrypted and encoded.
91     private final int mEncodedMessageLength;
92 
93     /**
94      * Construct an instance of IkeHeader. It is only called in the process of building outbound
95      * message.
96      *
97      * @param iSpi the SPI of IKE initiator
98      * @param rSpi the SPI of IKE responder
99      * @param nextPType the first payload's type
100      * @param eType the type of IKE exchange being used
101      * @param isResp indicates if this message is a response or a request
102      * @param fromInit indictaes if this message is sent from the IKE initiator or the IKE responder
103      * @param msgId the message identifier
104      */
IkeHeader( long iSpi, long rSpi, @PayloadType int nextPType, @ExchangeType int eType, boolean isResp, boolean fromInit, int msgId)105     public IkeHeader(
106             long iSpi,
107             long rSpi,
108             @PayloadType int nextPType,
109             @ExchangeType int eType,
110             boolean isResp,
111             boolean fromInit,
112             int msgId) {
113         ikeInitiatorSpi = iSpi;
114         ikeResponderSpi = rSpi;
115         nextPayloadType = nextPType;
116         exchangeType = eType;
117         isResponseMsg = isResp;
118         fromIkeInitiator = fromInit;
119         messageId = msgId;
120 
121         mEncodedMessageLength = ENCODED_MESSAGE_LEN_UNAVAILABLE;
122 
123         // Major version of IKE protocol in use; it must be set to 2 when building an IKEv2 message.
124         majorVersion = 2;
125         // Minor version of IKE protocol in use; it must be set to 0 when building an IKEv2 message.
126         minorVersion = 0;
127     }
128 
129     /**
130      * Decode IKE header from a byte array and construct an IkeHeader instance.
131      *
132      * @param packet the raw byte array of the whole IKE message
133      */
IkeHeader(byte[] packet)134     public IkeHeader(byte[] packet) throws IkeProtocolException {
135         if (packet.length <= IKE_HEADER_LENGTH) {
136             throw new InvalidSyntaxException("IKE message is too short to contain a header");
137         }
138 
139         ByteBuffer buffer = ByteBuffer.wrap(packet);
140 
141         ikeInitiatorSpi = buffer.getLong();
142         ikeResponderSpi = buffer.getLong();
143         nextPayloadType = Byte.toUnsignedInt(buffer.get());
144 
145         byte versionByte = buffer.get();
146         majorVersion = (byte) ((versionByte >> 4) & 0x0F);
147         minorVersion = (byte) (versionByte & 0x0F);
148 
149         exchangeType = Byte.toUnsignedInt(buffer.get());
150 
151         byte flagsByte = buffer.get();
152         isResponseMsg = ((flagsByte & 0x20) != 0);
153         fromIkeInitiator = ((flagsByte & 0x08) != 0);
154 
155         messageId = buffer.getInt();
156         mEncodedMessageLength = buffer.getInt();
157     }
158 
159     /** Packet private method to build header of an IKE fragemnt from current IKE header. */
makeSkfHeaderFromSkHeader()160     IkeHeader makeSkfHeaderFromSkHeader() {
161         if (nextPayloadType != IkePayload.PAYLOAD_TYPE_SK) {
162             throw new IllegalArgumentException("Next payload type is not SK.");
163         }
164         return new IkeHeader(
165                 ikeInitiatorSpi,
166                 ikeResponderSpi,
167                 IkePayload.PAYLOAD_TYPE_SKF,
168                 exchangeType,
169                 isResponseMsg,
170                 fromIkeInitiator,
171                 messageId);
172     }
173 
174     /*Package private*/
175     @VisibleForTesting
getInboundMessageLength()176     int getInboundMessageLength() {
177         if (mEncodedMessageLength == ENCODED_MESSAGE_LEN_UNAVAILABLE) {
178             throw new UnsupportedOperationException(
179                     "It is not supported to get encoded message length from an outbound message.");
180         }
181         return mEncodedMessageLength;
182     }
183 
184     /** Validate major version of inbound IKE header. */
validateMajorVersion()185     public void validateMajorVersion() throws IkeProtocolException {
186         if (majorVersion > 2) {
187             // Receive higher version of protocol. Stop parsing.
188             throw new InvalidMajorVersionException(majorVersion);
189         }
190         if (majorVersion < 2) {
191             // There is no specific instruction for dealing with this error case.
192             // Since IKE library only supports IKEv2 and not allowed to check if message
193             // sender supports higher version, it is proper to treat this error as an invalid syntax
194             // error.
195             throw new InvalidSyntaxException("Major version is smaller than 2.");
196         }
197     }
198 
199     /**
200      * Validate syntax of inbound IKE header.
201      *
202      * <p>It MUST only be used for an inbound IKE header because we don't know the outbound message
203      * length before we encode it.
204      */
validateInboundHeader(int packetLength)205     public void validateInboundHeader(int packetLength) throws IkeProtocolException {
206         if (exchangeType < EXCHANGE_TYPE_IKE_SA_INIT
207                 || exchangeType > EXCHANGE_TYPE_INFORMATIONAL) {
208             throw new InvalidSyntaxException("Invalid IKE Exchange Type.");
209         }
210         if (mEncodedMessageLength != packetLength) {
211             throw new InvalidSyntaxException("Invalid IKE Message Length.");
212         }
213     }
214 
215     /** Encode IKE header to ByteBuffer */
encodeToByteBuffer(ByteBuffer byteBuffer, int encodedMessageBodyLen)216     public void encodeToByteBuffer(ByteBuffer byteBuffer, int encodedMessageBodyLen) {
217         byteBuffer
218                 .putLong(ikeInitiatorSpi)
219                 .putLong(ikeResponderSpi)
220                 .put((byte) nextPayloadType)
221                 .put(IKE_HEADER_VERSION_INFO)
222                 .put((byte) exchangeType);
223 
224         byte flag = 0;
225         if (isResponseMsg) {
226             flag |= IKE_HEADER_FLAG_IS_RESP_MSG;
227         }
228         if (fromIkeInitiator) {
229             flag |= IKE_HEADER_FLAG_FROM_IKE_INITIATOR;
230         }
231 
232         byteBuffer.put(flag).putInt(messageId).putInt(IKE_HEADER_LENGTH + encodedMessageBodyLen);
233     }
234 
235     /** Returns basic information for logging. */
getBasicInfoString()236     public String getBasicInfoString() {
237         String exchangeStr = EXCHANGE_TYPE_TO_STRING.get(exchangeType);
238         if (exchangeStr == null) exchangeStr = "Unknown exchange (" + exchangeType + ")";
239 
240         String reqOrResp = isResponseMsg ? "response" : "request";
241 
242         return exchangeStr + " " + reqOrResp + " " + messageId;
243     }
244 }
245