| /* |
| * |
| * Copyright 2018 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include "src/core/tsi/grpc_shadow_boringssl.h" |
| |
| #include "src/core/tsi/alts/crypt/gsec.h" |
| |
| #include <openssl/bio.h> |
| #include <openssl/buffer.h> |
| #include <openssl/err.h> |
| #include <openssl/evp.h> |
| #include <openssl/hmac.h> |
| #include <string.h> |
| |
| #include <grpc/support/alloc.h> |
| |
| constexpr size_t kKdfKeyLen = 32; |
| constexpr size_t kKdfCounterLen = 6; |
| constexpr size_t kKdfCounterOffset = 2; |
| constexpr size_t kRekeyAeadKeyLen = kAes128GcmKeyLength; |
| |
| /* Struct for additional data required if rekeying is enabled. */ |
| struct gsec_aes_gcm_aead_rekey_data { |
| uint8_t kdf_counter[kKdfCounterLen]; |
| uint8_t nonce_mask[kAesGcmNonceLength]; |
| }; |
| |
| /* Main struct for AES_GCM crypter interface. */ |
| struct gsec_aes_gcm_aead_crypter { |
| gsec_aead_crypter crypter; |
| size_t key_length; |
| size_t nonce_length; |
| size_t tag_length; |
| uint8_t* key; |
| gsec_aes_gcm_aead_rekey_data* rekey_data; |
| EVP_CIPHER_CTX* ctx; |
| }; |
| |
| static char* aes_gcm_get_openssl_errors() { |
| BIO* bio = BIO_new(BIO_s_mem()); |
| ERR_print_errors(bio); |
| BUF_MEM* mem = nullptr; |
| char* error_msg = nullptr; |
| BIO_get_mem_ptr(bio, &mem); |
| if (mem != nullptr) { |
| error_msg = static_cast<char*>(gpr_malloc(mem->length + 1)); |
| memcpy(error_msg, mem->data, mem->length); |
| error_msg[mem->length] = '\0'; |
| } |
| BIO_free_all(bio); |
| return error_msg; |
| } |
| |
| static void aes_gcm_format_errors(const char* error_msg, char** error_details) { |
| if (error_details == nullptr) { |
| return; |
| } |
| unsigned long error = ERR_get_error(); |
| if (error == 0 && error_msg != nullptr) { |
| *error_details = static_cast<char*>(gpr_malloc(strlen(error_msg) + 1)); |
| memcpy(*error_details, error_msg, strlen(error_msg) + 1); |
| return; |
| } |
| char* openssl_errors = aes_gcm_get_openssl_errors(); |
| if (openssl_errors != nullptr && error_msg != nullptr) { |
| size_t len = strlen(error_msg) + strlen(openssl_errors) + 2; /* ", " */ |
| *error_details = static_cast<char*>(gpr_malloc(len + 1)); |
| snprintf(*error_details, len + 1, "%s, %s", error_msg, openssl_errors); |
| gpr_free(openssl_errors); |
| } |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length( |
| const gsec_aead_crypter* crypter, size_t plaintext_length, |
| size_t* max_ciphertext_and_tag_length, char** error_details) { |
| if (max_ciphertext_and_tag_length == nullptr) { |
| aes_gcm_format_errors("max_ciphertext_and_tag_length is nullptr.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| *max_ciphertext_and_tag_length = |
| plaintext_length + aes_gcm_crypter->tag_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_max_plaintext_length( |
| const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, |
| size_t* max_plaintext_length, char** error_details) { |
| if (max_plaintext_length == nullptr) { |
| aes_gcm_format_errors("max_plaintext_length is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| if (ciphertext_and_tag_length < aes_gcm_crypter->tag_length) { |
| *max_plaintext_length = 0; |
| aes_gcm_format_errors( |
| "ciphertext_and_tag_length is smaller than tag_length.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| *max_plaintext_length = |
| ciphertext_and_tag_length - aes_gcm_crypter->tag_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_nonce_length( |
| const gsec_aead_crypter* crypter, size_t* nonce_length, |
| char** error_details) { |
| if (nonce_length == nullptr) { |
| aes_gcm_format_errors("nonce_length is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| *nonce_length = aes_gcm_crypter->nonce_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_key_length( |
| const gsec_aead_crypter* crypter, size_t* key_length, |
| char** error_details) { |
| if (key_length == nullptr) { |
| aes_gcm_format_errors("key_length is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| *key_length = aes_gcm_crypter->key_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_tag_length( |
| const gsec_aead_crypter* crypter, size_t* tag_length, |
| char** error_details) { |
| if (tag_length == nullptr) { |
| aes_gcm_format_errors("tag_length is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| *tag_length = aes_gcm_crypter->tag_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static void aes_gcm_mask_nonce(uint8_t* dst, const uint8_t* nonce, |
| const uint8_t* mask) { |
| uint64_t mask1; |
| uint32_t mask2; |
| memcpy(&mask1, mask, sizeof(mask1)); |
| memcpy(&mask2, mask + sizeof(mask1), sizeof(mask2)); |
| uint64_t nonce1; |
| uint32_t nonce2; |
| memcpy(&nonce1, nonce, sizeof(nonce1)); |
| memcpy(&nonce2, nonce + sizeof(nonce1), sizeof(nonce2)); |
| nonce1 ^= mask1; |
| nonce2 ^= mask2; |
| memcpy(dst, &nonce1, sizeof(nonce1)); |
| memcpy(dst + sizeof(nonce1), &nonce2, sizeof(nonce2)); |
| } |
| |
| static grpc_status_code aes_gcm_derive_aead_key(uint8_t* dst, |
| const uint8_t* kdf_key, |
| const uint8_t* kdf_counter) { |
| unsigned char buf[EVP_MAX_MD_SIZE]; |
| unsigned char ctr = 1; |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L |
| HMAC_CTX hmac; |
| HMAC_CTX_init(&hmac); |
| if (!HMAC_Init_ex(&hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) || |
| !HMAC_Update(&hmac, kdf_counter, kKdfCounterLen) || |
| !HMAC_Update(&hmac, &ctr, 1) || !HMAC_Final(&hmac, buf, nullptr)) { |
| HMAC_CTX_cleanup(&hmac); |
| return GRPC_STATUS_INTERNAL; |
| } |
| HMAC_CTX_cleanup(&hmac); |
| #else |
| HMAC_CTX* hmac = HMAC_CTX_new(); |
| if (hmac == nullptr) { |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (!HMAC_Init_ex(hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) || |
| !HMAC_Update(hmac, kdf_counter, kKdfCounterLen) || |
| !HMAC_Update(hmac, &ctr, 1) || !HMAC_Final(hmac, buf, nullptr)) { |
| HMAC_CTX_free(hmac); |
| return GRPC_STATUS_INTERNAL; |
| } |
| HMAC_CTX_free(hmac); |
| #endif |
| memcpy(dst, buf, kRekeyAeadKeyLen); |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code aes_gcm_rekey_if_required( |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter, const uint8_t* nonce, |
| char** error_details) { |
| // If rekey_data is nullptr, then rekeying is not supported and not required. |
| // If bytes 2-7 of kdf_counter differ from the (per message) nonce, then the |
| // encryption key is recomputed from a new kdf_counter to ensure that we don't |
| // encrypt more than 2^16 messages per encryption key (in each direction). |
| if (aes_gcm_crypter->rekey_data == nullptr || |
| memcmp(aes_gcm_crypter->rekey_data->kdf_counter, |
| nonce + kKdfCounterOffset, kKdfCounterLen) == 0) { |
| return GRPC_STATUS_OK; |
| } |
| memcpy(aes_gcm_crypter->rekey_data->kdf_counter, nonce + kKdfCounterOffset, |
| kKdfCounterLen); |
| uint8_t aead_key[kRekeyAeadKeyLen]; |
| if (aes_gcm_derive_aead_key(aead_key, aes_gcm_crypter->key, |
| aes_gcm_crypter->rekey_data->kdf_counter) != |
| GRPC_STATUS_OK) { |
| aes_gcm_format_errors("Rekeying failed in key derivation.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, aead_key, |
| nullptr)) { |
| aes_gcm_format_errors("Rekeying failed in context update.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_encrypt_iovec( |
| gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, |
| const struct iovec* aad_vec, size_t aad_vec_length, |
| const struct iovec* plaintext_vec, size_t plaintext_vec_length, |
| struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, |
| char** error_details) { |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>(crypter); |
| // Input checks |
| if (nonce == nullptr) { |
| aes_gcm_format_errors("Nonce buffer is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (kAesGcmNonceLength != nonce_length) { |
| aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (aad_vec_length > 0 && aad_vec == nullptr) { |
| aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (plaintext_vec_length > 0 && plaintext_vec == nullptr) { |
| aes_gcm_format_errors( |
| "Non-zero plaintext_vec_length but plaintext_vec is nullptr.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (ciphertext_bytes_written == nullptr) { |
| aes_gcm_format_errors("bytes_written is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| *ciphertext_bytes_written = 0; |
| // rekey if required |
| if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) != |
| GRPC_STATUS_OK) { |
| return GRPC_STATUS_INTERNAL; |
| } |
| // mask nonce if required |
| const uint8_t* nonce_aead = nonce; |
| uint8_t nonce_masked[kAesGcmNonceLength]; |
| if (aes_gcm_crypter->rekey_data != nullptr) { |
| aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask, |
| nonce); |
| nonce_aead = nonce_masked; |
| } |
| // init openssl context |
| if (!EVP_EncryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr, |
| nonce_aead)) { |
| aes_gcm_format_errors("Initializing nonce failed", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| // process aad |
| size_t i; |
| for (i = 0; i < aad_vec_length; i++) { |
| const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base); |
| size_t aad_length = aad_vec[i].iov_len; |
| if (aad_length == 0) { |
| continue; |
| } |
| size_t aad_bytes_read = 0; |
| if (aad == nullptr) { |
| aes_gcm_format_errors("aad is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, nullptr, |
| reinterpret_cast<int*>(&aad_bytes_read), aad, |
| static_cast<int>(aad_length)) || |
| aad_bytes_read != aad_length) { |
| aes_gcm_format_errors("Setting authenticated associated data failed", |
| error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| } |
| uint8_t* ciphertext = static_cast<uint8_t*>(ciphertext_vec.iov_base); |
| size_t ciphertext_length = ciphertext_vec.iov_len; |
| if (ciphertext == nullptr) { |
| aes_gcm_format_errors("ciphertext is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| // process plaintext |
| for (i = 0; i < plaintext_vec_length; i++) { |
| const uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec[i].iov_base); |
| size_t plaintext_length = plaintext_vec[i].iov_len; |
| if (plaintext == nullptr) { |
| if (plaintext_length == 0) { |
| continue; |
| } |
| aes_gcm_format_errors("plaintext is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (ciphertext_length < plaintext_length) { |
| aes_gcm_format_errors( |
| "ciphertext is not large enough to hold the result.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| int bytes_written = 0; |
| int bytes_to_write = static_cast<int>(plaintext_length); |
| if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, ciphertext, &bytes_written, |
| plaintext, bytes_to_write)) { |
| aes_gcm_format_errors("Encrypting plaintext failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (bytes_written > bytes_to_write) { |
| aes_gcm_format_errors("More bytes written than expected.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| ciphertext += bytes_written; |
| ciphertext_length -= bytes_written; |
| } |
| int bytes_written_temp = 0; |
| if (!EVP_EncryptFinal_ex(aes_gcm_crypter->ctx, nullptr, |
| &bytes_written_temp)) { |
| aes_gcm_format_errors("Finalizing encryption failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (bytes_written_temp != 0) { |
| aes_gcm_format_errors("Openssl wrote some unexpected bytes.", |
| error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (ciphertext_length < kAesGcmTagLength) { |
| aes_gcm_format_errors("ciphertext is too small to hold a tag.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| |
| if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_GET_TAG, |
| kAesGcmTagLength, ciphertext)) { |
| aes_gcm_format_errors("Writing tag failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| ciphertext += kAesGcmTagLength; |
| ciphertext_length -= kAesGcmTagLength; |
| *ciphertext_bytes_written = ciphertext_vec.iov_len - ciphertext_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static grpc_status_code gsec_aes_gcm_aead_crypter_decrypt_iovec( |
| gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, |
| const struct iovec* aad_vec, size_t aad_vec_length, |
| const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, |
| struct iovec plaintext_vec, size_t* plaintext_bytes_written, |
| char** error_details) { |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| if (nonce == nullptr) { |
| aes_gcm_format_errors("Nonce buffer is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (kAesGcmNonceLength != nonce_length) { |
| aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (aad_vec_length > 0 && aad_vec == nullptr) { |
| aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (ciphertext_vec_length > 0 && ciphertext_vec == nullptr) { |
| aes_gcm_format_errors( |
| "Non-zero plaintext_vec_length but plaintext_vec is nullptr.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| // Compute the total length so we can ensure we don't pass the tag into |
| // EVP_decrypt. |
| size_t total_ciphertext_length = 0; |
| size_t i; |
| for (i = 0; i < ciphertext_vec_length; i++) { |
| total_ciphertext_length += ciphertext_vec[i].iov_len; |
| } |
| if (total_ciphertext_length < kAesGcmTagLength) { |
| aes_gcm_format_errors("ciphertext is too small to hold a tag.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (plaintext_bytes_written == nullptr) { |
| aes_gcm_format_errors("bytes_written is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| *plaintext_bytes_written = 0; |
| // rekey if required |
| if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) != |
| GRPC_STATUS_OK) { |
| aes_gcm_format_errors("Rekeying failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| // mask nonce if required |
| const uint8_t* nonce_aead = nonce; |
| uint8_t nonce_masked[kAesGcmNonceLength]; |
| if (aes_gcm_crypter->rekey_data != nullptr) { |
| aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask, |
| nonce); |
| nonce_aead = nonce_masked; |
| } |
| // init openssl context |
| if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr, |
| nonce_aead)) { |
| aes_gcm_format_errors("Initializing nonce failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| // process aad |
| for (i = 0; i < aad_vec_length; i++) { |
| const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base); |
| size_t aad_length = aad_vec[i].iov_len; |
| if (aad_length == 0) { |
| continue; |
| } |
| size_t aad_bytes_read = 0; |
| if (aad == nullptr) { |
| aes_gcm_format_errors("aad is nullptr.", error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, nullptr, |
| reinterpret_cast<int*>(&aad_bytes_read), aad, |
| static_cast<int>(aad_length)) || |
| aad_bytes_read != aad_length) { |
| aes_gcm_format_errors("Setting authenticated associated data failed.", |
| error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| } |
| // process ciphertext |
| uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec.iov_base); |
| size_t plaintext_length = plaintext_vec.iov_len; |
| if (plaintext_length > 0 && plaintext == nullptr) { |
| aes_gcm_format_errors( |
| "plaintext is nullptr, but plaintext_length is positive.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| const uint8_t* ciphertext = nullptr; |
| size_t ciphertext_length = 0; |
| for (i = 0; |
| i < ciphertext_vec_length && total_ciphertext_length > kAesGcmTagLength; |
| i++) { |
| ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base); |
| ciphertext_length = ciphertext_vec[i].iov_len; |
| if (ciphertext == nullptr) { |
| if (ciphertext_length == 0) { |
| continue; |
| } |
| aes_gcm_format_errors("ciphertext is nullptr.", error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| size_t bytes_written = 0; |
| size_t bytes_to_write = ciphertext_length; |
| // Don't include the tag |
| if (bytes_to_write > total_ciphertext_length - kAesGcmTagLength) { |
| bytes_to_write = total_ciphertext_length - kAesGcmTagLength; |
| } |
| if (plaintext_length < bytes_to_write) { |
| aes_gcm_format_errors( |
| "Not enough plaintext buffer to hold encrypted ciphertext.", |
| error_details); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, plaintext, |
| reinterpret_cast<int*>(&bytes_written), ciphertext, |
| static_cast<int>(bytes_to_write))) { |
| aes_gcm_format_errors("Decrypting ciphertext failed.", error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (bytes_written > ciphertext_length) { |
| aes_gcm_format_errors("More bytes written than expected.", error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INTERNAL; |
| } |
| ciphertext += bytes_written; |
| ciphertext_length -= bytes_written; |
| total_ciphertext_length -= bytes_written; |
| plaintext += bytes_written; |
| plaintext_length -= bytes_written; |
| } |
| if (total_ciphertext_length > kAesGcmTagLength) { |
| aes_gcm_format_errors( |
| "Not enough plaintext buffer to hold encrypted ciphertext.", |
| error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| uint8_t tag[kAesGcmTagLength]; |
| uint8_t* tag_tmp = tag; |
| if (ciphertext_length > 0) { |
| memcpy(tag_tmp, ciphertext, ciphertext_length); |
| tag_tmp += ciphertext_length; |
| total_ciphertext_length -= ciphertext_length; |
| } |
| for (; i < ciphertext_vec_length; i++) { |
| ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base); |
| ciphertext_length = ciphertext_vec[i].iov_len; |
| if (ciphertext == nullptr) { |
| if (ciphertext_length == 0) { |
| continue; |
| } |
| aes_gcm_format_errors("ciphertext is nullptr.", error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INVALID_ARGUMENT; |
| } |
| memcpy(tag_tmp, ciphertext, ciphertext_length); |
| tag_tmp += ciphertext_length; |
| total_ciphertext_length -= ciphertext_length; |
| } |
| if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_TAG, |
| kAesGcmTagLength, reinterpret_cast<void*>(tag))) { |
| aes_gcm_format_errors("Setting tag failed.", error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INTERNAL; |
| } |
| int bytes_written_temp = 0; |
| if (!EVP_DecryptFinal_ex(aes_gcm_crypter->ctx, nullptr, |
| &bytes_written_temp)) { |
| aes_gcm_format_errors("Checking tag failed.", error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_FAILED_PRECONDITION; |
| } |
| if (bytes_written_temp != 0) { |
| aes_gcm_format_errors("Openssl wrote some unexpected bytes.", |
| error_details); |
| memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); |
| return GRPC_STATUS_INTERNAL; |
| } |
| *plaintext_bytes_written = plaintext_vec.iov_len - plaintext_length; |
| return GRPC_STATUS_OK; |
| } |
| |
| static void gsec_aes_gcm_aead_crypter_destroy(gsec_aead_crypter* crypter) { |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| reinterpret_cast<gsec_aes_gcm_aead_crypter*>( |
| const_cast<gsec_aead_crypter*>(crypter)); |
| gpr_free(aes_gcm_crypter->key); |
| gpr_free(aes_gcm_crypter->rekey_data); |
| EVP_CIPHER_CTX_free(aes_gcm_crypter->ctx); |
| } |
| |
| static const gsec_aead_crypter_vtable vtable = { |
| gsec_aes_gcm_aead_crypter_encrypt_iovec, |
| gsec_aes_gcm_aead_crypter_decrypt_iovec, |
| gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length, |
| gsec_aes_gcm_aead_crypter_max_plaintext_length, |
| gsec_aes_gcm_aead_crypter_nonce_length, |
| gsec_aes_gcm_aead_crypter_key_length, |
| gsec_aes_gcm_aead_crypter_tag_length, |
| gsec_aes_gcm_aead_crypter_destroy}; |
| |
| static grpc_status_code aes_gcm_new_evp_cipher_ctx( |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter, char** error_details) { |
| const EVP_CIPHER* cipher = nullptr; |
| bool is_rekey = aes_gcm_crypter->rekey_data != nullptr; |
| switch (is_rekey ? kRekeyAeadKeyLen : aes_gcm_crypter->key_length) { |
| case kAes128GcmKeyLength: |
| cipher = EVP_aes_128_gcm(); |
| break; |
| case kAes256GcmKeyLength: |
| cipher = EVP_aes_256_gcm(); |
| break; |
| } |
| const uint8_t* aead_key = aes_gcm_crypter->key; |
| uint8_t aead_key_rekey[kRekeyAeadKeyLen]; |
| if (is_rekey) { |
| if (aes_gcm_derive_aead_key(aead_key_rekey, aes_gcm_crypter->key, |
| aes_gcm_crypter->rekey_data->kdf_counter) != |
| GRPC_STATUS_OK) { |
| aes_gcm_format_errors("Deriving key failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| aead_key = aead_key_rekey; |
| } |
| if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, cipher, nullptr, aead_key, |
| nullptr)) { |
| aes_gcm_format_errors("Setting key failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_IVLEN, |
| static_cast<int>(aes_gcm_crypter->nonce_length), |
| nullptr)) { |
| aes_gcm_format_errors("Setting nonce length failed.", error_details); |
| return GRPC_STATUS_INTERNAL; |
| } |
| return GRPC_STATUS_OK; |
| } |
| |
| grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key, |
| size_t key_length, |
| size_t nonce_length, |
| size_t tag_length, bool rekey, |
| gsec_aead_crypter** crypter, |
| char** error_details) { |
| if (key == nullptr) { |
| aes_gcm_format_errors("key is nullptr.", error_details); |
| return GRPC_STATUS_FAILED_PRECONDITION; |
| } |
| if (crypter == nullptr) { |
| aes_gcm_format_errors("crypter is nullptr.", error_details); |
| return GRPC_STATUS_FAILED_PRECONDITION; |
| } |
| *crypter = nullptr; |
| if ((rekey && key_length != kAes128GcmRekeyKeyLength) || |
| (!rekey && key_length != kAes128GcmKeyLength && |
| key_length != kAes256GcmKeyLength) || |
| (tag_length != kAesGcmTagLength) || |
| (nonce_length != kAesGcmNonceLength)) { |
| aes_gcm_format_errors( |
| "Invalid key and/or nonce and/or tag length are provided at AEAD " |
| "crypter instance construction time.", |
| error_details); |
| return GRPC_STATUS_FAILED_PRECONDITION; |
| } |
| gsec_aes_gcm_aead_crypter* aes_gcm_crypter = |
| static_cast<gsec_aes_gcm_aead_crypter*>( |
| gpr_malloc(sizeof(gsec_aes_gcm_aead_crypter))); |
| aes_gcm_crypter->crypter.vtable = &vtable; |
| aes_gcm_crypter->nonce_length = nonce_length; |
| aes_gcm_crypter->tag_length = tag_length; |
| if (rekey) { |
| aes_gcm_crypter->key_length = kKdfKeyLen; |
| aes_gcm_crypter->rekey_data = static_cast<gsec_aes_gcm_aead_rekey_data*>( |
| gpr_malloc(sizeof(gsec_aes_gcm_aead_rekey_data))); |
| memcpy(aes_gcm_crypter->rekey_data->nonce_mask, key + kKdfKeyLen, |
| kAesGcmNonceLength); |
| // Set kdf_counter to all-zero for initial key derivation. |
| memset(aes_gcm_crypter->rekey_data->kdf_counter, 0, kKdfCounterLen); |
| } else { |
| aes_gcm_crypter->key_length = key_length; |
| aes_gcm_crypter->rekey_data = nullptr; |
| } |
| aes_gcm_crypter->key = |
| static_cast<uint8_t*>(gpr_malloc(aes_gcm_crypter->key_length)); |
| memcpy(aes_gcm_crypter->key, key, aes_gcm_crypter->key_length); |
| aes_gcm_crypter->ctx = EVP_CIPHER_CTX_new(); |
| grpc_status_code status = |
| aes_gcm_new_evp_cipher_ctx(aes_gcm_crypter, error_details); |
| if (status != GRPC_STATUS_OK) { |
| gsec_aes_gcm_aead_crypter_destroy(&aes_gcm_crypter->crypter); |
| gpr_free(aes_gcm_crypter); |
| return status; |
| } |
| *crypter = &aes_gcm_crypter->crypter; |
| return GRPC_STATUS_OK; |
| } |