tiny-AES-c AES256 CTR interoperability
naquad opened this issue · 3 comments
I'm experiencing an issue with the latest tiny-AES-c and go1.20.6 decryption for AES256 in CTR mode. I didn't find any related information in the docs or the comments. Please clarify if the library is interoperable or am I doing something wrong?
Thank you.
Test C code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define AES256 1
#define CTR 1
#define CBC 0
#define ECB 0
#include "aes.h" // Tiny-AES-c
#define BLOCK_SIZE AES_BLOCKLEN
// Function to print data as hex
void print_hex(FILE *f, const char *title, uint8_t* data, size_t length) {
if (title) {
fprintf(f, "%s: ", title);
}
for (size_t i = 0; i < length; i++) {
fprintf(f, "%02x", data[i]);
}
fprintf(f, "\n");
}
int main() {
// Generate a random IV
uint8_t iv[AES_BLOCKLEN];
for (size_t i = 0; i < BLOCK_SIZE; i++) {
iv[i] = rand() % 256;
}
char* input = "MY KEY 123456789";
// Generate sequential key
uint8_t key[AES_KEYLEN];
for (uint8_t i = 0; i < AES_KEYLEN; ++i) {
key[i] = i;
}
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
// Encrypt the string
uint8_t output[1024];
size_t length = strlen(input);
memcpy(output, input, length);
AES_CTR_xcrypt_buffer(&ctx, output, length);
// Print the IV, key, and encrypted data
print_hex(stderr, "IV ", iv, BLOCK_SIZE);
print_hex(stderr, "Key ", key, AES_KEYLEN);
print_hex(stderr, "Cipher", output, strlen(input));
// Pass to the decryptor
print_hex(stdout, NULL, iv, BLOCK_SIZE);
print_hex(stdout, NULL, key, AES_KEYLEN);
print_hex(stdout, NULL, output, strlen(input));
return 0;
}
Test Go code:
package main
import (
"bufio"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Read the IV from stdin
scanner.Scan()
ivtxt := scanner.Text()
iv, err := hex.DecodeString(ivtxt)
if err != nil {
panic(err)
}
// Read the key from stdin
scanner.Scan()
keytxt := scanner.Text()
key, err := hex.DecodeString(keytxt)
if err != nil {
panic(err)
}
// Read the encrypted data from stdin
scanner.Scan()
data := scanner.Text()
fmt.Printf("IV : %s\n", ivtxt)
fmt.Printf("Key : %s\n", keytxt)
fmt.Printf("Cipher: %s\n", data)
encryptedData, err := hex.DecodeString(data)
if err != nil {
panic(err)
}
// Create a new AES cipher block
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// Create a new CTR (Counter) mode stream
stream := cipher.NewCTR(block, iv)
// Decrypt the data
stream.XORKeyStream(encryptedData, encryptedData)
// Print the decrypted data
fmt.Printf("Decrypted Data: %s\n", string(encryptedData))
}
The output:
$ gcc -o crypt ./crypt.c aes.c
$ ./crypt | go run decrypt.go
./crypt | go run ./decrypt.go
IV : 67c6697351ff4aec29cdbaabf2fbe346
Key : 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Cipher: b5e844a448c8441e7d14c59b15885df1
IV : 67c6697351ff4aec29cdbaabf2fbe346
Key : 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Cipher: b5e844a448c8441e7d14c59b15885df1
Decrypted Data: >�a��K���ђ�r�
The same issue is observed using python:
import sys
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import codecs
ivtxt = sys.stdin.readline().strip()
iv = codecs.decode(ivtxt, 'hex')
keytxt = sys.stdin.readline().strip()
key = codecs.decode(keytxt, 'hex')
data = sys.stdin.readline().strip()
encrypted_data = codecs.decode(data, 'hex')
# Create a new AES cipher
cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
# Print the decrypted data
print('IV :', ivtxt)
print('Key :', keytxt)
print('Cipher:', data)
print("Decrypted Data:", decrypted_data)
The output:
$ ./crypt | python3 decrypt.py
IV : 67c6697351ff4aec29cdbaabf2fbe346
Key : 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Cipher: b5e844a448c8441e7d14c59b15885df1
IV : 67c6697351ff4aec29cdbaabf2fbe346
Key : 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Cipher: b5e844a448c8441e7d14c59b15885df1
Decrypted Data: b'>\xe1a\x98\x07\x8eK\x83\xfa\x8a\x1d\xd1\x92\xe6r\xc5'
To make sure it is not a Go/Python error:
import sys
from random import randint
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import codecs
iv = bytes([randint(0, 255) for _ in range(16)])
key = bytes(range(32))
# Create a new AES cipher
cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
decryptor = cipher.encryptor()
crypto = decryptor.update(b'MY TEST INPUT') + decryptor.finalize()
print(codecs.encode(iv, 'hex').decode('utf-8'))
print(codecs.encode(key, 'hex').decode('utf-8'))
print(codecs.encode(crypto, 'hex').decode('utf-8'))
The output:
$ python3 crypt.py | go run ./decrypt.go
IV : 917b2bc01beaf40dc5e14ab387f3ee59
Key : 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Cipher: fc9908431c8cdf134153a6cb78
Decrypted Data: MY TEST INPUT
The same code with AES128 is working as expected.
Ok, after reading the library code skip all this. For those who hit the same issue:
aes.c includes aes.h and all defines should be either in the aes.h or on the command line (gcc -DAES256=1
).
Updating the aes.h and uncommeting #define AES256 1
fixes the issue.
My apologies for the confusion.