| /* |
| * 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); |
| } |
| } |