I am trying to implement a bit of Rust / Zenroom (the context is performance testing on very small devices for DP-3T privacy preserving content tracking[3]): Rust (i is 0 .. 10) let mut iv = [0u8; 16]; iv[12..16].copy_from_slice(&i.to_be_bytes()); let mut block = GenericArray::clone_from_slice(& iv); cipher.encrypt_block(&mut block); Zen (i is 0 .. 10): PRF = SHA256:hmac(ACK.secret_day_key, BROADCAST_KEY) PRG = AES.ctr(PRF, OCTET.from_number(0), OCTET.from_number(i)) Using native OpenSSL its EVP interface for the AES 128 bit CTR mode. I've disabled padding and am passing it 128 bit blocks. But what I get back from EVP_EncryptFinal() is an 'extra' block at the end: uint8_t aes_key[16], iv[16], plain[16]; bzero(iv, 16); *(uint32_t*)(iv+12) = htonl(i); // big endian or network order. EVP_EncryptInit(ctx, cipher, aes_key, iv); EVP_CIPHER_CTX_set_padding(ctx, 0); int len = 0, len2 = 0; EVP_EncryptUpdate(ctx, outptr, &len, plain, 16); assert(len <= 16); // ok = len is sizeof(plain) == 16 EVP_EncryptFinal(ctx, outptr + len, &len2); assert(len + len2 != 16); // actual result is len2 = 16 (expected 0) and 32 byts in total. Last 16 bytes of outptr are 0. Why is that ? Or how should this be done correctly ? With kind regards, Dw. 0: full code at:https://gist.github.com/dirkx/53143596fa935b6de96e6521d82797b6 1: man page bit: EVP_CIPHER_CTX_set_padding() enables or disables padding. By default encryption operations are padded using standard block padding and the padding is checked and removed when decrypting. If the pad parameter is zero then no padding is performed, the total amount of data encrypted or decrypted must then be a multiple of the block size or an error will occur. 3: https://github.com/DP-3T/documents/blob/master/DP3T%20White%20Paper.pdf