namespace OpenSSL {

  [CCode (cname="ENGINE")]
  public struct ENGINE{}

  [CCode (cprefix="EVP_", lower_case_cprefix="EVP_", cheader_filename="openssl/evp.h")]
  namespace EVP {

    [CCode (cname="EVP_CIPHER", cprefix="EVP_CIPHER_")]
    public struct CIPHER{
      public int key_length();
      public int iv_length();
    }

    [CCode (cname="EVP_aes_256_cbc")]
    public unowned CIPHER? aes_256_cbc();

    [CCode (cname="EVP_aes_256_ecb")]
    public unowned CIPHER? aes_256_ecb();

    [CCode (cname="EVP_BytesToKey")]
    public int bytes_to_key(
      CIPHER cipher, 
      MD md,
      [CCode (array_length = false)] int[] salt,
      uchar[] key_data,
      int nrounds,
      [CCode (array_length = false)] uchar[] key,
      [CCode (array_length = false)] uchar[] iv
    );

    [CCode (cname="EVP_MD")]
    public struct MD{}

    [CCode (cname="EVP_sha1")]
    public unowned MD? sha1();

    //EVP_CIPHER_CTX_init
    [CCode (cname="EVP_CIPHER_CTX_init")]
    public CipherCTX init();

    [CCode (cname = "EVP_CIPHER_CTX", cprefix="EVP_CIPHER_CTX_")]
    public struct CipherCTX {
      public void cleanup();

      [CCode (cname="EVP_EncryptInit_ex")]
      public int encrypt_init(
        CIPHER cipher,
        ENGINE? engine = null,
        [CCode (array_length = false)] uchar* key,
        [CCode (array_length = false)] uchar* iv
      );

      [CCode (cname="EVP_EncryptUpdate")]
      public int encrypt_update(
        [CCode (array_length = false)] uchar* ciphertext,
        out int ciphertext_length,
        [CCode (array_length = false)] uchar* plaintext,
        int plaintext_length
      );

      [CCode (cname="EVP_EncryptFinal_ex")]
      public int encrypt_final(
        [CCode (array_length = false)] uchar* ciphertext,
        out int finished_length
      );

      [CCode (cname="EVP_DecryptInit_ex")]
      public int decrypt_init(
        CIPHER cipher,
        ENGINE? engine = null,
        [CCode (array_length = false)] uchar* key,
        [CCode (array_length = false)] uchar* iv
      );

      [CCode (cname="EVP_DecryptUpdate")]
      public int decrypt_update(
        [CCode (array_length = false)] uchar* plaintext,
        out int plaintext_length,
        [CCode (array_length = false)] uchar* ciphertext,
        int ciphertext_length
      );

      [CCode (cname="EVP_DecryptFinal_ex")]
      public int decrypt_final(
        [CCode (array_length = false)] uchar* plaintext,
        out int finished_length
      );
    }
  }

  [CCode (lower_case_cprefix="AES_", cheader_filename="openssl/aes.h")]
  namespace AES {
    [CCode (cname = "AES_BLOCK_SIZE")]
    public const int BLOCK_SIZE;
  }

}
