static grpc_status_code gsec_aes_gcm_aead_crypter_encrypt_iovec()

in src/core/tsi/alts/crypt/aes_gcm.cc [246:382]


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;
}