pythonc++cryptographyaestrezor

Encryption with AES-128-CTR using trezor-crypto giving different results in python


I need to work with data which was encrypted using the aes-128-ctr implementation of trezor-crypto in python. However, using pycryptodome in python to encrypt the same data with the same parameters giving me a different result.

C++ code using trezor-crypto library

#include <vector>
#include <iostream>
#include <cassert>
#include "TrezorCrypto/aes.h"

std::vector<uint8_t> iv = HexToBytes("cd26feb0cb51469d06b8134251da1d02");
std::vector<uint8_t> key = HexToBytes("130886bab757196acdd97243cad73d74571c462ed8c8791afbfce94ca6a37a1c");
std::vector<uint8_t> plaintext = HexToBytes("48656c6c6f20576f726c6421"); // = Hello World! in utf-8

aes_encrypt_ctx ctx;
auto result = aes_encrypt_key128(key.data(), &ctx);
assert(result == EXIT_SUCCESS);
std::vector<uint8_t> encrypted(plaintext.size());
result = aes_ctr_encrypt(plaintext.data(), encrypted.data(), plaintext.size(), iv.data(), aes_ctr_cbuf_inc, &ctx)
assert(result == EXIT_SUCCESS);
std::cout << BytesToHex(encrypted.data(), encrypted.size()) << std::endl;
// e61da4e002a75e857a3a2954

I double-checked the HexToBytes/BytesToHex helpers which produce the exact same byte sequence as bytes.fromhex/bytes.hex methods in python!

Python code using PyCryptoDome

from Crypto.Util import Counter
from Crypto.Cipher import AES

iv = bytes.fromhex("cd26feb0cb51469d06b8134251da1d02")
key = bytes.fromhex("130886bab757196acdd97243cad73d74571c462ed8c8791afbfce94ca6a37a1c")
plaintext = bytes.fromhex("48656c6c6f20576f726c6421")

ctr = Counter.new(128, initial_value=int.from_bytes(iv))
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
encrypted = aes.encrypt(plaintext)
print(encrypted.hex())
# c80f8d90f05dc9346f972b62

Both scripts use the exact same data but produce different results (e61da4e002a75e857a3a2954 in c++/trezor-crypto and c80f8d90f05dc9346f972b62 in python/pycryptodome).

My best guess is that the Counter setup in the python code is wrong but I can't find the exact issue.

Is there something wrong with the python code or does trezor-crypto's functions produce wrong results?


Solution

  • After following the advice of J_H's answer, I realized the key I use is 32-byte (which is aes-256-ctr) instead of 16-byte long. Since the PyCryptoDome aes implementation handles encrypting the key itself, it (correctly) used aes-256-ctr while my c++ implementation was using aes-128-ctr without key length check.

    So for my example, the fix is to use aes_encrypt_key (which checks the key length and would chose aes_encrypt_key256 for my key) instead of aes_encrypt_key128 .

    #include <vector>
    #include <iostream>
    #include <cassert>
    #include "TrezorCrypto/aes.h"
    
    std::vector<uint8_t> iv = HexToBytes("cd26feb0cb51469d06b8134251da1d02");
    std::vector<uint8_t> key = HexToBytes("130886bab757196acdd97243cad73d74571c462ed8c8791afbfce94ca6a37a1c");
    std::vector<uint8_t> plaintext = HexToBytes("48656c6c6f20576f726c6421"); // = Hello World! in utf-8
    
    aes_encrypt_ctx ctx;
    auto result = aes_encrypt_key(key.data(), key.size(), &ctx); // let trezor-crypto check and chose keylength
    assert(result == EXIT_SUCCESS);
    std::vector<uint8_t> encrypted(plaintext.size());
    result = aes_ctr_encrypt(plaintext.data(), encrypted.data(), plaintext.size(), iv.data(), aes_ctr_cbuf_inc, &ctx)
    assert(result == EXIT_SUCCESS);
    std::cout << BytesToHex(encrypted.data(), encrypted.size()) << std::endl;
    // c80f8d90f05dc9346f972b62