  ErrorType encrypt(unsigned char* packet, uint32_t packet_len) {
    ErrorType r = kErrNone;

    // `packet` layout:
    //   <packet length> <padding length> <payload> <padding> <mac>
    uint32_t data_len = packet_len - 16;

    int outlen = 0;

    // 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_EncryptUpdate(ctx_, nullptr, &outlen, packet, 4)) {
      r = kErrOpenSSL;
      goto out;
    }
    if (outlen != 4) {
      r = kErrAADFailure;
      goto out;
    }

    // Encrypt everything but the packet length
    if (EVP_EncryptUpdate(ctx_,
                          packet + 4,
                          &outlen,
                          packet + 4,
                          data_len - 4) != 1) {
      r = kErrOpenSSL;
      goto out;
    }
    if (static_cast<size_t>(outlen) != data_len - 4) {
      r = kErrPartialEncrypt;
      goto out;
    }

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

    // Write authentication tag
    if (EVP_CIPHER_CTX_ctrl(ctx_,
                            EVP_CTRL_AEAD_GET_TAG,
                            16,
                            packet + data_len) != 1) {
      r = kErrOpenSSL;
      goto out;
    }

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

     
     
    uint32_t data_len = packet_len - 16;

    int outlen = 0;

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

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

     
    if (EVP_EncryptUpdate(ctx_,
                          packet + 4,
                          &outlen,
                          packet + 4,
                          data_len - 4) != 1) {
      r = kErrOpenSSL;
      goto out;
    }
    if (static_cast<size_t>(outlen) != data_len - 4) {
      r = kErrPartialEncrypt;
      goto out;
    }

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

     
    if (EVP_CIPHER_CTX_ctrl(ctx_,
                            EVP_CTRL_AEAD_GET_TAG,
                            16,
                            packet + data_len) != 1) {
      r = kErrOpenSSL;
      goto out;
    }

out:
    return r;
  }