  ErrorType init(const char* name,
                 unsigned char* key,
                 size_t key_len,
                 unsigned char* iv,
                 size_t iv_len) {
    ErrorType r = kErrNone;

    const EVP_CIPHER* const cipher = EVP_get_cipherbyname(name);
    if (cipher == nullptr) {
      r = kErrOpenSSL;
      goto out;
    }

    if (cipher != EVP_aes_128_gcm() && cipher != EVP_aes_256_gcm()) {
      r = kErrBadCipherName;
      goto out;
    }

    if ((ctx_ = EVP_CIPHER_CTX_new()) == nullptr
        || EVP_EncryptInit_ex(ctx_, cipher, nullptr, nullptr, nullptr) != 1) {
      r = kErrOpenSSL;
      goto out;
    }

    if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_AEAD_SET_IVLEN, iv_len, nullptr)) {
      r = kErrOpenSSL;
      goto out;
    }

    //~ if (iv_len != static_cast<size_t>(EVP_CIPHER_CTX_iv_length(ctx_))) {
      //~ r = kErrBadIVLen;
      //~ goto out;
    //~ }

    if (key_len != static_cast<size_t>(EVP_CIPHER_CTX_key_length(ctx_))) {
      if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
        r = kErrBadKeyLen;
        goto out;
      }
    }

    // Set key and IV
    if (EVP_EncryptInit_ex(ctx_, nullptr, nullptr, key, iv) != 1) {
      r = kErrOpenSSL;
      goto out;
    }
    if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_SET_IV_FIXED, -1, iv)) {
      r = kErrOpenSSL;
      goto out;
    }

    // Disable padding
    EVP_CIPHER_CTX_set_padding(ctx_, 0);

out:
    if (r != kErrNone)
      clear();
    return r;
  }
  ErrorType init(const char* name,
                 unsigned char* key,
                 size_t key_len,
                 unsigned char* iv,
                 size_t iv_len) {
    ErrorType r = kErrNone;

    const EVP_CIPHER* const cipher = EVP_get_cipherbyname(name);
    if (cipher == nullptr) {
      r = kErrOpenSSL;
      goto out;
    }

    if (cipher != EVP_aes_128_gcm() && cipher != EVP_aes_256_gcm()) {
      r = kErrBadCipherName;
      goto out;
    }

    if ((ctx_ = EVP_CIPHER_CTX_new()) == nullptr
        || EVP_EncryptInit_ex(ctx_, cipher, nullptr, nullptr, nullptr) != 1) {
      r = kErrOpenSSL;
      goto out;
    }

    if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_AEAD_SET_IVLEN, iv_len, nullptr)) {
      r = kErrOpenSSL;
      goto out;
    }

     
       
       
     

    if (key_len != static_cast<size_t>(EVP_CIPHER_CTX_key_length(ctx_))) {
      if (!EVP_CIPHER_CTX_set_key_length(ctx_, key_len)) {
        r = kErrBadKeyLen;
        goto out;
      }
    }

     
    if (EVP_EncryptInit_ex(ctx_, nullptr, nullptr, key, iv) != 1) {
      r = kErrOpenSSL;
      goto out;
    }
    if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_SET_IV_FIXED, -1, iv)) {
      r = kErrOpenSSL;
      goto out;
    }

     
    EVP_CIPHER_CTX_set_padding(ctx_, 0);

out:
    if (r != kErrNone)
      clear();
    return r;
  }