• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.eap.message.ttls;
18 
19 import static com.android.internal.net.eap.EapAuthenticator.LOG;
20 
21 import com.android.internal.annotations.VisibleForTesting;
22 import com.android.internal.net.eap.EapResult.EapError;
23 import com.android.internal.net.eap.exceptions.ttls.EapTtlsParsingException;
24 import com.android.internal.net.eap.message.EapMessage;
25 
26 import java.nio.BufferUnderflowException;
27 import java.nio.ByteBuffer;
28 
29 /**
30  * EapTtlsTypeData represents the type data for an {@link EapMessage} during an EAP-TTLS session.
31  * The structure of the flag byte is as follows:
32  *
33  * <pre>
34  * |---+---+---+---+---+-------+
35  * | 0 | 1 | 2 | 3 | 4 | 5 6 7 |
36  * | L | M | S | R | R |   V   |
37  * |---+---+---+---+---+-------+
38  * L = Message length is included
39  * M = More fragments incoming
40  * S = Start
41  * R = Reserved
42  * V = Version
43  * </pre>
44  *
45  * @see <a href="https://tools.ietf.org/html/rfc5281">RFC 5281, Extensible Authentication Protocol
46  *     Tunneled Transport Layer Security Authenticated Protocol Version 0 (EAP-TTLSv0)</a>
47  */
48 public class EapTtlsTypeData {
49     private static final String TAG = EapTtlsTypeData.class.getSimpleName();
50 
51     /*
52      * Used to extract bits from the flag byte as well as set them. Flag defined via:
53      * https://tools.ietf.org/html/rfc5281#section-9.1
54      * Note that unlike the flag diagram, the length included field is treated as the
55      * most significant bit
56      */
57     private static final int FLAG_LENGTH_INCLUDED = 1 << 7;
58     private static final int FLAG_PACKET_FRAGMENTED = 1 << 6;
59     private static final int FLAG_START = 1 << 5;
60     // used to extract the lower 3 bits from the flag byte
61     private static final int FLAG_VERSION_MASK = 0x07;
62 
63     private static final int FLAGS_LEN_BYTES = 1;
64     private static final int MESSAGE_LENGTH_LEN_BYTES = 4;
65 
66     private static final int SUPPORTED_EAP_TTLS_VERSION = 0;
67     private static final int LEN_NOT_INCLUDED = 0;
68 
69     public final boolean isLengthIncluded;
70     public final boolean isStart;
71     public final boolean isDataFragmented;
72     public final int version;
73     public final int messageLength;
74     public byte[] data;
75 
76     // Package-private
EapTtlsTypeData(ByteBuffer buffer)77     EapTtlsTypeData(ByteBuffer buffer) throws EapTtlsParsingException {
78         byte flags = buffer.get();
79         isLengthIncluded = (flags & FLAG_LENGTH_INCLUDED) != 0;
80         isDataFragmented = (flags & FLAG_PACKET_FRAGMENTED) != 0;
81         isStart = (flags & FLAG_START) != 0;
82         version = (flags & FLAG_VERSION_MASK);
83 
84         messageLength = isLengthIncluded ? buffer.getInt() : 0;
85         data = new byte[buffer.remaining()];
86         buffer.get(data);
87 
88         if (!isDataFragmented && isLengthIncluded && data.length != messageLength) {
89             throw new EapTtlsParsingException(
90                     "Received an unfragmented packet with message length not equal to payload");
91         }
92     }
93 
EapTtlsTypeData( boolean isDataFragmented, boolean isStart, int version, int messageLength, byte[] data)94     private EapTtlsTypeData(
95             boolean isDataFragmented, boolean isStart, int version, int messageLength, byte[] data)
96             throws EapTtlsParsingException {
97         this.isLengthIncluded = messageLength != LEN_NOT_INCLUDED;
98         this.isDataFragmented = isDataFragmented;
99         this.isStart = isStart;
100         if (version != SUPPORTED_EAP_TTLS_VERSION) {
101             throw new EapTtlsParsingException("Unsupported version number: " + version);
102         }
103         this.version = version;
104         this.messageLength = messageLength;
105         this.data = data;
106 
107         if (!isDataFragmented && isLengthIncluded && data.length != messageLength) {
108             throw new EapTtlsParsingException(
109                     "Received an unfragmented packet with message length not equal to payload");
110         }
111     }
112     /**
113      * Assembles each bit from the flag byte into a byte
114      *
115      * @return a byte that compromises the EAP-TTLS flags
116      */
getFlagByte()117     private byte getFlagByte() {
118         return (byte)
119                 ((isLengthIncluded ? FLAG_LENGTH_INCLUDED : 0)
120                         | (isDataFragmented ? FLAG_PACKET_FRAGMENTED : 0)
121                         | (isStart ? FLAG_START : 0)
122                         | (version));
123     }
124 
125     /**
126      * Determines if the type data represents an acknowledgment packet (RFC5281#9.2.3)
127      *
128      * @return true if it is an ack
129      */
isAcknowledgmentPacket()130     public boolean isAcknowledgmentPacket() {
131         return data.length == 0 && !isStart && !isLengthIncluded && !isDataFragmented;
132     }
133 
134     /**
135      * Constructs and returns new EAP-TTLS response type data.
136      *
137      * @param packetFragmented a boolean that indicates whether this is a fragmented message
138      * @param start indicates if the start bit should be set
139      * @param version the EAP-TTLS version number
140      * @param messageLength an optional field to indicate the raw length of the data field prior to
141      *     fragmentation
142      * @param data the raw tls message sequence
143      * @return an EapTtlsTypeData or null if the packet configuration is invalid
144      */
getEapTtlsTypeData( boolean packetFragmented, boolean start, int version, int messageLength, byte[] data)145     public static EapTtlsTypeData getEapTtlsTypeData(
146             boolean packetFragmented, boolean start, int version, int messageLength, byte[] data) {
147         try {
148             return new EapTtlsTypeData(packetFragmented, start, version, messageLength, data);
149         } catch (EapTtlsParsingException e) {
150             LOG.e(TAG, "Parsing exception thrown while attempting to create an EapTtlsTypeData");
151             return null;
152         }
153     }
154 
155     /**
156      * Encodes this EapTtlsTypeData instance as a byte[].
157      *
158      * @return byte[] representing the encoded value of this EapTtlsTypeData instance
159      */
encode()160     public byte[] encode() {
161         int msgLen = isLengthIncluded ? MESSAGE_LENGTH_LEN_BYTES : 0;
162         int bufferSize = data.length + FLAGS_LEN_BYTES + msgLen;
163         ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
164         buffer.put(getFlagByte());
165         if (isLengthIncluded) {
166             buffer.putInt(messageLength);
167         }
168         buffer.put(data);
169         return buffer.array();
170     }
171 
172     /** EapTtlsAcknowledgement represents an EapTtls ack response (EAP-TTLS#9.2.3) */
173     public static class EapTtlsAcknowledgement extends EapTtlsTypeData {
174         private static final String TAG = EapTtlsAcknowledgement.class.getSimpleName();
175 
176         @VisibleForTesting
EapTtlsAcknowledgement()177         public EapTtlsAcknowledgement() throws EapTtlsParsingException {
178             super(
179                     false /* no fragmentation */,
180                     false /* not start */,
181                     0 /* version */,
182                     0 /* length */,
183                     new byte[0] /* no data */);
184         }
185 
186         /**
187          * Constructs and returns a new EAP-TTLS acknowledgement type data.
188          *
189          * @return a new EapTtlsAcknowledgement instance
190          */
getEapTtlsAcknowledgement()191         public static EapTtlsAcknowledgement getEapTtlsAcknowledgement() {
192             try {
193                 return new EapTtlsAcknowledgement();
194             } catch (EapTtlsParsingException e) {
195                 // This should never happen
196                 LOG.e(
197                         TAG,
198                         "Parsing exception thrown while attempting"
199                                 + "to create an acknowledgement packet");
200                 return null;
201             }
202         }
203     }
204 
205     /** EapTtlsTypeDataDecoder will be used for decoding {@link EapTtlsTypeData} objects. */
206     public static class EapTtlsTypeDataDecoder {
207 
208         /**
209          * Decodes and returns an EapTtlsTypeData for the specified eapTypeData.
210          *
211          * @param eapTypeData byte[] to be decoded as an EapTtlsTypeData instance
212          * @return DecodeResult wrapping an EapTtlsTypeData instance for the given eapTypeData iff
213          *     the eapTypeData is formatted correctly. Otherwise, the DecodeResult wraps the
214          *     appropriate EapError.
215          */
decodeEapTtlsRequestPacket(byte[] eapTypeData)216         public DecodeResult decodeEapTtlsRequestPacket(byte[] eapTypeData) {
217             try {
218                 ByteBuffer buffer = ByteBuffer.wrap(eapTypeData);
219                 return new DecodeResult(new EapTtlsTypeData(buffer));
220             } catch (BufferUnderflowException | EapTtlsParsingException e) {
221                 return new DecodeResult(new EapError(e));
222             }
223         }
224 
225         /**
226          * DecodeResult represents the result from calling a decode method within
227          * EapTtlsTypeDataDecoder. It will contain either an EapTtlsTypeData or an EapError.
228          */
229         public static class DecodeResult {
230             public final EapTtlsTypeData eapTypeData;
231             public final EapError eapError;
232 
DecodeResult(EapTtlsTypeData eapTypeData)233             public DecodeResult(EapTtlsTypeData eapTypeData) {
234                 this.eapTypeData = eapTypeData;
235                 this.eapError = null;
236             }
237 
DecodeResult(EapError eapError)238             public DecodeResult(EapError eapError) {
239                 this.eapTypeData = null;
240                 this.eapError = eapError;
241             }
242 
243             /**
244              * Checks whether this instance represents a successful decode operation.
245              *
246              * @return true iff this DecodeResult represents a successfully decoded Type Data
247              */
isSuccessfulDecode()248             public boolean isSuccessfulDecode() {
249                 return eapTypeData != null;
250             }
251         }
252     }
253 }
254