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