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