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