• 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/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
22 
23 #include <string.h>
24 
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/log.h>
27 
28 #include "src/core/lib/gpr/useful.h"
29 #include "src/core/lib/iomgr/exec_ctx.h"
30 #include "src/core/lib/slice/slice_internal.h"
31 #include "src/core/tsi/alts/crypt/gsec.h"
32 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h"
33 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h"
34 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h"
35 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h"
36 #include "src/core/tsi/transport_security_grpc.h"
37 
38 constexpr size_t kMinFrameLength = 1024;
39 constexpr size_t kDefaultFrameLength = 16 * 1024;
40 constexpr size_t kMaxFrameLength = 1024 * 1024;
41 
42 /**
43  * Main struct for alts_zero_copy_grpc_protector.
44  * We choose to have two alts_grpc_record_protocol objects and two sets of slice
45  * buffers: one for protect and the other for unprotect, so that protect and
46  * unprotect can be executed in parallel. Implementations of this object must be
47  * thread compatible.
48  */
49 typedef struct alts_zero_copy_grpc_protector {
50   tsi_zero_copy_grpc_protector base;
51   alts_grpc_record_protocol* record_protocol;
52   alts_grpc_record_protocol* unrecord_protocol;
53   size_t max_protected_frame_size;
54   size_t max_unprotected_data_size;
55   grpc_slice_buffer unprotected_staging_sb;
56   grpc_slice_buffer protected_sb;
57   grpc_slice_buffer protected_staging_sb;
58   uint32_t parsed_frame_size;
59 } alts_zero_copy_grpc_protector;
60 
61 /**
62  * Given a slice buffer, parses the first 4 bytes little-endian unsigned frame
63  * size and returns the total frame size including the frame field. Caller
64  * needs to make sure the input slice buffer has at least 4 bytes. Returns true
65  * on success and false on failure.
66  */
read_frame_size(const grpc_slice_buffer * sb,uint32_t * total_frame_size)67 static bool read_frame_size(const grpc_slice_buffer* sb,
68                             uint32_t* total_frame_size) {
69   if (sb == nullptr || sb->length < kZeroCopyFrameLengthFieldSize) {
70     return false;
71   }
72   uint8_t frame_size_buffer[kZeroCopyFrameLengthFieldSize];
73   uint8_t* buf = frame_size_buffer;
74   /* Copies the first 4 bytes to a temporary buffer.  */
75   size_t remaining = kZeroCopyFrameLengthFieldSize;
76   for (size_t i = 0; i < sb->count; i++) {
77     size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]);
78     if (remaining <= slice_length) {
79       memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining);
80       remaining = 0;
81       break;
82     } else {
83       memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length);
84       buf += slice_length;
85       remaining -= slice_length;
86     }
87   }
88   GPR_ASSERT(remaining == 0);
89   /* Gets little-endian frame size.  */
90   uint32_t frame_size = (((uint32_t)frame_size_buffer[3]) << 24) |
91                         (((uint32_t)frame_size_buffer[2]) << 16) |
92                         (((uint32_t)frame_size_buffer[1]) << 8) |
93                         ((uint32_t)frame_size_buffer[0]);
94   if (frame_size > kMaxFrameLength) {
95     gpr_log(GPR_ERROR, "Frame size is larger than maximum frame size");
96     return false;
97   }
98   /* Returns frame size including frame length field.  */
99   *total_frame_size =
100       static_cast<uint32_t>(frame_size + kZeroCopyFrameLengthFieldSize);
101   return true;
102 }
103 
104 /**
105  * Creates an alts_grpc_record_protocol object, given key, key size, and flags
106  * to indicate whether the record_protocol object uses the rekeying AEAD,
107  * whether the object is for client or server, whether the object is for
108  * integrity-only or privacy-integrity mode, and whether the object is is used
109  * for protect or unprotect.
110  */
create_alts_grpc_record_protocol(const uint8_t * key,size_t key_size,bool is_rekey,bool is_client,bool is_integrity_only,bool is_protect,bool enable_extra_copy,alts_grpc_record_protocol ** record_protocol)111 static tsi_result create_alts_grpc_record_protocol(
112     const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
113     bool is_integrity_only, bool is_protect, bool enable_extra_copy,
114     alts_grpc_record_protocol** record_protocol) {
115   if (key == nullptr || record_protocol == nullptr) {
116     return TSI_INVALID_ARGUMENT;
117   }
118   grpc_status_code status;
119   gsec_aead_crypter* crypter = nullptr;
120   char* error_details = nullptr;
121   status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength,
122                                             kAesGcmTagLength, is_rekey,
123                                             &crypter, &error_details);
124   if (status != GRPC_STATUS_OK) {
125     gpr_log(GPR_ERROR, "Failed to create AEAD crypter, %s", error_details);
126     gpr_free(error_details);
127     return TSI_INTERNAL_ERROR;
128   }
129   size_t overflow_limit = is_rekey ? kAltsRecordProtocolRekeyFrameLimit
130                                    : kAltsRecordProtocolFrameLimit;
131   /* Creates alts_grpc_record_protocol with AEAD crypter ownership transferred.
132    */
133   tsi_result result = is_integrity_only
134                           ? alts_grpc_integrity_only_record_protocol_create(
135                                 crypter, overflow_limit, is_client, is_protect,
136                                 enable_extra_copy, record_protocol)
137                           : alts_grpc_privacy_integrity_record_protocol_create(
138                                 crypter, overflow_limit, is_client, is_protect,
139                                 record_protocol);
140   if (result != TSI_OK) {
141     gsec_aead_crypter_destroy(crypter);
142     return result;
143   }
144   return TSI_OK;
145 }
146 
147 /* --- tsi_zero_copy_grpc_protector methods implementation. --- */
148 
alts_zero_copy_grpc_protector_protect(tsi_zero_copy_grpc_protector * self,grpc_slice_buffer * unprotected_slices,grpc_slice_buffer * protected_slices)149 static tsi_result alts_zero_copy_grpc_protector_protect(
150     tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices,
151     grpc_slice_buffer* protected_slices) {
152   if (self == nullptr || unprotected_slices == nullptr ||
153       protected_slices == nullptr) {
154     gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect.");
155     return TSI_INVALID_ARGUMENT;
156   }
157   alts_zero_copy_grpc_protector* protector =
158       reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
159   /* Calls alts_grpc_record_protocol protect repeatly.  */
160   while (unprotected_slices->length > protector->max_unprotected_data_size) {
161     grpc_slice_buffer_move_first(unprotected_slices,
162                                  protector->max_unprotected_data_size,
163                                  &protector->unprotected_staging_sb);
164     tsi_result status = alts_grpc_record_protocol_protect(
165         protector->record_protocol, &protector->unprotected_staging_sb,
166         protected_slices);
167     if (status != TSI_OK) {
168       return status;
169     }
170   }
171   return alts_grpc_record_protocol_protect(
172       protector->record_protocol, unprotected_slices, protected_slices);
173 }
174 
alts_zero_copy_grpc_protector_unprotect(tsi_zero_copy_grpc_protector * self,grpc_slice_buffer * protected_slices,grpc_slice_buffer * unprotected_slices)175 static tsi_result alts_zero_copy_grpc_protector_unprotect(
176     tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices,
177     grpc_slice_buffer* unprotected_slices) {
178   if (self == nullptr || unprotected_slices == nullptr ||
179       protected_slices == nullptr) {
180     gpr_log(GPR_ERROR,
181             "Invalid nullptr arguments to zero-copy grpc unprotect.");
182     return TSI_INVALID_ARGUMENT;
183   }
184   alts_zero_copy_grpc_protector* protector =
185       reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
186   grpc_slice_buffer_move_into(protected_slices, &protector->protected_sb);
187   /* Keep unprotecting each frame if possible.  */
188   while (protector->protected_sb.length >= kZeroCopyFrameLengthFieldSize) {
189     if (protector->parsed_frame_size == 0) {
190       /* We have not parsed frame size yet. Parses frame size.  */
191       if (!read_frame_size(&protector->protected_sb,
192                            &protector->parsed_frame_size)) {
193         grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
194         return TSI_DATA_CORRUPTED;
195       }
196     }
197     if (protector->protected_sb.length < protector->parsed_frame_size) break;
198     /* At this point, protected_sb contains at least one frame of data.  */
199     tsi_result status;
200     if (protector->protected_sb.length == protector->parsed_frame_size) {
201       status = alts_grpc_record_protocol_unprotect(protector->unrecord_protocol,
202                                                    &protector->protected_sb,
203                                                    unprotected_slices);
204     } else {
205       grpc_slice_buffer_move_first(&protector->protected_sb,
206                                    protector->parsed_frame_size,
207                                    &protector->protected_staging_sb);
208       status = alts_grpc_record_protocol_unprotect(
209           protector->unrecord_protocol, &protector->protected_staging_sb,
210           unprotected_slices);
211     }
212     protector->parsed_frame_size = 0;
213     if (status != TSI_OK) {
214       grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb);
215       return status;
216     }
217   }
218   return TSI_OK;
219 }
220 
alts_zero_copy_grpc_protector_destroy(tsi_zero_copy_grpc_protector * self)221 static void alts_zero_copy_grpc_protector_destroy(
222     tsi_zero_copy_grpc_protector* self) {
223   if (self == nullptr) {
224     return;
225   }
226   alts_zero_copy_grpc_protector* protector =
227       reinterpret_cast<alts_zero_copy_grpc_protector*>(self);
228   alts_grpc_record_protocol_destroy(protector->record_protocol);
229   alts_grpc_record_protocol_destroy(protector->unrecord_protocol);
230   grpc_slice_buffer_destroy_internal(&protector->unprotected_staging_sb);
231   grpc_slice_buffer_destroy_internal(&protector->protected_sb);
232   grpc_slice_buffer_destroy_internal(&protector->protected_staging_sb);
233   gpr_free(protector);
234 }
235 
236 static const tsi_zero_copy_grpc_protector_vtable
237     alts_zero_copy_grpc_protector_vtable = {
238         alts_zero_copy_grpc_protector_protect,
239         alts_zero_copy_grpc_protector_unprotect,
240         alts_zero_copy_grpc_protector_destroy};
241 
alts_zero_copy_grpc_protector_create(const uint8_t * key,size_t key_size,bool is_rekey,bool is_client,bool is_integrity_only,bool enable_extra_copy,size_t * max_protected_frame_size,tsi_zero_copy_grpc_protector ** protector)242 tsi_result alts_zero_copy_grpc_protector_create(
243     const uint8_t* key, size_t key_size, bool is_rekey, bool is_client,
244     bool is_integrity_only, bool enable_extra_copy,
245     size_t* max_protected_frame_size,
246     tsi_zero_copy_grpc_protector** protector) {
247   if (grpc_core::ExecCtx::Get() == nullptr || key == nullptr ||
248       protector == nullptr) {
249     gpr_log(
250         GPR_ERROR,
251         "Invalid nullptr arguments to alts_zero_copy_grpc_protector create.");
252     return TSI_INVALID_ARGUMENT;
253   }
254   /* Creates alts_zero_copy_protector.  */
255   alts_zero_copy_grpc_protector* impl =
256       static_cast<alts_zero_copy_grpc_protector*>(
257           gpr_zalloc(sizeof(alts_zero_copy_grpc_protector)));
258   /* Creates alts_grpc_record_protocol objects.  */
259   tsi_result status = create_alts_grpc_record_protocol(
260       key, key_size, is_rekey, is_client, is_integrity_only,
261       /*is_protect=*/true, enable_extra_copy, &impl->record_protocol);
262   if (status == TSI_OK) {
263     status = create_alts_grpc_record_protocol(
264         key, key_size, is_rekey, is_client, is_integrity_only,
265         /*is_protect=*/false, enable_extra_copy, &impl->unrecord_protocol);
266     if (status == TSI_OK) {
267       /* Sets maximum frame size.  */
268       size_t max_protected_frame_size_to_set = kDefaultFrameLength;
269       if (max_protected_frame_size != nullptr) {
270         *max_protected_frame_size =
271             GPR_MIN(*max_protected_frame_size, kMaxFrameLength);
272         *max_protected_frame_size =
273             GPR_MAX(*max_protected_frame_size, kMinFrameLength);
274         max_protected_frame_size_to_set = *max_protected_frame_size;
275       }
276       impl->max_protected_frame_size = max_protected_frame_size_to_set;
277       impl->max_unprotected_data_size =
278           alts_grpc_record_protocol_max_unprotected_data_size(
279               impl->record_protocol, max_protected_frame_size_to_set);
280       GPR_ASSERT(impl->max_unprotected_data_size > 0);
281       /* Allocates internal slice buffers.  */
282       grpc_slice_buffer_init(&impl->unprotected_staging_sb);
283       grpc_slice_buffer_init(&impl->protected_sb);
284       grpc_slice_buffer_init(&impl->protected_staging_sb);
285       impl->parsed_frame_size = 0;
286       impl->base.vtable = &alts_zero_copy_grpc_protector_vtable;
287       *protector = &impl->base;
288       return TSI_OK;
289     }
290   }
291 
292   /* Cleanup if create failed.  */
293   alts_grpc_record_protocol_destroy(impl->record_protocol);
294   alts_grpc_record_protocol_destroy(impl->unrecord_protocol);
295   gpr_free(impl);
296   return TSI_INTERNAL_ERROR;
297 }
298