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