• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.net.ipsec.ike.exceptions.InvalidSyntaxException;
22 import android.util.Pair;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
26 import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
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