• 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_iovec_record_protocol.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 
29 #include "src/core/tsi/alts/frame_protector/alts_counter.h"
30 
31 struct alts_iovec_record_protocol {
32   alts_counter* ctr;
33   gsec_aead_crypter* crypter;
34   size_t tag_length;
35   bool is_integrity_only;
36   bool is_protect;
37 };
38 
39 /* Copies error message to destination.  */
maybe_copy_error_msg(const char * src,char ** dst)40 static void maybe_copy_error_msg(const char* src, char** dst) {
41   if (dst != nullptr && src != nullptr) {
42     *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1));
43     memcpy(*dst, src, strlen(src) + 1);
44   }
45 }
46 
47 /* Appends error message to destination.  */
maybe_append_error_msg(const char * appendix,char ** dst)48 static void maybe_append_error_msg(const char* appendix, char** dst) {
49   if (dst != nullptr && appendix != nullptr) {
50     int dst_len = static_cast<int>(strlen(*dst));
51     *dst = static_cast<char*>(realloc(*dst, dst_len + strlen(appendix) + 1));
52     assert(*dst != nullptr);
53     memcpy(*dst + dst_len, appendix, strlen(appendix) + 1);
54   }
55 }
56 
57 /* Use little endian to interpret a string of bytes as uint32_t.  */
load_32_le(const unsigned char * buffer)58 static uint32_t load_32_le(const unsigned char* buffer) {
59   return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) |
60          (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]);
61 }
62 
63 /* Store uint32_t as a string of little endian bytes.  */
store_32_le(uint32_t value,unsigned char * buffer)64 static void store_32_le(uint32_t value, unsigned char* buffer) {
65   buffer[3] = (unsigned char)(value >> 24) & 0xFF;
66   buffer[2] = (unsigned char)(value >> 16) & 0xFF;
67   buffer[1] = (unsigned char)(value >> 8) & 0xFF;
68   buffer[0] = (unsigned char)(value)&0xFF;
69 }
70 
71 /* Ensures header and tag iovec have sufficient length.  */
ensure_header_and_tag_length(const alts_iovec_record_protocol * rp,iovec_t header,iovec_t tag,char ** error_details)72 static grpc_status_code ensure_header_and_tag_length(
73     const alts_iovec_record_protocol* rp, iovec_t header, iovec_t tag,
74     char** error_details) {
75   if (rp == nullptr) {
76     return GRPC_STATUS_FAILED_PRECONDITION;
77   }
78   if (header.iov_base == nullptr) {
79     maybe_copy_error_msg("Header is nullptr.", error_details);
80     return GRPC_STATUS_INVALID_ARGUMENT;
81   }
82   if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
83     maybe_copy_error_msg("Header length is incorrect.", error_details);
84     return GRPC_STATUS_INVALID_ARGUMENT;
85   }
86   if (tag.iov_base == nullptr) {
87     maybe_copy_error_msg("Tag is nullptr.", error_details);
88     return GRPC_STATUS_INVALID_ARGUMENT;
89   }
90   if (tag.iov_len != rp->tag_length) {
91     maybe_copy_error_msg("Tag length is incorrect.", error_details);
92     return GRPC_STATUS_INVALID_ARGUMENT;
93   }
94   return GRPC_STATUS_OK;
95 }
96 
97 /* Increments crypter counter and checks overflow.  */
increment_counter(alts_counter * counter,char ** error_details)98 static grpc_status_code increment_counter(alts_counter* counter,
99                                           char** error_details) {
100   if (counter == nullptr) {
101     return GRPC_STATUS_FAILED_PRECONDITION;
102   }
103   bool is_overflow = false;
104   grpc_status_code status =
105       alts_counter_increment(counter, &is_overflow, error_details);
106   if (status != GRPC_STATUS_OK) {
107     return status;
108   }
109   if (is_overflow) {
110     maybe_copy_error_msg("Crypter counter is overflowed.", error_details);
111     return GRPC_STATUS_INTERNAL;
112   }
113   return GRPC_STATUS_OK;
114 }
115 
116 /* Given an array of iovec, computes the total length of buffer.  */
get_total_length(const iovec_t * vec,size_t vec_length)117 static size_t get_total_length(const iovec_t* vec, size_t vec_length) {
118   size_t total_length = 0;
119   for (size_t i = 0; i < vec_length; ++i) {
120     total_length += vec[i].iov_len;
121   }
122   return total_length;
123 }
124 
125 /* Writes frame header given data and tag length.  */
write_frame_header(size_t data_length,unsigned char * header,char ** error_details)126 static grpc_status_code write_frame_header(size_t data_length,
127                                            unsigned char* header,
128                                            char** error_details) {
129   if (header == nullptr) {
130     maybe_copy_error_msg("Header is nullptr.", error_details);
131     return GRPC_STATUS_FAILED_PRECONDITION;
132   }
133   size_t frame_length = kZeroCopyFrameMessageTypeFieldSize + data_length;
134   store_32_le(static_cast<uint32_t>(frame_length), header);
135   store_32_le(kZeroCopyFrameMessageType,
136               header + kZeroCopyFrameLengthFieldSize);
137   return GRPC_STATUS_OK;
138 }
139 
140 /* Verifies frame header given protected data length.  */
verify_frame_header(size_t data_length,unsigned char * header,char ** error_details)141 static grpc_status_code verify_frame_header(size_t data_length,
142                                             unsigned char* header,
143                                             char** error_details) {
144   if (header == nullptr) {
145     maybe_copy_error_msg("Header is nullptr.", error_details);
146     return GRPC_STATUS_FAILED_PRECONDITION;
147   }
148   size_t frame_length = load_32_le(header);
149   if (frame_length != kZeroCopyFrameMessageTypeFieldSize + data_length) {
150     maybe_copy_error_msg("Bad frame length.", error_details);
151     return GRPC_STATUS_INTERNAL;
152   }
153   size_t message_type = load_32_le(header + kZeroCopyFrameLengthFieldSize);
154   if (message_type != kZeroCopyFrameMessageType) {
155     maybe_copy_error_msg("Unsupported message type.", error_details);
156     return GRPC_STATUS_INTERNAL;
157   }
158   return GRPC_STATUS_OK;
159 }
160 
161 /* --- alts_iovec_record_protocol methods implementation. --- */
162 
alts_iovec_record_protocol_get_header_length()163 size_t alts_iovec_record_protocol_get_header_length() {
164   return kZeroCopyFrameHeaderSize;
165 }
166 
alts_iovec_record_protocol_get_tag_length(const alts_iovec_record_protocol * rp)167 size_t alts_iovec_record_protocol_get_tag_length(
168     const alts_iovec_record_protocol* rp) {
169   if (rp != nullptr) {
170     return rp->tag_length;
171   }
172   return 0;
173 }
174 
alts_iovec_record_protocol_max_unprotected_data_size(const alts_iovec_record_protocol * rp,size_t max_protected_frame_size)175 size_t alts_iovec_record_protocol_max_unprotected_data_size(
176     const alts_iovec_record_protocol* rp, size_t max_protected_frame_size) {
177   if (rp == nullptr) {
178     return 0;
179   }
180   size_t overhead_bytes_size =
181       kZeroCopyFrameMessageTypeFieldSize + rp->tag_length;
182   if (max_protected_frame_size <= overhead_bytes_size) return 0;
183   return max_protected_frame_size - overhead_bytes_size;
184 }
185 
alts_iovec_record_protocol_integrity_only_protect(alts_iovec_record_protocol * rp,const iovec_t * unprotected_vec,size_t unprotected_vec_length,iovec_t header,iovec_t tag,char ** error_details)186 grpc_status_code alts_iovec_record_protocol_integrity_only_protect(
187     alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
188     size_t unprotected_vec_length, iovec_t header, iovec_t tag,
189     char** error_details) {
190   /* Input sanity checks.  */
191   if (rp == nullptr) {
192     maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
193                          error_details);
194     return GRPC_STATUS_INVALID_ARGUMENT;
195   }
196   if (!rp->is_integrity_only) {
197     maybe_copy_error_msg(
198         "Integrity-only operations are not allowed for this object.",
199         error_details);
200     return GRPC_STATUS_FAILED_PRECONDITION;
201   }
202   if (!rp->is_protect) {
203     maybe_copy_error_msg("Protect operations are not allowed for this object.",
204                          error_details);
205     return GRPC_STATUS_FAILED_PRECONDITION;
206   }
207   grpc_status_code status =
208       ensure_header_and_tag_length(rp, header, tag, error_details);
209   if (status != GRPC_STATUS_OK) {
210     return status;
211   }
212   /* Unprotected data should not be zero length.  */
213   size_t data_length =
214       get_total_length(unprotected_vec, unprotected_vec_length);
215   /* Sets frame header.  */
216   status = write_frame_header(data_length + rp->tag_length,
217                               static_cast<unsigned char*>(header.iov_base),
218                               error_details);
219   if (status != GRPC_STATUS_OK) {
220     return status;
221   }
222   /* Computes frame tag by calling AEAD crypter.  */
223   size_t bytes_written = 0;
224   status = gsec_aead_crypter_encrypt_iovec(
225       rp->crypter, alts_counter_get_counter(rp->ctr),
226       alts_counter_get_size(rp->ctr), unprotected_vec, unprotected_vec_length,
227       /* plaintext_vec = */ nullptr, /* plaintext_vec_length = */ 0, tag,
228       &bytes_written, error_details);
229   if (status != GRPC_STATUS_OK) {
230     return status;
231   }
232   if (bytes_written != rp->tag_length) {
233     maybe_copy_error_msg("Bytes written expects to be the same as tag length.",
234                          error_details);
235     return GRPC_STATUS_INTERNAL;
236   }
237   /* Increments the crypter counter.  */
238   return increment_counter(rp->ctr, error_details);
239 }
240 
alts_iovec_record_protocol_integrity_only_unprotect(alts_iovec_record_protocol * rp,const iovec_t * protected_vec,size_t protected_vec_length,iovec_t header,iovec_t tag,char ** error_details)241 grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect(
242     alts_iovec_record_protocol* rp, const iovec_t* protected_vec,
243     size_t protected_vec_length, iovec_t header, iovec_t tag,
244     char** error_details) {
245   /* Input sanity checks.  */
246   if (rp == nullptr) {
247     maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
248                          error_details);
249     return GRPC_STATUS_INVALID_ARGUMENT;
250   }
251   if (!rp->is_integrity_only) {
252     maybe_copy_error_msg(
253         "Integrity-only operations are not allowed for this object.",
254         error_details);
255     return GRPC_STATUS_FAILED_PRECONDITION;
256   }
257   if (rp->is_protect) {
258     maybe_copy_error_msg(
259         "Unprotect operations are not allowed for this object.", error_details);
260     return GRPC_STATUS_FAILED_PRECONDITION;
261   }
262   grpc_status_code status =
263       ensure_header_and_tag_length(rp, header, tag, error_details);
264   if (status != GRPC_STATUS_OK) return status;
265   /* Protected data should not be zero length.  */
266   size_t data_length = get_total_length(protected_vec, protected_vec_length);
267   /* Verifies frame header.  */
268   status = verify_frame_header(data_length + rp->tag_length,
269                                static_cast<unsigned char*>(header.iov_base),
270                                error_details);
271   if (status != GRPC_STATUS_OK) {
272     return status;
273   }
274   /* Verifies frame tag by calling AEAD crypter.  */
275   iovec_t plaintext = {nullptr, 0};
276   size_t bytes_written = 0;
277   status = gsec_aead_crypter_decrypt_iovec(
278       rp->crypter, alts_counter_get_counter(rp->ctr),
279       alts_counter_get_size(rp->ctr), protected_vec, protected_vec_length, &tag,
280       1, plaintext, &bytes_written, error_details);
281   if (status != GRPC_STATUS_OK || bytes_written != 0) {
282     maybe_append_error_msg(" Frame tag verification failed.", error_details);
283     return GRPC_STATUS_INTERNAL;
284   }
285   /* Increments the crypter counter.  */
286   return increment_counter(rp->ctr, error_details);
287 }
288 
alts_iovec_record_protocol_privacy_integrity_protect(alts_iovec_record_protocol * rp,const iovec_t * unprotected_vec,size_t unprotected_vec_length,iovec_t protected_frame,char ** error_details)289 grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect(
290     alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec,
291     size_t unprotected_vec_length, iovec_t protected_frame,
292     char** error_details) {
293   /* Input sanity checks.  */
294   if (rp == nullptr) {
295     maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
296                          error_details);
297     return GRPC_STATUS_INVALID_ARGUMENT;
298   }
299   if (rp->is_integrity_only) {
300     maybe_copy_error_msg(
301         "Privacy-integrity operations are not allowed for this object.",
302         error_details);
303     return GRPC_STATUS_FAILED_PRECONDITION;
304   }
305   if (!rp->is_protect) {
306     maybe_copy_error_msg("Protect operations are not allowed for this object.",
307                          error_details);
308     return GRPC_STATUS_FAILED_PRECONDITION;
309   }
310   /* Unprotected data should not be zero length.  */
311   size_t data_length =
312       get_total_length(unprotected_vec, unprotected_vec_length);
313   /* Ensures protected frame iovec has sufficient size.  */
314   if (protected_frame.iov_base == nullptr) {
315     maybe_copy_error_msg("Protected frame is nullptr.", error_details);
316     return GRPC_STATUS_INVALID_ARGUMENT;
317   }
318   if (protected_frame.iov_len !=
319       alts_iovec_record_protocol_get_header_length() + data_length +
320           rp->tag_length) {
321     maybe_copy_error_msg("Protected frame size is incorrect.", error_details);
322     return GRPC_STATUS_INVALID_ARGUMENT;
323   }
324   /* Writer frame header.  */
325   grpc_status_code status = write_frame_header(
326       data_length + rp->tag_length,
327       static_cast<unsigned char*>(protected_frame.iov_base), error_details);
328   if (status != GRPC_STATUS_OK) {
329     return status;
330   }
331   /* Encrypt unprotected data by calling AEAD crypter.  */
332   unsigned char* ciphertext_buffer =
333       static_cast<unsigned char*>(protected_frame.iov_base) +
334       alts_iovec_record_protocol_get_header_length();
335   iovec_t ciphertext = {ciphertext_buffer, data_length + rp->tag_length};
336   size_t bytes_written = 0;
337   status = gsec_aead_crypter_encrypt_iovec(
338       rp->crypter, alts_counter_get_counter(rp->ctr),
339       alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
340       /* aad_vec_length = */ 0, unprotected_vec, unprotected_vec_length,
341       ciphertext, &bytes_written, error_details);
342   if (status != GRPC_STATUS_OK) {
343     return status;
344   }
345   if (bytes_written != data_length + rp->tag_length) {
346     maybe_copy_error_msg(
347         "Bytes written expects to be data length plus tag length.",
348         error_details);
349     return GRPC_STATUS_INTERNAL;
350   }
351   /* Increments the crypter counter. */
352   return increment_counter(rp->ctr, error_details);
353 }
354 
alts_iovec_record_protocol_privacy_integrity_unprotect(alts_iovec_record_protocol * rp,iovec_t header,const iovec_t * protected_vec,size_t protected_vec_length,iovec_t unprotected_data,char ** error_details)355 grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect(
356     alts_iovec_record_protocol* rp, iovec_t header,
357     const iovec_t* protected_vec, size_t protected_vec_length,
358     iovec_t unprotected_data, char** error_details) {
359   /* Input sanity checks.  */
360   if (rp == nullptr) {
361     maybe_copy_error_msg("Input iovec_record_protocol is nullptr.",
362                          error_details);
363     return GRPC_STATUS_INVALID_ARGUMENT;
364   }
365   if (rp->is_integrity_only) {
366     maybe_copy_error_msg(
367         "Privacy-integrity operations are not allowed for this object.",
368         error_details);
369     return GRPC_STATUS_FAILED_PRECONDITION;
370   }
371   if (rp->is_protect) {
372     maybe_copy_error_msg(
373         "Unprotect operations are not allowed for this object.", error_details);
374     return GRPC_STATUS_FAILED_PRECONDITION;
375   }
376   /* Protected data size should be no less than tag size.  */
377   size_t protected_data_length =
378       get_total_length(protected_vec, protected_vec_length);
379   if (protected_data_length < rp->tag_length) {
380     maybe_copy_error_msg(
381         "Protected data length should be more than the tag length.",
382         error_details);
383     return GRPC_STATUS_INVALID_ARGUMENT;
384   }
385   /* Ensures header has sufficient size.  */
386   if (header.iov_base == nullptr) {
387     maybe_copy_error_msg("Header is nullptr.", error_details);
388     return GRPC_STATUS_INVALID_ARGUMENT;
389   }
390   if (header.iov_len != alts_iovec_record_protocol_get_header_length()) {
391     maybe_copy_error_msg("Header length is incorrect.", error_details);
392     return GRPC_STATUS_INVALID_ARGUMENT;
393   }
394   /* Ensures unprotected data iovec has sufficient size.  */
395   if (unprotected_data.iov_len != protected_data_length - rp->tag_length) {
396     maybe_copy_error_msg("Unprotected data size is incorrect.", error_details);
397     return GRPC_STATUS_INVALID_ARGUMENT;
398   }
399   /* Verify frame header.  */
400   grpc_status_code status = verify_frame_header(
401       protected_data_length, static_cast<unsigned char*>(header.iov_base),
402       error_details);
403   if (status != GRPC_STATUS_OK) {
404     return status;
405   }
406   /* Decrypt protected data by calling AEAD crypter.  */
407   size_t bytes_written = 0;
408   status = gsec_aead_crypter_decrypt_iovec(
409       rp->crypter, alts_counter_get_counter(rp->ctr),
410       alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr,
411       /* aad_vec_length = */ 0, protected_vec, protected_vec_length,
412       unprotected_data, &bytes_written, error_details);
413   if (status != GRPC_STATUS_OK) {
414     maybe_append_error_msg(" Frame decryption failed.", error_details);
415     return GRPC_STATUS_INTERNAL;
416   }
417   if (bytes_written != protected_data_length - rp->tag_length) {
418     maybe_copy_error_msg(
419         "Bytes written expects to be protected data length minus tag length.",
420         error_details);
421     return GRPC_STATUS_INTERNAL;
422   }
423   /* Increments the crypter counter. */
424   return increment_counter(rp->ctr, error_details);
425 }
426 
alts_iovec_record_protocol_create(gsec_aead_crypter * crypter,size_t overflow_size,bool is_client,bool is_integrity_only,bool is_protect,alts_iovec_record_protocol ** rp,char ** error_details)427 grpc_status_code alts_iovec_record_protocol_create(
428     gsec_aead_crypter* crypter, size_t overflow_size, bool is_client,
429     bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp,
430     char** error_details) {
431   if (crypter == nullptr || rp == nullptr) {
432     maybe_copy_error_msg(
433         "Invalid nullptr arguments to alts_iovec_record_protocol create.",
434         error_details);
435     return GRPC_STATUS_INVALID_ARGUMENT;
436   }
437   alts_iovec_record_protocol* impl = static_cast<alts_iovec_record_protocol*>(
438       gpr_zalloc(sizeof(alts_iovec_record_protocol)));
439   /* Gets counter length.  */
440   size_t counter_length = 0;
441   grpc_status_code status =
442       gsec_aead_crypter_nonce_length(crypter, &counter_length, error_details);
443   if (status != GRPC_STATUS_OK) {
444     goto cleanup;
445   }
446   /* Creates counters.  */
447   status =
448       alts_counter_create(is_protect ? !is_client : is_client, counter_length,
449                           overflow_size, &impl->ctr, error_details);
450   if (status != GRPC_STATUS_OK) {
451     goto cleanup;
452   }
453   /* Gets tag length.  */
454   status =
455       gsec_aead_crypter_tag_length(crypter, &impl->tag_length, error_details);
456   if (status != GRPC_STATUS_OK) {
457     goto cleanup;
458   }
459   impl->crypter = crypter;
460   impl->is_integrity_only = is_integrity_only;
461   impl->is_protect = is_protect;
462   *rp = impl;
463   return GRPC_STATUS_OK;
464 cleanup:
465   alts_counter_destroy(impl->ctr);
466   gpr_free(impl);
467   return GRPC_STATUS_FAILED_PRECONDITION;
468 }
469 
alts_iovec_record_protocol_destroy(alts_iovec_record_protocol * rp)470 void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp) {
471   if (rp != nullptr) {
472     alts_counter_destroy(rp->ctr);
473     gsec_aead_crypter_destroy(rp->crypter);
474     gpr_free(rp);
475   }
476 }
477