  ErrorType decrypt(unsigned char* packet,
                    uint32_t packet_len,
                    unsigned char* length_bytes,
                    unsigned char* tag) {
    ErrorType r = kErrNone;

    // `packet` layout:
    //   <padding length> <payload> <padding>

    int outlen;

    // Increment IV
    unsigned char lastiv[1];
    if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_IV_GEN, 1, lastiv)) {
      r = kErrOpenSSL;
      goto out;
    }

    // Set AAD (the packet length)
    if (!EVP_DecryptUpdate(ctx_, nullptr, &outlen, length_bytes, 4)) {
      r = kErrOpenSSL;
      goto out;
    }
    if (outlen != 4) {
      r = kErrAADFailure;
      goto out;
    }

    // Decrypt everything but the packet length
    if (EVP_DecryptUpdate(ctx_, packet, &outlen, packet, packet_len) != 1) {
      r = kErrOpenSSL;
      goto out;
    }
    if (static_cast<size_t>(outlen) != packet_len) {
      r = kErrPartialDecrypt;
      goto out;
    }

    // Set authentication tag
    if (EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_AEAD_SET_TAG, 16, tag) != 1) {
      r = kErrOpenSSL;
      goto out;
    }

    // Verify authentication tag
    if (!EVP_DecryptFinal_ex(ctx_, nullptr, &outlen)) {
      r = kErrOpenSSL;
      goto out;
    }

out:
    return r;
  }
  ErrorType decrypt(unsigned char* packet,
                    uint32_t packet_len,
                    unsigned char* length_bytes,
                    unsigned char* tag) {
    ErrorType r = kErrNone;

     
     

    int outlen;

     
    unsigned char lastiv[1];
    if (!EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_GCM_IV_GEN, 1, lastiv)) {
      r = kErrOpenSSL;
      goto out;
    }

     
    if (!EVP_DecryptUpdate(ctx_, nullptr, &outlen, length_bytes, 4)) {
      r = kErrOpenSSL;
      goto out;
    }
    if (outlen != 4) {
      r = kErrAADFailure;
      goto out;
    }

     
    if (EVP_DecryptUpdate(ctx_, packet, &outlen, packet, packet_len) != 1) {
      r = kErrOpenSSL;
      goto out;
    }
    if (static_cast<size_t>(outlen) != packet_len) {
      r = kErrPartialDecrypt;
      goto out;
    }

     
    if (EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_AEAD_SET_TAG, 16, tag) != 1) {
      r = kErrOpenSSL;
      goto out;
    }

     
    if (!EVP_DecryptFinal_ex(ctx_, nullptr, &outlen)) {
      r = kErrOpenSSL;
      goto out;
    }

out:
    return r;
  }