//
//
// 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.
//
//

#ifndef GRPC_SRC_CORE_TSI_ALTS_CRYPT_GSEC_H
#define GRPC_SRC_CORE_TSI_ALTS_CRYPT_GSEC_H

#include <grpc/support/port_platform.h>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#include <grpc/event_engine/port.h>
#include <grpc/grpc.h>

#ifndef _STRUCT_IOVEC
#if !defined(GRPC_EVENT_ENGINE_POSIX)
struct iovec {
  void* iov_base;
  size_t iov_len;
};
#endif  // GRPC_EVENT_ENGINE_POSIX
#endif  // _STRUCT_IOVEC

///
/// A gsec interface for AEAD encryption schemes. The API is thread-compatible.
/// Each implementation of this interface should specify supported values for
/// key, nonce, and tag lengths.
///

// Key, nonce, and tag length in bytes
const size_t kAesGcmNonceLength = 12;
const size_t kAesGcmTagLength = 16;
const size_t kAes128GcmKeyLength = 16;
const size_t kAes256GcmKeyLength = 32;

// The first 32 bytes are used as a KDF key and the remaining 12 bytes are used
// to mask the nonce.
const size_t kAes128GcmRekeyKeyLength = 44;

typedef struct gsec_aead_crypter gsec_aead_crypter;

///
/// The gsec_aead_crypter is an API for different AEAD implementations such as
/// AES_GCM. It encapsulates all AEAD-related operations in the format of
/// V-table that stores pointers to functions implementing those operations.
/// It also provides helper functions to wrap each of those function pointers.
///
/// A typical usage of this object would be:
///
///------------------------------------------------------------------------------
///// Declare a gsec_aead_crypter object, and create and assign an instance
///// of specific AEAD implementation e.g., AES_GCM to it. We assume both
///// key and nonce contain cryptographically secure random bytes, and the key
///// can be derived from an upper-layer application.
/// gsec_aead_crypter* crypter;
/// char* error_in_creation;
///// User can populate the message with any 100 bytes data.
/// uint8_t* message = gpr_malloc(100);
/// grpc_status_code creation_status = gsec_aes_gcm_aead_crypter_create(key,
///                                                     kAes128GcmKeyLength,
///                                                     kAesGcmNonceLength,
///                                                     kAesGcmTagLength,
///                                                     &crypter,
///                                                     false,
///                                                     0
///                                                     &error_in_creation);
///
/// if (creation_status == GRPC_STATUS_OK) {
///   // Allocate a correct amount of memory to hold a ciphertext.
///   size_t clength = 0;
///   gsec_aead_crypter_max_ciphertext_and_tag_length(crypter, 100, &clength,
///                                                   nullptr);
///   uint8_t* ciphertext = gpr_malloc(clength);
///
///   // Perform encryption
///   size_t num_encrypted_bytes = 0;
///   char* error_in_encryption = nullptr;
///   grpc_status_code status = gsec_aead_crypter_encrypt(crypter, nonce,
///                                                       kAesGcmNonceLength,
///                                                       nullptr, 0, message,
///                                                       100, ciphertext,
///                                                       clength,
///                                                       &num_encrypted_bytes,
///                                                       &error_in_encryption);
/// if (status == GRPC_STATUS_OK) {
///      // Allocate a correct amount of memory to hold a plaintext.
///      size_t plength = 0;
///      gsec_aead_crypter_max_plaintext_length(crypter, num_encrypted_bytes,
///                                             &plength, nullptr);
///      uint8_t* plaintext = gpr_malloc(plength);
///
///      // Perform decryption.
///      size_t num_decrypted_bytes = 0;
///      char* error_in_decryption = nullptr;
///      status = gsec_aead_crypter_decrypt(crypter, nonce,
///                                         kAesGcmNonceLength, nullptr, 0,
///                                         ciphertext, num_encrypted_bytes,
///                                         plaintext, plength,
///                                         &num_decrypted_bytes,
///                                         &error_in_decryption);
///      if (status != GRPC_STATUS_OK) {
///        fprintf(stderr, "AEAD decrypt operation failed with error code:"
///                        "%d, message: %s\n", status, error_in_decryption);
///      }
///      ...
///      gpr_free(plaintext);
///      gpr_free(error_in_decryption);
///   } else {
///       fprintf(stderr, "AEAD encrypt operation failed with error code:"
///                       "%d, message: %s\n", status, error_in_encryption);
///   }
///   ...
///   gpr_free(ciphertext);
///   gpr_free(error_in_encryption);
///} else {
///  fprintf(stderr, "Creation of AEAD crypter instance failed with error code:"
///                  "%d, message: %s\n", creation_status, error_in_creation);
///}
///
///// Destruct AEAD crypter instance.
/// if (creation_status == GRPC_STATUS_OK) {
///  gsec_aead_crypter_destroy(crypter);
///}
/// gpr_free(error_in_creation);
/// gpr_free(message);
///-----------------------------------------------------------------------------
///

