in src/core/tsi/alts/crypt/aes_gcm.cc [384:574]
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;
}