• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2018 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 
29 #include "src/core/lib/gpr/useful.h"
30 #include "src/core/tsi/alts/crypt/gsec.h"
31 #include "src/core/tsi/alts/frame_protector/alts_crypter.h"
32 #include "src/core/tsi/alts/frame_protector/frame_handler.h"
33 #include "src/core/tsi/transport_security.h"
34 
35 constexpr size_t kMinFrameLength = 1024;
36 constexpr size_t kDefaultFrameLength = 16 * 1024;
37 constexpr size_t kMaxFrameLength = 1024 * 1024;
38 
39 // Limit k on number of frames such that at most 2^(8 * k) frames can be sent.
40 constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8;
41 constexpr size_t kAltsRecordProtocolFrameLimit = 5;
42 
43 /* Main struct for alts_frame_protector. */
44 struct alts_frame_protector {
45   tsi_frame_protector base;
46   alts_crypter* seal_crypter;
47   alts_crypter* unseal_crypter;
48   alts_frame_writer* writer;
49   alts_frame_reader* reader;
50   unsigned char* in_place_protect_buffer;
51   unsigned char* in_place_unprotect_buffer;
52   size_t in_place_protect_bytes_buffered;
53   size_t in_place_unprotect_bytes_processed;
54   size_t max_protected_frame_size;
55   size_t max_unprotected_frame_size;
56   size_t overhead_length;
57   size_t counter_overflow;
58 };
59 
seal(alts_frame_protector * impl)60 static tsi_result seal(alts_frame_protector* impl) {
61   char* error_details = nullptr;
62   size_t output_size = 0;
63   grpc_status_code status = alts_crypter_process_in_place(
64       impl->seal_crypter, impl->in_place_protect_buffer,
65       impl->max_protected_frame_size, impl->in_place_protect_bytes_buffered,
66       &output_size, &error_details);
67   impl->in_place_protect_bytes_buffered = output_size;
68   if (status != GRPC_STATUS_OK) {
69     gpr_log(GPR_ERROR, "%s", error_details);
70     gpr_free(error_details);
71     return TSI_INTERNAL_ERROR;
72   }
73   return TSI_OK;
74 }
75 
max_encrypted_payload_bytes(alts_frame_protector * impl)76 static size_t max_encrypted_payload_bytes(alts_frame_protector* impl) {
77   return impl->max_protected_frame_size - kFrameHeaderSize;
78 }
79 
alts_protect_flush(tsi_frame_protector * self,unsigned char * protected_output_frames,size_t * protected_output_frames_size,size_t * still_pending_size)80 static tsi_result alts_protect_flush(tsi_frame_protector* self,
81                                      unsigned char* protected_output_frames,
82                                      size_t* protected_output_frames_size,
83                                      size_t* still_pending_size) {
84   if (self == nullptr || protected_output_frames == nullptr ||
85       protected_output_frames_size == nullptr ||
86       still_pending_size == nullptr) {
87     gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect_flush().");
88     return TSI_INVALID_ARGUMENT;
89   }
90   alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
91   /**
92    * If there's nothing to flush (i.e., in_place_protect_buffer is empty),
93    * we're done.
94    */
95   if (impl->in_place_protect_bytes_buffered == 0) {
96     *protected_output_frames_size = 0;
97     *still_pending_size = 0;
98     return TSI_OK;
99   }
100   /**
101    * If a new frame can start being processed, we encrypt the payload and reset
102    * the frame writer to point to in_place_protect_buffer that holds the newly
103    * sealed frame.
104    */
105   if (alts_is_frame_writer_done(impl->writer)) {
106     tsi_result result = seal(impl);
107     if (result != TSI_OK) {
108       return result;
109     }
110     if (!alts_reset_frame_writer(impl->writer, impl->in_place_protect_buffer,
111                                  impl->in_place_protect_bytes_buffered)) {
112       gpr_log(GPR_ERROR, "Couldn't reset frame writer.");
113       return TSI_INTERNAL_ERROR;
114     }
115   }
116   /**
117    * Write the sealed frame as much as possible to protected_output_frames. It's
118    * possible a frame will not be written out completely by a single flush
119    * (i.e., still_pending_size != 0), in which case the flush should be called
120    * iteratively until a complete frame has been written out.
121    */
122   size_t written_frame_bytes = *protected_output_frames_size;
123   if (!alts_write_frame_bytes(impl->writer, protected_output_frames,
124                               &written_frame_bytes)) {
125     gpr_log(GPR_ERROR, "Couldn't write frame bytes.");
126     return TSI_INTERNAL_ERROR;
127   }
128   *protected_output_frames_size = written_frame_bytes;
129   *still_pending_size = alts_get_num_writer_bytes_remaining(impl->writer);
130   /**
131    * If the current frame has been finished processing (i.e., sealed and written
132    * out completely), we empty in_place_protect_buffer.
133    */
134   if (alts_is_frame_writer_done(impl->writer)) {
135     impl->in_place_protect_bytes_buffered = 0;
136   }
137   return TSI_OK;
138 }
139 
alts_protect(tsi_frame_protector * self,const unsigned char * unprotected_bytes,size_t * unprotected_bytes_size,unsigned char * protected_output_frames,size_t * protected_output_frames_size)140 static tsi_result alts_protect(tsi_frame_protector* self,
141                                const unsigned char* unprotected_bytes,
142                                size_t* unprotected_bytes_size,
143                                unsigned char* protected_output_frames,
144                                size_t* protected_output_frames_size) {
145   if (self == nullptr || unprotected_bytes == nullptr ||
146       unprotected_bytes_size == nullptr || protected_output_frames == nullptr ||
147       protected_output_frames_size == nullptr) {
148     gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect().");
149     return TSI_INVALID_ARGUMENT;
150   }
151   alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
152 
153   /**
154    * If more payload can be buffered, we buffer it as much as possible to
155    * in_place_protect_buffer.
156    */
157   if (impl->in_place_protect_bytes_buffered + impl->overhead_length <
158       max_encrypted_payload_bytes(impl)) {
159     size_t bytes_to_buffer = GPR_MIN(*unprotected_bytes_size,
160                                      max_encrypted_payload_bytes(impl) -
161                                          impl->in_place_protect_bytes_buffered -
162                                          impl->overhead_length);
163     *unprotected_bytes_size = bytes_to_buffer;
164     if (bytes_to_buffer > 0) {
165       memcpy(
166           impl->in_place_protect_buffer + impl->in_place_protect_bytes_buffered,
167           unprotected_bytes, bytes_to_buffer);
168       impl->in_place_protect_bytes_buffered += bytes_to_buffer;
169     }
170   } else {
171     *unprotected_bytes_size = 0;
172   }
173   /**
174    * If a full frame has been buffered, we output it. If the first condition
175    * holds, then there exists an unencrypted full frame. If the second
176    * condition holds, then there exists a full frame that has already been
177    * encrypted.
178    */
179   if (max_encrypted_payload_bytes(impl) ==
180           impl->in_place_protect_bytes_buffered + impl->overhead_length ||
181       max_encrypted_payload_bytes(impl) ==
182           impl->in_place_protect_bytes_buffered) {
183     size_t still_pending_size = 0;
184     return alts_protect_flush(self, protected_output_frames,
185                               protected_output_frames_size,
186                               &still_pending_size);
187   } else {
188     *protected_output_frames_size = 0;
189     return TSI_OK;
190   }
191 }
192 
unseal(alts_frame_protector * impl)193 static tsi_result unseal(alts_frame_protector* impl) {
194   char* error_details = nullptr;
195   size_t output_size = 0;
196   grpc_status_code status = alts_crypter_process_in_place(
197       impl->unseal_crypter, impl->in_place_unprotect_buffer,
198       impl->max_unprotected_frame_size,
199       alts_get_output_bytes_read(impl->reader), &output_size, &error_details);
200   if (status != GRPC_STATUS_OK) {
201     gpr_log(GPR_ERROR, "%s", error_details);
202     gpr_free(error_details);
203     return TSI_DATA_CORRUPTED;
204   }
205   return TSI_OK;
206 }
207 
ensure_buffer_size(alts_frame_protector * impl)208 static void ensure_buffer_size(alts_frame_protector* impl) {
209   if (!alts_has_read_frame_length(impl->reader)) {
210     return;
211   }
212   size_t buffer_space_remaining = impl->max_unprotected_frame_size -
213                                   alts_get_output_bytes_read(impl->reader);
214   /**
215    * Check if we need to resize in_place_unprotect_buffer in order to hold
216    * remaining bytes of a full frame.
217    */
218   if (buffer_space_remaining < alts_get_reader_bytes_remaining(impl->reader)) {
219     size_t buffer_len = alts_get_output_bytes_read(impl->reader) +
220                         alts_get_reader_bytes_remaining(impl->reader);
221     unsigned char* buffer = static_cast<unsigned char*>(gpr_malloc(buffer_len));
222     memcpy(buffer, impl->in_place_unprotect_buffer,
223            alts_get_output_bytes_read(impl->reader));
224     impl->max_unprotected_frame_size = buffer_len;
225     gpr_free(impl->in_place_unprotect_buffer);
226     impl->in_place_unprotect_buffer = buffer;
227     alts_reset_reader_output_buffer(
228         impl->reader, buffer + alts_get_output_bytes_read(impl->reader));
229   }
230 }
231 
alts_unprotect(tsi_frame_protector * self,const unsigned char * protected_frames_bytes,size_t * protected_frames_bytes_size,unsigned char * unprotected_bytes,size_t * unprotected_bytes_size)232 static tsi_result alts_unprotect(tsi_frame_protector* self,
233                                  const unsigned char* protected_frames_bytes,
234                                  size_t* protected_frames_bytes_size,
235                                  unsigned char* unprotected_bytes,
236                                  size_t* unprotected_bytes_size) {
237   if (self == nullptr || protected_frames_bytes == nullptr ||
238       protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr ||
239       unprotected_bytes_size == nullptr) {
240     gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_unprotect().");
241     return TSI_INVALID_ARGUMENT;
242   }
243   alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
244   /**
245    * If a new frame can start being processed, we reset the frame reader to
246    * point to in_place_unprotect_buffer that will be used to hold deframed
247    * result.
248    */
249   if (alts_is_frame_reader_done(impl->reader) &&
250       ((alts_get_output_buffer(impl->reader) == nullptr) ||
251        (alts_get_output_bytes_read(impl->reader) ==
252         impl->in_place_unprotect_bytes_processed + impl->overhead_length))) {
253     if (!alts_reset_frame_reader(impl->reader,
254                                  impl->in_place_unprotect_buffer)) {
255       gpr_log(GPR_ERROR, "Couldn't reset frame reader.");
256       return TSI_INTERNAL_ERROR;
257     }
258     impl->in_place_unprotect_bytes_processed = 0;
259   }
260   /**
261    * If a full frame has not yet been read, we read more bytes from
262    * protected_frames_bytes until a full frame has been read. We also need to
263    * make sure in_place_unprotect_buffer is large enough to hold a complete
264    * frame.
265    */
266   if (!alts_is_frame_reader_done(impl->reader)) {
267     ensure_buffer_size(impl);
268     *protected_frames_bytes_size =
269         GPR_MIN(impl->max_unprotected_frame_size -
270                     alts_get_output_bytes_read(impl->reader),
271                 *protected_frames_bytes_size);
272     size_t read_frames_bytes_size = *protected_frames_bytes_size;
273     if (!alts_read_frame_bytes(impl->reader, protected_frames_bytes,
274                                &read_frames_bytes_size)) {
275       gpr_log(GPR_ERROR, "Failed to process frame.");
276       return TSI_INTERNAL_ERROR;
277     }
278     *protected_frames_bytes_size = read_frames_bytes_size;
279   } else {
280     *protected_frames_bytes_size = 0;
281   }
282   /**
283    * If a full frame has been read, we unseal it, and write out the
284    * deframed result to unprotected_bytes.
285    */
286   if (alts_is_frame_reader_done(impl->reader)) {
287     if (impl->in_place_unprotect_bytes_processed == 0) {
288       tsi_result result = unseal(impl);
289       if (result != TSI_OK) {
290         return result;
291       }
292     }
293     size_t bytes_to_write = GPR_MIN(
294         *unprotected_bytes_size, alts_get_output_bytes_read(impl->reader) -
295                                      impl->in_place_unprotect_bytes_processed -
296                                      impl->overhead_length);
297     if (bytes_to_write > 0) {
298       memcpy(unprotected_bytes,
299              impl->in_place_unprotect_buffer +
300                  impl->in_place_unprotect_bytes_processed,
301              bytes_to_write);
302     }
303     *unprotected_bytes_size = bytes_to_write;
304     impl->in_place_unprotect_bytes_processed += bytes_to_write;
305     return TSI_OK;
306   } else {
307     *unprotected_bytes_size = 0;
308     return TSI_OK;
309   }
310 }
311 
alts_destroy(tsi_frame_protector * self)312 static void alts_destroy(tsi_frame_protector* self) {
313   alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self);
314   if (impl != nullptr) {
315     alts_crypter_destroy(impl->seal_crypter);
316     alts_crypter_destroy(impl->unseal_crypter);
317     gpr_free(impl->in_place_protect_buffer);
318     gpr_free(impl->in_place_unprotect_buffer);
319     alts_destroy_frame_writer(impl->writer);
320     alts_destroy_frame_reader(impl->reader);
321     gpr_free(impl);
322   }
323 }
324 
325 static const tsi_frame_protector_vtable alts_frame_protector_vtable = {
326     alts_protect, alts_protect_flush, alts_unprotect, alts_destroy};
327 
create_alts_crypters(const uint8_t * key,size_t key_size,bool is_client,bool is_rekey,alts_frame_protector * impl,char ** error_details)328 static grpc_status_code create_alts_crypters(const uint8_t* key,
329                                              size_t key_size, bool is_client,
330                                              bool is_rekey,
331                                              alts_frame_protector* impl,
332                                              char** error_details) {
333   grpc_status_code status;
334   gsec_aead_crypter* aead_crypter_seal = nullptr;
335   gsec_aead_crypter* aead_crypter_unseal = nullptr;
336   status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
337                                             kAesGcmTagLength, is_rekey,
338                                             &aead_crypter_seal, error_details);
339   if (status != GRPC_STATUS_OK) {
340     return status;
341   }
342   status = gsec_aes_gcm_aead_crypter_create(
343       key, key_size, kAesGcmNonceLength, kAesGcmTagLength, is_rekey,
344       &aead_crypter_unseal, error_details);
345   if (status != GRPC_STATUS_OK) {
346     return status;
347   }
348   size_t overflow_size = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
349                                   : kAltsRecordProtocolFrameLimit;
350   status = alts_seal_crypter_create(aead_crypter_seal, is_client, overflow_size,
351                                     &impl->seal_crypter, error_details);
352   if (status != GRPC_STATUS_OK) {
353     return status;
354   }
355   status =
356       alts_unseal_crypter_create(aead_crypter_unseal, is_client, overflow_size,
357                                  &impl->unseal_crypter, error_details);
358   return status;
359 }
360 
alts_create_frame_protector(const uint8_t * key,size_t key_size,bool is_client,bool is_rekey,size_t * max_protected_frame_size,tsi_frame_protector ** self)361 tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size,
362                                        bool is_client, bool is_rekey,
363                                        size_t* max_protected_frame_size,
364                                        tsi_frame_protector** self) {
365   if (key == nullptr || self == nullptr) {
366     gpr_log(GPR_ERROR,
367             "Invalid nullptr arguments to alts_create_frame_protector().");
368     return TSI_INTERNAL_ERROR;
369   }
370   char* error_details = nullptr;
371   alts_frame_protector* impl =
372       static_cast<alts_frame_protector*>(gpr_zalloc(sizeof(*impl)));
373   grpc_status_code status = create_alts_crypters(
374       key, key_size, is_client, is_rekey, impl, &error_details);
375   if (status != GRPC_STATUS_OK) {
376     gpr_log(GPR_ERROR, "Failed to create ALTS crypters, %s.", error_details);
377     gpr_free(error_details);
378     return TSI_INTERNAL_ERROR;
379   }
380   /**
381    * Set maximum frame size to be used by a frame protector. If it is nullptr, a
382    * default frame size will be used. Otherwise, the provided frame size will be
383    * adjusted (if not falling into a valid frame range) and used.
384    */
385   size_t max_protected_frame_size_to_set = kDefaultFrameLength;
386   if (max_protected_frame_size != nullptr) {
387     *max_protected_frame_size =
388         GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
389     *max_protected_frame_size =
390         GPR_MAX(*max_protected_frame_size, kMinFrameLength);
391     max_protected_frame_size_to_set = *max_protected_frame_size;
392   }
393   impl->max_protected_frame_size = max_protected_frame_size_to_set;
394   impl->max_unprotected_frame_size = max_protected_frame_size_to_set;
395   impl->in_place_protect_bytes_buffered = 0;
396   impl->in_place_unprotect_bytes_processed = 0;
397   impl->in_place_protect_buffer = static_cast<unsigned char*>(
398       gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set));
399   impl->in_place_unprotect_buffer = static_cast<unsigned char*>(
400       gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set));
401   impl->overhead_length = alts_crypter_num_overhead_bytes(impl->seal_crypter);
402   impl->writer = alts_create_frame_writer();
403   impl->reader = alts_create_frame_reader();
404   impl->base.vtable = &alts_frame_protector_vtable;
405   *self = &impl->base;
406   return TSI_OK;
407 }
408