// V-table for gsec AEAD operations
typedef struct gsec_aead_crypter_vtable {
  grpc_status_code (*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);
  grpc_status_code (*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);
  grpc_status_code (*max_ciphertext_and_tag_length)(
      const gsec_aead_crypter* crypter, size_t plaintext_length,
      size_t* max_ciphertext_and_tag_length_to_return, char** error_details);
  grpc_status_code (*max_plaintext_length)(
      const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
      size_t* max_plaintext_length_to_return, char** error_details);
  grpc_status_code (*nonce_length)(const gsec_aead_crypter* crypter,
                                   size_t* nonce_length_to_return,
                                   char** error_details);
  grpc_status_code (*key_length)(const gsec_aead_crypter* crypter,
                                 size_t* key_length_to_return,
                                 char** error_details);
  grpc_status_code (*tag_length)(const gsec_aead_crypter* crypter,
                                 size_t* tag_length_to_return,
                                 char** error_details);
  void (*destruct)(gsec_aead_crypter* crypter);
} gsec_aead_crypter_vtable;

// Main struct for gsec interface
struct gsec_aead_crypter {
  const struct gsec_aead_crypter_vtable* vtable;
};

///
/// This method performs an AEAD encrypt operation.
///
///- crypter: AEAD crypter instance.
///- nonce: buffer containing a nonce with its size equal to nonce_length.
///- nonce_length: size of nonce buffer, and must be equal to the value returned
///  from method gsec_aead_crypter_nonce_length.
///- aad: buffer containing data that needs to be authenticated but not
///  encrypted with its size equal to aad_length.
///- aad_length: size of aad buffer, which should be zero if the buffer is
///  nullptr.
///- plaintext: buffer containing data that needs to be both encrypted and
///  authenticated with its size equal to plaintext_length.
///- plaintext_length: size of plaintext buffer, which should be zero if
///  plaintext is nullptr.
///- ciphertext_and_tag: buffer that will contain ciphertext and tags the method
///  produced. The buffer should not overlap the plaintext buffer, and pointers
///  to those buffers should not be equal. Also if the ciphertext+tag buffer is
///  nullptr, the plaintext_length should be zero.
///- ciphertext_and_tag_length: size of ciphertext+tag buffer, which should be
///  at least as long as the one returned from method
///  gsec_aead_crypter_max_ciphertext_and_tag_length.
///- bytes_written: the actual number of bytes written to the ciphertext+tag
///  buffer. If bytes_written is nullptr, the plaintext_length should be zero.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
///
grpc_status_code gsec_aead_crypter_encrypt(
    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
    const uint8_t* aad, size_t aad_length, const uint8_t* plaintext,
    size_t plaintext_length, uint8_t* ciphertext_and_tag,
    size_t ciphertext_and_tag_length, size_t* bytes_written,
    char** error_details);

///
/// This method performs an AEAD encrypt operation.
///
///- crypter: AEAD crypter instance.
///- nonce: buffer containing a nonce with its size equal to nonce_length.
///- nonce_length: size of nonce buffer, and must be equal to the value returned
///  from method gsec_aead_crypter_nonce_length.
///- aad_vec: an iovec array containing data that needs to be authenticated but
///  not encrypted.
///- aad_vec_length: the array length of aad_vec.
///- plaintext_vec: an iovec array containing data that needs to be both
///  encrypted and authenticated.
///- plaintext_vec_length: the array length of plaintext_vec.
///- ciphertext_vec: an iovec containing a ciphertext buffer. The buffer should
///  not overlap the plaintext buffer.
///- ciphertext_bytes_written: the actual number of bytes written to
///  ciphertext_vec.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
///
grpc_status_code gsec_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);

///
/// This method performs an AEAD decrypt operation.
///
///- crypter: AEAD crypter instance.
///- nonce: buffer containing a nonce with its size equal to nonce_length.
///- nonce_length: size of nonce buffer, and must be equal to the value returned
///  from method gsec_aead_crypter_nonce_length.
///- aad: buffer containing data that needs to be authenticated only.
///- aad_length: size of aad buffer, which should be zero if the buffer is
///  nullptr.
///- ciphertext_and_tag: buffer containing ciphertext and tag.
///- ciphertext_and_tag_length: length of ciphertext and tag. It should be zero
///  if any of plaintext, ciphertext_and_tag, or bytes_written is nullptr. Also,
///  ciphertext_and_tag_length should be at least as large as the tag length set
///  at AEAD crypter instance construction time.
///- plaintext: buffer containing decrypted and authenticated data the method
///  produced. The buffer should not overlap with the ciphertext+tag buffer, and
///  pointers to those buffers should not be equal.
///- plaintext_length: size of plaintext buffer, which should be at least as
///  long as the one returned from gsec_aead_crypter_max_plaintext_length
///  method.
///- bytes_written: the actual number of bytes written to the plaintext
///  buffer.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_aead_crypter_decrypt(
    gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length,
    const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag,
    size_t ciphertext_and_tag_length, uint8_t* plaintext,
    size_t plaintext_length, size_t* bytes_written, char** error_details);

