1 /*
2  * Copyright 2018 The gRPC Authors
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 io.grpc.alts.internal;
18 
19 import com.google.common.base.Preconditions;
20 import java.nio.ByteBuffer;
21 import java.nio.ByteOrder;
22 import java.security.GeneralSecurityException;
23 
24 /** Framing and deframing methods and classes used by handshaker. */
25 public final class AltsFraming {
26   // The size of the frame field. Must correspond to the size of int, 4 bytes.
27   // Left package-private for testing.
28   private static final int FRAME_LENGTH_HEADER_SIZE = 4;
29   private static final int FRAME_MESSAGE_TYPE_HEADER_SIZE = 4;
30   private static final int MAX_DATA_LENGTH = 1024 * 1024;
31   private static final int INITIAL_BUFFER_CAPACITY = 1024 * 64;
32 
33   // TODO: Make this the responsibility of the caller.
34   private static final int MESSAGE_TYPE = 6;
35 
AltsFraming()36   private AltsFraming() {}
37 
getFrameLengthHeaderSize()38   static int getFrameLengthHeaderSize() {
39     return FRAME_LENGTH_HEADER_SIZE;
40   }
41 
getFrameMessageTypeHeaderSize()42   static int getFrameMessageTypeHeaderSize() {
43     return FRAME_MESSAGE_TYPE_HEADER_SIZE;
44   }
45 
getMaxDataLength()46   static int getMaxDataLength() {
47     return MAX_DATA_LENGTH;
48   }
49 
getFramingOverhead()50   static int getFramingOverhead() {
51     return FRAME_LENGTH_HEADER_SIZE + FRAME_MESSAGE_TYPE_HEADER_SIZE;
52   }
53 
54   /**
55    * Creates a frame of length dataSize + FRAME_HEADER_SIZE using the input bytes, if dataSize <=
56    * input.remaining(). Otherwise, a frame of length input.remaining() + FRAME_HEADER_SIZE is
57    * created.
58    */
toFrame(ByteBuffer input, int dataSize)59   static ByteBuffer toFrame(ByteBuffer input, int dataSize) throws GeneralSecurityException {
60     Preconditions.checkNotNull(input);
61     if (dataSize > input.remaining()) {
62       dataSize = input.remaining();
63     }
64     Producer producer = new Producer();
65     ByteBuffer inputAlias = input.duplicate();
66     inputAlias.limit(input.position() + dataSize);
67     producer.readBytes(inputAlias);
68     producer.flush();
69     input.position(inputAlias.position());
70     ByteBuffer output = producer.getRawFrame();
71     return output;
72   }
73 
74   /**
75    * A helper class to write a frame.
76    *
77    * <p>This class guarantees that one of the following is true:
78    *
79    * <ul>
80    *   <li>readBytes will read from the input
81    *   <li>writeBytes will write to the output
82    * </ul>
83    *
84    * <p>Sample usage:
85    *
86    * <pre>{@code
87    * Producer producer = new Producer();
88    * ByteBuffer inputBuffer = readBytesFromMyStream();
89    * ByteBuffer outputBuffer = writeBytesToMyStream();
90    * while (inputBuffer.hasRemaining() || outputBuffer.hasRemaining()) {
91    *   producer.readBytes(inputBuffer);
92    *   producer.writeBytes(outputBuffer);
93    * }
94    * }</pre>
95    *
96    * <p>Alternatively, this class guarantees that one of the following is true:
97    *
98    * <ul>
99    *   <li>readBytes will read from the input
100    *   <li>{@code isComplete()} returns true and {@code getByteBuffer()} returns the contents of a
101    *       processed frame.
102    * </ul>
103    *
104    * <p>Sample usage:
105    *
106    * <pre>{@code
107    * Producer producer = new Producer();
108    * while (!producer.isComplete()) {
109    *   ByteBuffer inputBuffer = readBytesFromMyStream();
110    *   producer.readBytes(inputBuffer);
111    * }
112    * producer.flush();
113    * ByteBuffer outputBuffer = producer.getRawFrame();
114    * }</pre>
115    */
116   static final class Producer {
117     private ByteBuffer buffer;
118     private boolean isComplete;
119 
Producer(int maxFrameSize)120     Producer(int maxFrameSize) {
121       buffer = ByteBuffer.allocate(maxFrameSize);
122       reset();
123       Preconditions.checkArgument(maxFrameSize > getFramePrefixLength() + getFrameSuffixLength());
124     }
125 
Producer()126     Producer() {
127       this(INITIAL_BUFFER_CAPACITY);
128     }
129 
130     /** The length of the frame prefix data, including the message length/type fields. */
getFramePrefixLength()131     int getFramePrefixLength() {
132       int result = FRAME_LENGTH_HEADER_SIZE + FRAME_MESSAGE_TYPE_HEADER_SIZE;
133       return result;
134     }
135 
getFrameSuffixLength()136     int getFrameSuffixLength() {
137       return 0;
138     }
139 
140     /**
141      * Reads bytes from input, parsing them into a frame. Returns false if and only if more data is
142      * needed. To obtain a full frame this method must be called repeatedly until it returns true.
143      */
readBytes(ByteBuffer input)144     boolean readBytes(ByteBuffer input) throws GeneralSecurityException {
145       Preconditions.checkNotNull(input);
146       if (isComplete) {
147         return true;
148       }
149       copy(buffer, input);
150       if (!buffer.hasRemaining()) {
151         flush();
152       }
153       return isComplete;
154     }
155 
156     /**
157      * Completes the current frame, signaling that no further data is available to be passed to
158      * readBytes and that the client requires writeBytes to start returning data. isComplete() is
159      * guaranteed to return true after this call.
160      */
flush()161     void flush() throws GeneralSecurityException {
162       if (isComplete) {
163         return;
164       }
165       // Get the length of the complete frame.
166       int frameLength = buffer.position() + getFrameSuffixLength();
167 
168       // Set the limit and move to the start.
169       buffer.flip();
170 
171       // Advance the limit to allow a crypto suffix.
172       buffer.limit(buffer.limit() + getFrameSuffixLength());
173 
174       // Write the data length and the message type.
175       int dataLength = frameLength - FRAME_LENGTH_HEADER_SIZE;
176       buffer.order(ByteOrder.LITTLE_ENDIAN);
177       buffer.putInt(dataLength);
178       buffer.putInt(MESSAGE_TYPE);
179 
180       // Move the position back to 0, the frame is ready.
181       buffer.position(0);
182       isComplete = true;
183     }
184 
185     /** Resets the state, preparing to construct a new frame. Must be called between frames. */
reset()186     private void reset() {
187       buffer.clear();
188 
189       // Save some space for framing, we'll fill that in later.
190       buffer.position(getFramePrefixLength());
191       buffer.limit(buffer.limit() - getFrameSuffixLength());
192 
193       isComplete = false;
194     }
195 
196     /**
197      * Returns a ByteBuffer containing a complete raw frame, if it's available. Should only be
198      * called when isComplete() returns true, otherwise null is returned. The returned object
199      * aliases the internal buffer, that is, it shares memory with the internal buffer. No further
200      * operations are permitted on this object until the caller has processed the data it needs from
201      * the returned byte buffer.
202      */
getRawFrame()203     ByteBuffer getRawFrame() {
204       if (!isComplete) {
205         return null;
206       }
207       ByteBuffer result = buffer.duplicate();
208       reset();
209       return result;
210     }
211   }
212 
213   /**
214    * A helper class to read a frame.
215    *
216    * <p>This class guarantees that one of the following is true:
217    *
218    * <ul>
219    *   <li>readBytes will read from the input
220    *   <li>writeBytes will write to the output
221    * </ul>
222    *
223    * <p>Sample usage:
224    *
225    * <pre>{@code
226    * Parser parser = new Parser();
227    * ByteBuffer inputBuffer = readBytesFromMyStream();
228    * ByteBuffer outputBuffer = writeBytesToMyStream();
229    * while (inputBuffer.hasRemaining() || outputBuffer.hasRemaining()) {
230    *   parser.readBytes(inputBuffer);
231    *   parser.writeBytes(outputBuffer); }
232    * }</pre>
233    *
234    * <p>Alternatively, this class guarantees that one of the following is true:
235    *
236    * <ul>
237    *   <li>readBytes will read from the input
238    *   <li>{@code isComplete()} returns true and {@code getByteBuffer()} returns the contents of a
239    *       processed frame.
240    * </ul>
241    *
242    * <p>Sample usage:
243    *
244    * <pre>{@code
245    * Parser parser = new Parser();
246    * while (!parser.isComplete()) {
247    *   ByteBuffer inputBuffer = readBytesFromMyStream();
248    *   parser.readBytes(inputBuffer);
249    * }
250    * ByteBuffer outputBuffer = parser.getRawFrame();
251    * }</pre>
252    */
253   public static final class Parser {
254     private ByteBuffer buffer = ByteBuffer.allocate(INITIAL_BUFFER_CAPACITY);
255     private boolean isComplete = false;
256 
Parser()257     public Parser() {
258       Preconditions.checkArgument(
259           INITIAL_BUFFER_CAPACITY > getFramePrefixLength() + getFrameSuffixLength());
260     }
261 
262     /**
263      * Reads bytes from input, parsing them into a frame. Returns false if and only if more data is
264      * needed. To obtain a full frame this method must be called repeatedly until it returns true.
265      */
readBytes(ByteBuffer input)266     public boolean readBytes(ByteBuffer input) throws GeneralSecurityException {
267       Preconditions.checkNotNull(input);
268 
269       if (isComplete) {
270         return true;
271       }
272 
273       // Read enough bytes to determine the length
274       while (buffer.position() < FRAME_LENGTH_HEADER_SIZE && input.hasRemaining()) {
275         buffer.put(input.get());
276       }
277 
278       // If we have enough bytes to determine the length, read the length and ensure that our
279       // internal buffer is large enough.
280       if (buffer.position() == FRAME_LENGTH_HEADER_SIZE && input.hasRemaining()) {
281         ByteBuffer bufferAlias = buffer.duplicate();
282         bufferAlias.flip();
283         bufferAlias.order(ByteOrder.LITTLE_ENDIAN);
284         int dataLength = bufferAlias.getInt();
285         if (dataLength < FRAME_MESSAGE_TYPE_HEADER_SIZE || dataLength > MAX_DATA_LENGTH) {
286           throw new IllegalArgumentException("Invalid frame length " + dataLength);
287         }
288         // Maybe resize the buffer
289         int frameLength = dataLength + FRAME_LENGTH_HEADER_SIZE;
290         if (buffer.capacity() < frameLength) {
291           buffer = ByteBuffer.allocate(frameLength);
292           buffer.order(ByteOrder.LITTLE_ENDIAN);
293           buffer.putInt(dataLength);
294         }
295         buffer.limit(frameLength);
296       }
297 
298       // TODO: Similarly extract and check message type.
299 
300       // Read the remaining data into the internal buffer.
301       copy(buffer, input);
302       if (!buffer.hasRemaining()) {
303         buffer.flip();
304         isComplete = true;
305       }
306       return isComplete;
307     }
308 
309     /** The length of the frame prefix data, including the message length/type fields. */
getFramePrefixLength()310     int getFramePrefixLength() {
311       int result = FRAME_LENGTH_HEADER_SIZE + FRAME_MESSAGE_TYPE_HEADER_SIZE;
312       return result;
313     }
314 
getFrameSuffixLength()315     int getFrameSuffixLength() {
316       return 0;
317     }
318 
319     /** Returns true if we've parsed a complete frame. */
isComplete()320     public boolean isComplete() {
321       return isComplete;
322     }
323 
324     /** Resets the state, preparing to parse a new frame. Must be called between frames. */
reset()325     private void reset() {
326       buffer.clear();
327       isComplete = false;
328     }
329 
330     /**
331      * Returns a ByteBuffer containing a complete raw frame, if it's available. Should only be
332      * called when isComplete() returns true, otherwise null is returned. The returned object
333      * aliases the internal buffer, that is, it shares memory with the internal buffer. No further
334      * operations are permitted on this object until the caller has processed the data it needs from
335      * the returned byte buffer.
336      */
getRawFrame()337     public ByteBuffer getRawFrame() {
338       if (!isComplete) {
339         return null;
340       }
341       ByteBuffer result = buffer.duplicate();
342       reset();
343       return result;
344     }
345   }
346 
347   /**
348    * Copy as much as possible to dst from src. Unlike {@link ByteBuffer#put(ByteBuffer)}, this stops
349    * early if there is no room left in dst.
350    */
copy(ByteBuffer dst, ByteBuffer src)351   private static void copy(ByteBuffer dst, ByteBuffer src) {
352     if (dst.hasRemaining() && src.hasRemaining()) {
353       // Avoid an allocation if possible.
354       if (dst.remaining() >= src.remaining()) {
355         dst.put(src);
356       } else {
357         int count = Math.min(dst.remaining(), src.remaining());
358         ByteBuffer slice = src.slice();
359         slice.limit(count);
360         dst.put(slice);
361         src.position(src.position() + count);
362       }
363     }
364   }
365 }
366