aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/ike/ikev2/message/IkeHeader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/ike/ikev2/message/IkeHeader.java')
-rw-r--r--src/java/com/android/ike/ikev2/message/IkeHeader.java199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/java/com/android/ike/ikev2/message/IkeHeader.java b/src/java/com/android/ike/ikev2/message/IkeHeader.java
new file mode 100644
index 00000000..c4f215ca
--- /dev/null
+++ b/src/java/com/android/ike/ikev2/message/IkeHeader.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ike.ikev2.message;
+
+import static com.android.ike.ikev2.message.IkePayload.PayloadType;
+
+import android.annotation.IntDef;
+
+import com.android.ike.ikev2.exceptions.IkeException;
+import com.android.ike.ikev2.exceptions.InvalidMajorVersionException;
+import com.android.ike.ikev2.exceptions.InvalidSyntaxException;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+
+/**
+ * IkeHeader represents an IKE message header. It contains all header attributes and provide methods
+ * for encoding and decoding it.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.1">RFC 7296, Internet Key Exchange
+ * Protocol Version 2 (IKEv2)</a>
+ */
+public final class IkeHeader {
+ // TODO: b/122838549 Change IkeHeader to static inner class of IkeMessage.
+ private static final byte IKE_HEADER_VERSION_INFO = (byte) 0x20;
+
+ // Indicate whether this message is a response message
+ private static final byte IKE_HEADER_FLAG_IS_RESP_MSG = (byte) 0x20;
+ // Indicate whether this message is sent from the original IKE initiator
+ private static final byte IKE_HEADER_FLAG_FROM_IKE_INITIATOR = (byte) 0x08;
+
+ public static final int IKE_HEADER_LENGTH = 28;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ EXCHANGE_TYPE_IKE_SA_INIT,
+ EXCHANGE_TYPE_IKE_AUTH,
+ EXCHANGE_TYPE_CREATE_CHILD_SA,
+ EXCHANGE_TYPE_INFORMATIONAL
+ })
+ public @interface ExchangeType {}
+
+ public static final int EXCHANGE_TYPE_IKE_SA_INIT = 34;
+ public static final int EXCHANGE_TYPE_IKE_AUTH = 35;
+ public static final int EXCHANGE_TYPE_CREATE_CHILD_SA = 36;
+ public static final int EXCHANGE_TYPE_INFORMATIONAL = 37;
+
+ public final long ikeInitiatorSpi;
+ public final long ikeResponderSpi;
+ @PayloadType public final int nextPayloadType;
+ public final byte majorVersion;
+ public final byte minorVersion;
+ @ExchangeType public final int exchangeType;
+ public final boolean isResponseMsg;
+ public final boolean fromIkeInitiator;
+ public final int messageId;
+
+ // Cannot assign encoded message length value for an outbound IKE message before it's encoded.
+ private static final int ENCODED_MESSAGE_LEN_UNAVAILABLE = -1;
+
+ // mEncodedMessageLength is only set for an inbound IkeMessage. When building an outbound
+ // IkeMessage, message length is not set because message body length is unknown until it gets
+ // encrypted and encoded.
+ private final int mEncodedMessageLength;
+
+ /**
+ * Construct an instance of IkeHeader. It is only called in the process of building outbound
+ * message.
+ *
+ * @param iSpi the SPI of IKE initiator
+ * @param rSpi the SPI of IKE responder
+ * @param nextPType the first payload's type
+ * @param eType the type of IKE exchange being used
+ * @param isResp indicates if this message is a response or a request
+ * @param fromInit indictaes if this message is sent from the IKE initiator or the IKE responder
+ * @param msgId the message identifier
+ */
+ public IkeHeader(
+ long iSpi,
+ long rSpi,
+ @PayloadType int nextPType,
+ @ExchangeType int eType,
+ boolean isResp,
+ boolean fromInit,
+ int msgId) {
+ ikeInitiatorSpi = iSpi;
+ ikeResponderSpi = rSpi;
+ nextPayloadType = nextPType;
+ exchangeType = eType;
+ isResponseMsg = isResp;
+ fromIkeInitiator = fromInit;
+ messageId = msgId;
+
+ mEncodedMessageLength = ENCODED_MESSAGE_LEN_UNAVAILABLE;
+
+ // Major version of IKE protocol in use; it must be set to 2 when building an IKEv2 message.
+ majorVersion = 2;
+ // Minor version of IKE protocol in use; it must be set to 0 when building an IKEv2 message.
+ minorVersion = 0;
+ }
+
+ /**
+ * Decode IKE header from a byte array and construct an IkeHeader instance.
+ *
+ * @param packet the raw byte array of the whole IKE message
+ */
+ public IkeHeader(byte[] packet) throws IkeException {
+ if (packet.length <= IKE_HEADER_LENGTH) {
+ throw new InvalidSyntaxException("IKE message is too short to contain a header");
+ }
+
+ ByteBuffer buffer = ByteBuffer.wrap(packet);
+
+ ikeInitiatorSpi = buffer.getLong();
+ ikeResponderSpi = buffer.getLong();
+ nextPayloadType = Byte.toUnsignedInt(buffer.get());
+
+ byte versionByte = buffer.get();
+ majorVersion = (byte) ((versionByte >> 4) & 0x0F);
+ minorVersion = (byte) (versionByte & 0x0F);
+
+ exchangeType = Byte.toUnsignedInt(buffer.get());
+
+ byte flagsByte = buffer.get();
+ isResponseMsg = ((flagsByte & 0x20) != 0);
+ fromIkeInitiator = ((flagsByte & 0x08) != 0);
+
+ messageId = buffer.getInt();
+ mEncodedMessageLength = buffer.getInt();
+ }
+
+ /*Package private*/
+ @VisibleForTesting
+ int getInboundMessageLength() {
+ if (mEncodedMessageLength == ENCODED_MESSAGE_LEN_UNAVAILABLE) {
+ throw new UnsupportedOperationException(
+ "It is not supported to get encoded message length from an outbound message.");
+ }
+ return mEncodedMessageLength;
+ }
+
+ /** Validate syntax and major version of inbound IKE header. */
+ public void checkInboundValidOrThrow(int packetLength) throws IkeException {
+ if (majorVersion > 2) {
+ // Receive higher version of protocol. Stop parsing.
+ throw new InvalidMajorVersionException(majorVersion);
+ }
+ if (majorVersion < 2) {
+ // There is no specific instruction for dealing with this error case.
+ // Since IKE library only supports IKEv2 and not allowed to check if message
+ // sender supports higher version, it is proper to treat this error as an invalid syntax
+ // error.
+ throw new InvalidSyntaxException("Major version is smaller than 2.");
+ }
+ if (exchangeType < EXCHANGE_TYPE_IKE_SA_INIT
+ || exchangeType > EXCHANGE_TYPE_INFORMATIONAL) {
+ throw new InvalidSyntaxException("Invalid IKE Exchange Type.");
+ }
+ if (mEncodedMessageLength != packetLength) {
+ throw new InvalidSyntaxException("Invalid IKE Message Length.");
+ }
+ }
+
+ /** Encode IKE header to ByteBuffer */
+ public void encodeToByteBuffer(ByteBuffer byteBuffer, int encodedMessageBodyLen) {
+ byteBuffer
+ .putLong(ikeInitiatorSpi)
+ .putLong(ikeResponderSpi)
+ .put((byte) nextPayloadType)
+ .put(IKE_HEADER_VERSION_INFO)
+ .put((byte) exchangeType);
+
+ byte flag = 0;
+ if (isResponseMsg) {
+ flag |= IKE_HEADER_FLAG_IS_RESP_MSG;
+ }
+ if (fromIkeInitiator) {
+ flag |= IKE_HEADER_FLAG_FROM_IKE_INITIATOR;
+ }
+
+ byteBuffer.put(flag).putInt(messageId).putInt(IKE_HEADER_LENGTH + encodedMessageBodyLen);
+ }
+}