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