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 android.annotation.Nullable; 20 import android.net.ipsec.ike.exceptions.IkeProtocolException; 21 import android.util.Pair; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.internal.net.ipsec.ike.crypto.IkeCipher; 25 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity; 26 import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException; 27 28 import java.nio.ByteBuffer; 29 import java.security.GeneralSecurityException; 30 31 /** 32 * IkePayloadFactory is used for creating IkePayload according to is type. 33 * 34 * @see <a href="https://tools.ietf.org/html/rfc7296#section-3">RFC 7296, Internet Key Exchange 35 * Protocol Version 2 (IKEv2)</a> 36 */ 37 final class IkePayloadFactory { 38 39 // Critical bit is set and following reserved 7 bits are unset. 40 private static final byte PAYLOAD_HEADER_CRITICAL_BIT_SET = (byte) 0x80; 41 isCriticalPayload(byte flagByte)42 private static boolean isCriticalPayload(byte flagByte) { 43 // Reserved 7 bits following critical bit must be ignore on receipt. 44 return (flagByte & PAYLOAD_HEADER_CRITICAL_BIT_SET) == PAYLOAD_HEADER_CRITICAL_BIT_SET; 45 } 46 47 /** Default IIkePayloadDecoder instance used for constructing IkePayload */ 48 static IIkePayloadDecoder sDecoderInstance = new IkePayloadDecoder(); 49 50 /** 51 * IkePayloadDecoder implements IIkePayloadDecoder for constructing IkePayload from decoding 52 * received message. 53 * 54 * <p>Package private 55 */ 56 @VisibleForTesting 57 static class IkePayloadDecoder implements IIkePayloadDecoder { 58 @Override decodeIkePayload( int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody)59 public IkePayload decodeIkePayload( 60 int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody) 61 throws IkeProtocolException { 62 switch (payloadType) { 63 case IkePayload.PAYLOAD_TYPE_SA: 64 return new IkeSaPayload(isCritical, isResp, payloadBody); 65 case IkePayload.PAYLOAD_TYPE_KE: 66 return new IkeKePayload(isCritical, payloadBody); 67 case IkePayload.PAYLOAD_TYPE_ID_INITIATOR: 68 return new IkeIdPayload(isCritical, payloadBody, true); 69 case IkePayload.PAYLOAD_TYPE_ID_RESPONDER: 70 return new IkeIdPayload(isCritical, payloadBody, false); 71 case IkePayload.PAYLOAD_TYPE_CERT: 72 return IkeCertPayload.getIkeCertPayload(isCritical, payloadBody); 73 case IkeCertReqPayload.PAYLOAD_TYPE_CERT_REQUEST: 74 return new IkeCertReqPayload(isCritical, payloadBody); 75 case IkePayload.PAYLOAD_TYPE_AUTH: 76 return IkeAuthPayload.getIkeAuthPayload(isCritical, payloadBody); 77 case IkePayload.PAYLOAD_TYPE_NONCE: 78 return new IkeNoncePayload(isCritical, payloadBody); 79 case IkePayload.PAYLOAD_TYPE_NOTIFY: 80 return new IkeNotifyPayload(isCritical, payloadBody); 81 case IkePayload.PAYLOAD_TYPE_DELETE: 82 return new IkeDeletePayload(isCritical, payloadBody); 83 case IkePayload.PAYLOAD_TYPE_VENDOR: 84 return new IkeVendorPayload(isCritical, payloadBody); 85 case IkePayload.PAYLOAD_TYPE_TS_INITIATOR: 86 return new IkeTsPayload(isCritical, payloadBody, true); 87 case IkePayload.PAYLOAD_TYPE_TS_RESPONDER: 88 return new IkeTsPayload(isCritical, payloadBody, false); 89 case IkePayload.PAYLOAD_TYPE_CP: 90 return new IkeConfigPayload(isCritical, payloadBody); 91 case IkePayload.PAYLOAD_TYPE_EAP: 92 return new IkeEapPayload(isCritical, payloadBody); 93 default: 94 return new IkeUnsupportedPayload(payloadType, isCritical); 95 } 96 } 97 98 @Override decodeIkeSkPayload( boolean isSkf, boolean critical, byte[] message, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, byte[] integrityKey, byte[] decryptionKey)99 public IkeSkPayload decodeIkeSkPayload( 100 boolean isSkf, 101 boolean critical, 102 byte[] message, 103 @Nullable IkeMacIntegrity integrityMac, 104 IkeCipher decryptCipher, 105 byte[] integrityKey, 106 byte[] decryptionKey) 107 throws IkeProtocolException, GeneralSecurityException { 108 if (isSkf) { 109 return new IkeSkfPayload( 110 critical, 111 message, 112 integrityMac, 113 decryptCipher, 114 integrityKey, 115 decryptionKey); 116 } else { 117 return new IkeSkPayload( 118 critical, 119 message, 120 integrityMac, 121 decryptCipher, 122 integrityKey, 123 decryptionKey); 124 } 125 } 126 } 127 128 /** 129 * Construct an instance of IkePayload according to its payload type. 130 * 131 * @param payloadType the current payload type. All supported types will fall in {@link 132 * IkePayload.PayloadType} 133 * @param input the encoded IKE message body containing all payloads. Position of it will 134 * increment. 135 * @return a Pair including IkePayload and next payload type. 136 */ getIkePayload( int payloadType, boolean isResp, ByteBuffer input)137 protected static Pair<IkePayload, Integer> getIkePayload( 138 int payloadType, boolean isResp, ByteBuffer input) throws IkeProtocolException { 139 int nextPayloadType = (int) input.get(); 140 // read critical bit 141 boolean isCritical = isCriticalPayload(input.get()); 142 143 int payloadLength = Short.toUnsignedInt(input.getShort()); 144 if (payloadLength <= IkePayload.GENERIC_HEADER_LENGTH) { 145 throw new InvalidSyntaxException( 146 "Invalid Payload Length: Payload length is too short."); 147 } 148 int bodyLength = payloadLength - IkePayload.GENERIC_HEADER_LENGTH; 149 if (bodyLength > input.remaining()) { 150 // It is not clear whether previous payloads or current payload has invalid payload 151 // length. 152 throw new InvalidSyntaxException("Invalid Payload Length: Payload length is too long."); 153 } 154 byte[] payloadBody = new byte[bodyLength]; 155 input.get(payloadBody); 156 157 IkePayload payload = 158 sDecoderInstance.decodeIkePayload(payloadType, isCritical, isResp, payloadBody); 159 return new Pair(payload, nextPayloadType); 160 } 161 162 /** 163 * Construct an instance of IkeSkPayload by decrypting the received message. 164 * 165 * @param isSkf indicates if this is a SKF Payload. 166 * @param message the byte array contains the whole IKE message. 167 * @param integrityMac the negotiated integrity algorithm. 168 * @param decryptCipher the negotiated encryption algorithm. 169 * @param integrityKey the negotiated integrity algorithm key. 170 * @param decryptionKey the negotiated decryption key. 171 * @return a pair including IkePayload and next payload type. 172 * @throws IkeProtocolException for decoding errors. 173 * @throws GeneralSecurityException if there is any error during integrity check or decryption. 174 */ getIkeSkPayload( boolean isSkf, byte[] message, IkeMacIntegrity integrityMac, IkeCipher decryptCipher, byte[] integrityKey, byte[] decryptionKey)175 protected static Pair<IkeSkPayload, Integer> getIkeSkPayload( 176 boolean isSkf, 177 byte[] message, 178 IkeMacIntegrity integrityMac, 179 IkeCipher decryptCipher, 180 byte[] integrityKey, 181 byte[] decryptionKey) 182 throws IkeProtocolException, GeneralSecurityException { 183 ByteBuffer input = 184 ByteBuffer.wrap( 185 message, 186 IkeHeader.IKE_HEADER_LENGTH, 187 message.length - IkeHeader.IKE_HEADER_LENGTH); 188 189 int nextPayloadType = (int) input.get(); 190 // read critical bit 191 boolean isCritical = isCriticalPayload(input.get()); 192 193 int payloadLength = Short.toUnsignedInt(input.getShort()); 194 195 int bodyLength = message.length - IkeHeader.IKE_HEADER_LENGTH; 196 if (bodyLength < payloadLength) { 197 throw new InvalidSyntaxException( 198 "Invalid length of SK Payload: Payload length is too long."); 199 } else if (bodyLength > payloadLength) { 200 // According to RFC 7296, SK Payload must be the last payload and for CREATE_CHILD_SA, 201 // IKE_AUTH and INFORMATIONAL exchanges, message following the header is encrypted. Thus 202 // this implementaion only accepts that SK Payload to be the only payload. Any IKE 203 // packet violating this format will be treated as invalid. A request violating this 204 // format will be rejected and replied with an error notification. 205 throw new InvalidSyntaxException( 206 "Invalid length of SK Payload: Payload length is too short" 207 + " or SK Payload is not the only payload."); 208 } 209 210 IkeSkPayload payload = 211 sDecoderInstance.decodeIkeSkPayload( 212 isSkf, 213 isCritical, 214 message, 215 integrityMac, 216 decryptCipher, 217 integrityKey, 218 decryptionKey); 219 220 return new Pair(payload, nextPayloadType); 221 } 222 223 /** 224 * IIkePayloadDecoder provides a package private interface for constructing IkePayload from 225 * decoding received message. 226 * 227 * <p>IIkePayloadDecoder exists so that the interface is injectable for testing. 228 */ 229 @VisibleForTesting 230 interface IIkePayloadDecoder { decodeIkePayload( int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody)231 IkePayload decodeIkePayload( 232 int payloadType, boolean isCritical, boolean isResp, byte[] payloadBody) 233 throws IkeProtocolException; 234 decodeIkeSkPayload( boolean isSkf, boolean critical, byte[] message, @Nullable IkeMacIntegrity integrityMac, IkeCipher decryptCipher, byte[] integrityKey, byte[] decryptionKey)235 IkeSkPayload decodeIkeSkPayload( 236 boolean isSkf, 237 boolean critical, 238 byte[] message, 239 @Nullable IkeMacIntegrity integrityMac, 240 IkeCipher decryptCipher, 241 byte[] integrityKey, 242 byte[] decryptionKey) 243 throws IkeProtocolException, GeneralSecurityException; 244 } 245 } 246