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

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

    int outlen;

    uint8_t seqbuf[4] = {0};
    ((uint8_t*)(seqbuf))[0] = (seqno >> 24) & 0xff;
    ((uint8_t*)(seqbuf))[1] = (seqno >> 16) & 0xff;
    ((uint8_t*)(seqbuf))[2] = (seqno >> 8) & 0xff;
    ((uint8_t*)(seqbuf))[3] = seqno & 0xff;

    if (is_etm_) {
      // Encrypt everything but 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;
      }

      // HMAC over unencrypted packet length and ciphertext
      {
        unsigned int outlen = hmac_len_;
        if (HMAC_Init_ex(ctx_hmac_, nullptr, 0, nullptr, nullptr) != 1
            || HMAC_Update(ctx_hmac_, seqbuf, sizeof(seqbuf)) != 1
            || HMAC_Update(ctx_hmac_, packet, data_len) != 1
            || HMAC_Final(ctx_hmac_, packet + data_len, &outlen) != 1) {
          r = kErrOpenSSL;
          goto out;
        }
        if (outlen != hmac_len_) {
          r = kErrBadHMACLen;
          goto out;
        }
      }
    } else {
      // HMAC over plaintext
      {
        unsigned int outlen = hmac_len_;
        if (HMAC_Init_ex(ctx_hmac_, nullptr, 0, nullptr, nullptr) != 1
            || HMAC_Update(ctx_hmac_, seqbuf, sizeof(seqbuf)) != 1
            || HMAC_Update(ctx_hmac_, packet, data_len) != 1
            || HMAC_Final(ctx_hmac_, packet + data_len, &outlen) != 1) {
          r = kErrOpenSSL;
          goto out;
        }
        if (outlen != hmac_len_) {
          r = kErrBadHMACLen;
          goto out;
        }
      }

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

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

     
     
    uint32_t data_len = packet_len - hmac_len_;

    int outlen;

    uint8_t seqbuf[4] = {0};
    ((uint8_t*)(seqbuf))[0] = (seqno >> 24) & 0xff;
    ((uint8_t*)(seqbuf))[1] = (seqno >> 16) & 0xff;
    ((uint8_t*)(seqbuf))[2] = (seqno >> 8) & 0xff;
    ((uint8_t*)(seqbuf))[3] = seqno & 0xff;

    if (is_etm_) {
       
      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;
      }

       
      {
        unsigned int outlen = hmac_len_;
        if (HMAC_Init_ex(ctx_hmac_, nullptr, 0, nullptr, nullptr) != 1
            || HMAC_Update(ctx_hmac_, seqbuf, sizeof(seqbuf)) != 1
            || HMAC_Update(ctx_hmac_, packet, data_len) != 1
            || HMAC_Final(ctx_hmac_, packet + data_len, &outlen) != 1) {
          r = kErrOpenSSL;
          goto out;
        }
        if (outlen != hmac_len_) {
          r = kErrBadHMACLen;
          goto out;
        }
      }
    } else {
       
      {
        unsigned int outlen = hmac_len_;
        if (HMAC_Init_ex(ctx_hmac_, nullptr, 0, nullptr, nullptr) != 1
            || HMAC_Update(ctx_hmac_, seqbuf, sizeof(seqbuf)) != 1
            || HMAC_Update(ctx_hmac_, packet, data_len) != 1
            || HMAC_Final(ctx_hmac_, packet + data_len, &outlen) != 1) {
          r = kErrOpenSSL;
          goto out;
        }
        if (outlen != hmac_len_) {
          r = kErrBadHMACLen;
          goto out;
        }
      }

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

out:
    return r;
  }