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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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 163 size_t alts_iovec_record_protocol_get_header_length() { 164 return kZeroCopyFrameHeaderSize; 165 } 166 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 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 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 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 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 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 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 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