///
/// This method performs an AEAD decrypt operation.
///
///- crypter: AEAD crypter instance.
///- nonce: buffer containing a nonce with its size equal to nonce_length.
///- nonce_length: size of nonce buffer, and must be equal to the value returned
///  from method gsec_aead_crypter_nonce_length.
///- aad_vec: an iovec array containing data that needs to be authenticated but
///  not encrypted.
///- aad_vec_length: the array length of aad_vec.
///- ciphertext_vec: an iovec array containing the ciphertext and tag.
///- ciphertext_vec_length: the array length of ciphertext_vec.
///- plaintext_vec: an iovec containing a plaintext buffer. The buffer should
///  not overlap the ciphertext buffer.
///- plaintext_bytes_written: the actual number of bytes written to
///  plaintext_vec.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_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);

///
/// This method computes the size of ciphertext+tag buffer that must be passed
/// to gsec_aead_crypter_encrypt function to ensure correct encryption of a
/// plaintext. The actual size of ciphertext+tag written to the buffer could be
/// smaller.
///
///- crypter: AEAD crypter instance.
///- plaintext_length: length of plaintext.
///- max_ciphertext_and_tag_length_to_return: the size of ciphertext+tag buffer
///  the method returns.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length(
    const gsec_aead_crypter* crypter, size_t plaintext_length,
    size_t* max_ciphertext_and_tag_length_to_return, char** error_details);

///
/// This method computes the size of plaintext buffer that must be passed to
/// gsec_aead_crypter_decrypt function to ensure correct decryption of a
/// ciphertext. The actual size of plaintext written to the buffer could be
/// smaller.
///
///- crypter: AEAD crypter instance.
///- ciphertext_and_tag_length: length of ciphertext and tag.
///- max_plaintext_length_to_return: the size of plaintext buffer the method
///  returns.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_aead_crypter_max_plaintext_length(
    const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length,
    size_t* max_plaintext_length_to_return, char** error_details);

///
/// This method returns a valid size of nonce array used at the construction of
/// AEAD crypter instance. It is also the size that should be passed to encrypt
/// and decrypt methods executed on the instance.
///
///- crypter: AEAD crypter instance.
///- nonce_length_to_return: the length of nonce array the method returns.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_aead_crypter_nonce_length(
    const gsec_aead_crypter* crypter, size_t* nonce_length_to_return,
    char** error_details);

///
/// This method returns a valid size of key array used at the construction of
/// AEAD crypter instance. It is also the size that should be passed to encrypt
/// and decrypt methods executed on the instance.
///
///- crypter: AEAD crypter instance.
///- key_length_to_return: the length of key array the method returns.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter,
                                              size_t* key_length_to_return,
                                              char** error_details);
///
/// This method returns a valid size of tag array used at the construction of
/// AEAD crypter instance. It is also the size that should be passed to encrypt
/// and decrypt methods executed on the instance.
///
///- crypter: AEAD crypter instance.
///- tag_length_to_return: the length of tag array the method returns.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On the success of execution, the method returns GRPC_STATUS_OK. Otherwise,
/// it returns an error status code along with its details specified in
/// error_details (if error_details is not nullptr).
///
grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter,
                                              size_t* tag_length_to_return,
                                              char** error_details);

///
/// This method destroys an AEAD crypter instance by de-allocating all of its
/// occupied memory.
///
///- crypter: AEAD crypter instance that needs to be destroyed.
///
void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter);

///
/// This method creates an AEAD crypter instance of AES-GCM encryption scheme
/// which supports 16 and 32 bytes long keys, 12 and 16 bytes long nonces, and
/// 16 bytes long tags. It should be noted that once the lengths of key, nonce,
/// and tag are determined at construction time, they cannot be modified later.
///
///- key: buffer containing a key which is binded with AEAD crypter instance.
///- key_length: length of a key in bytes, which should be 44 if rekeying is
///  enabled and 16 or 32 otherwise.
///- nonce_length: length of a nonce in bytes, which should be either 12 or 16.
///- tag_length: length of a tag in bytes, which should be always 16.
///- rekey: enable nonce-based rekeying and nonce-masking.
///- crypter: address of AES_GCM crypter instance returned from the method.
///- error_details: a buffer containing an error message if the method does not
///  function correctly. It is legal to pass nullptr into error_details, and
///  otherwise, the parameter should be freed with gpr_free.
///
/// On success of instance creation, it stores the address of instance at
/// crypter. Otherwise, it returns an error status code together with its
/// details specified in error_details.
///
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);

#endif  // GRPC_SRC_CORE_TSI_ALTS_CRYPT_GSEC_H
