diff --git a/ctaes.c b/ctaes.c index decc66d..eade167 100644 --- a/ctaes.c +++ b/ctaes.c @@ -14,8 +14,11 @@ #include "ctaes.h" +#include #include +#define BLOCK_SIZE 16 // Standard AES block size + /* Slice variable slice_i contains the i'th bit of the 16 state variables in this order: * 0 1 2 3 * 4 5 6 7 @@ -592,41 +595,101 @@ static void AESCBC_decrypt(const AES_state* rounds, uint8_t* iv, int nk, size_t } } -void AES128_CBC_init(AES128_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv) { + +/** + * Computes the required length of padded plaintext. + * Ensures that the output length is always a multiple of BLOCK_SIZE. + */ +static inline size_t compute_padded_length(size_t plaintext_len) { + size_t pad_size = BLOCK_SIZE - (plaintext_len % BLOCK_SIZE); + return plaintext_len + pad_size; +} + +/* + * Applies PKCS#7 Padding https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 to the input plaintext. + */ +static inline void PKCS7_pad(const unsigned char *plain, unsigned char *padded_plain, size_t plaintext_len, size_t padded_len) { + size_t pad_size = padded_len - plaintext_len; + + memcpy(padded_plain, plain, plaintext_len); // Copy original data + memset(padded_plain + plaintext_len, pad_size, pad_size); // Apply padding +} + +/** + * Removes PKCS#7 padding from decrypted plaintext and validate padding bytes + * Returns true if padding is valid; false otherwise. + */ +static inline bool PKCS7_unpad(unsigned char *plaintext, size_t ciphertext_len, size_t* plaintext_len) { + if (ciphertext_len == 0 || ciphertext_len % BLOCK_SIZE != 0) return false; // Invalid length + + size_t pad_size = plaintext[ciphertext_len - 1]; // Last byte indicates padding size + if (pad_size == 0 || pad_size > BLOCK_SIZE || pad_size > ciphertext_len) return false; + + // Verify that all padding bytes match pad_size + for (size_t i = ciphertext_len - pad_size; i < ciphertext_len; i++) { + if (plaintext[i] != pad_size) return false; + } + + *plaintext_len = ciphertext_len - pad_size; // Exclude padding from plaintext + return true; +} + +void AES128_CBC_init(AES128_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv, size_t cipher_len, size_t plaintext_len) { AES128_init(&(ctx->ctx), key16); - memcpy(ctx->iv, iv, 16); + memcpy(ctx->data.iv, iv, BLOCK_SIZE); + ctx->data.ciphertext_len = cipher_len; + ctx->data.plaintext_len = plaintext_len; } -void AES192_CBC_init(AES192_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv) { +void AES192_CBC_init(AES192_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv, size_t cipher_len, size_t plaintext_len) { AES192_init(&(ctx->ctx), key16); - memcpy(ctx->iv, iv, 16); + memcpy(ctx->data.iv, iv, BLOCK_SIZE); + ctx->data.ciphertext_len = cipher_len; + ctx->data.plaintext_len = plaintext_len; } -void AES256_CBC_init(AES256_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv) { +void AES256_CBC_init(AES256_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv, size_t cipher_len, size_t plaintext_len) { AES256_init(&(ctx->ctx), key16); - memcpy(ctx->iv, iv, 16); + memcpy(ctx->data.iv, iv, BLOCK_SIZE); + ctx->data.ciphertext_len = cipher_len; + ctx->data.plaintext_len = plaintext_len; } void AES128_CBC_encrypt(AES128_CBC_ctx* ctx, size_t blocks, unsigned char* encrypted, const unsigned char* plain) { - AESCBC_encrypt(ctx->ctx.rk, ctx->iv, 10, blocks, encrypted, plain); + ctx->data.padded_len = compute_padded_length(ctx->data.plaintext_len); + unsigned char padded_plain[ctx->data.padded_len]; // Allocate based on computed padding + PKCS7_pad(plain, padded_plain, ctx->data.plaintext_len, ctx->data.padded_len); + ctx->data.ciphertext_len = ctx->data.padded_len; + AESCBC_encrypt(ctx->ctx.rk, ctx->data.iv, 10, blocks, encrypted, padded_plain); } -void AES128_CBC_decrypt(AES128_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted) { - AESCBC_decrypt(ctx->ctx.rk, ctx->iv, 10, blocks, plain, encrypted); +bool AES128_CBC_decrypt(AES128_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted) { + AESCBC_decrypt(ctx->ctx.rk, ctx->data.iv, 10, blocks, plain, encrypted); + return PKCS7_unpad(plain, ctx->data.ciphertext_len, &ctx->data.plaintext_len); } void AES192_CBC_encrypt(AES192_CBC_ctx* ctx, size_t blocks, unsigned char* encrypted, const unsigned char* plain) { - AESCBC_encrypt(ctx->ctx.rk, ctx->iv, 12, blocks, encrypted, plain); + ctx->data.padded_len = compute_padded_length(ctx->data.plaintext_len); + unsigned char padded_plain[ctx->data.padded_len]; // Allocate based on computed padding + PKCS7_pad(plain, padded_plain, ctx->data.plaintext_len, ctx->data.padded_len); + ctx->data.ciphertext_len = ctx->data.padded_len; + AESCBC_encrypt(ctx->ctx.rk, ctx->data.iv, 12, blocks, encrypted, padded_plain); } -void AES192_CBC_decrypt(AES192_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted) { - AESCBC_decrypt(ctx->ctx.rk, ctx->iv, 12, blocks, plain, encrypted); +bool AES192_CBC_decrypt(AES192_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted) { + AESCBC_decrypt(ctx->ctx.rk, ctx->data.iv, 12, blocks, plain, encrypted); + return PKCS7_unpad(plain, ctx->data.ciphertext_len, &ctx->data.plaintext_len); } void AES256_CBC_encrypt(AES256_CBC_ctx* ctx, size_t blocks, unsigned char* encrypted, const unsigned char* plain) { - AESCBC_encrypt(ctx->ctx.rk, ctx->iv, 14, blocks, encrypted, plain); + ctx->data.padded_len = compute_padded_length(ctx->data.plaintext_len); + unsigned char padded_plain[ctx->data.padded_len]; // Allocate based on computed padding + PKCS7_pad(plain, padded_plain, ctx->data.plaintext_len, ctx->data.padded_len); + ctx->data.ciphertext_len = ctx->data.padded_len; + AESCBC_encrypt(ctx->ctx.rk, ctx->data.iv, 14, blocks, encrypted, padded_plain); } -void AES256_CBC_decrypt(AES256_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted) { - AESCBC_decrypt(ctx->ctx.rk, ctx->iv, 14, blocks, plain, encrypted); +bool AES256_CBC_decrypt(AES256_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted) { + AESCBC_decrypt(ctx->ctx.rk, ctx->data.iv, 14, blocks, plain, encrypted); + return PKCS7_unpad(plain, ctx->data.ciphertext_len, &ctx->data.plaintext_len); } diff --git a/ctaes.h b/ctaes.h index c916a50..4518624 100644 --- a/ctaes.h +++ b/ctaes.h @@ -7,6 +7,7 @@ #ifndef CTAES_H #define CTAES_H +#include #include #include @@ -14,6 +15,13 @@ typedef struct { uint16_t slice[8]; } AES_state; +typedef struct { + uint8_t iv[16]; /* iv is updated after each use */ + size_t plaintext_len; /* Actual plaintext length */ + size_t padded_len; /* Length of plaintext after padding */ + size_t ciphertext_len; /* Length of ciphertext */ +} AES_CBC_data; + typedef struct { AES_state rk[11]; } AES128_ctx; @@ -28,17 +36,17 @@ typedef struct { typedef struct { AES128_ctx ctx; - uint8_t iv[16]; /* iv is updated after each use */ + AES_CBC_data data; } AES128_CBC_ctx; typedef struct { AES192_ctx ctx; - uint8_t iv[16]; /* iv is updated after each use */ + AES_CBC_data data; } AES192_CBC_ctx; typedef struct { AES256_ctx ctx; - uint8_t iv[16]; /* iv is updated after each use */ + AES_CBC_data data; } AES256_CBC_ctx; void AES128_init(AES128_ctx* ctx, const unsigned char* key16); @@ -53,16 +61,16 @@ void AES256_init(AES256_ctx* ctx, const unsigned char* key32); void AES256_encrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16); void AES256_decrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16); -void AES128_CBC_init(AES128_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv); +void AES128_CBC_init(AES128_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv, size_t cipher_len, size_t plaintext_len); void AES128_CBC_encrypt(AES128_CBC_ctx* ctx, size_t blocks, unsigned char* encrypted, const unsigned char* plain); -void AES128_CBC_decrypt(AES128_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted); +bool AES128_CBC_decrypt(AES128_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted); -void AES192_CBC_init(AES192_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv); +void AES192_CBC_init(AES192_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv, size_t cipher_len, size_t plaintext_len); void AES192_CBC_encrypt(AES192_CBC_ctx* ctx, size_t blocks, unsigned char* encrypted, const unsigned char* plain); -void AES192_CBC_decrypt(AES192_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted); +bool AES192_CBC_decrypt(AES192_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted); -void AES256_CBC_init(AES256_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv); +void AES256_CBC_init(AES256_CBC_ctx* ctx, const unsigned char* key16, const uint8_t* iv, size_t cipher_len, size_t plaintext_len); void AES256_CBC_encrypt(AES256_CBC_ctx* ctx, size_t blocks, unsigned char* encrypted, const unsigned char* plain); -void AES256_CBC_decrypt(AES256_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted); +bool AES256_CBC_decrypt(AES256_CBC_ctx* ctx, size_t blocks, unsigned char* plain, const unsigned char *encrypted); #endif /* CTAES_H */ diff --git a/test.c b/test.c index e063afa..0c27150 100644 --- a/test.c +++ b/test.c @@ -6,6 +6,7 @@ #include "ctaes.h" +#include #include #include #include @@ -21,7 +22,6 @@ typedef struct { int keysize; const char* key; const char* iv; - int nblocks; const char* plain; const char* cipher; } ctaes_cbc_test; @@ -50,19 +50,46 @@ static const ctaes_test ctaes_tests[] = { static const ctaes_cbc_test ctaes_cbc_tests[] = { /* AES-CBC test vectors from NIST sp800-38a. */ { - 128, "2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090a0b0c0d0e0f", 4, + // Full block length will pad 1 more block + 128, "2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090a0b0c0d0e0f", "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", - "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7" + "a12b0386d815a2c18e9c23b63a7adbf2fd5576ec89b64bef8fff20c67a2db525d6ddfa8efd7b0fcd869ffe84564e603e6df965ae0dc86e097bdf29bfaa45cd57718fb6e84747912749339489f2c17c4d" }, { - 192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "000102030405060708090a0b0c0d0e0f", 4, - "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", - "4f021db243bc633d7178183a9fa071e8b4d9ada9ad7dedf4e5e738763f69145a571b242012fb7ae07fa9baac3df102e008b0e27988598881d920a9e64f5615cd" + // Incomplete block length pad will pad the remaining bytes to fill block + 128, "2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090a0b0c0d0e0f", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b41", + "a12b0386d815a2c18e9c23b63a7adbf2fd5576ec89b64bef8fff20c67a2db525d6ddfa8efd7b0fcd869ffe84564e603e3501ddd9be60093899819bbc115cfe86" }, { - 256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090a0b0c0d0e0f", 4, + // Full block length will pad 1 more block + 192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "000102030405060708090a0b0c0d0e0f", "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", - "f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461b2eb05e2c39be9fcda6c19078c6a9d1b" + "17701a9d29c91a94ceed723c34e87abe1c96845ca8b7e8586dfef2fa6bed24098a52cee8d76db67bfde21553d31c2833f77eb59500ac4903bc7076b18465d0ea7e38ba13918e47c316e53ee336370345" + }, + { + // Incomplete block length pad will pad the remaining bytes to fill block + 192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "000102030405060708090a0b0c0d0e0f", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417b", + "17701a9d29c91a94ceed723c34e87abe1c96845ca8b7e8586dfef2fa6bed24098a52cee8d76db67bfde21553d31c28339ad0a856f760c64a6bbb3df2dbecdb53" + }, + { + // Full block length will pad 1 more block + 256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090a0b0c0d0e0f", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445", + "f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d39f23369a9d9bacfa530e26304231461164e1f6488d14e7407e98486d3da86f0" + }, + { + // Incomplete block length pad will pad the remaining bytes to fill block + 256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090a0b0c0d0e0f", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac", + "f58c4c04d6e5f1ba779eabfb5f7bfbd68d99fefe25debc6c0b1eedaac5e98e7e" + }, + /* AES-CBC padded test vector randomly generated. */ + { + 128, "d2e4c2c3b3c1fe4878a7bf99807ffe2f", "14e7903c078a28514885abac58618a30", + "5468697320697320612036332d62797465206c6f6e6720706c61696e74657874206578616d706c6520666f72204145532d434243206d6f6465212121", + "35866e380595f69503f3700004d2e57a732522827550158b0e64ee9307d8d58477699f0706f33690aed4147987f9ab8485611ba9662bf2e41aefa170810f625d" } }; @@ -130,47 +157,83 @@ int main(void) { } } for (i = 0; i < sizeof(ctaes_cbc_tests) / sizeof(ctaes_cbc_tests[0]); i++) { + // Define AES block size (16 bytes for AES). + const int block_size = 16; + + // Retrieve the test case. const ctaes_cbc_test* test = &ctaes_cbc_tests[i]; - unsigned char key[32], iv[16], plain[4 * 16], cipher[4 * 16], ciphered[4 * 16], deciphered[4 * 16]; + + // Compute plaintext length (hex string length divided by 2). + const int plain_len = strlen(test->plain) / 2; + + // Compute the padded plaintext length for PKCS#7. + // If plaintext is already a multiple of block_size, an extra block is needed. + const int padded_plain_len = (plain_len % block_size == 0) + ? (plain_len + block_size) + : (plain_len + (block_size - (plain_len % block_size))); + + // Compute the number of AES blocks. + const int blocks = padded_plain_len / block_size; + + // Compute ciphertext length (hex string length divided by 2). + const int cipher_len = strlen(test->cipher) / 2; + + // Allocate buffers for encryption/decryption. + unsigned char key[32]; // Key buffer (up to 256-bit keys, i.e., 32 bytes). + unsigned char iv[block_size]; // Initialization vector (IV). + unsigned char plain[plain_len]; // Buffer to store original plaintext. + unsigned char cipher[cipher_len]; // Expected ciphertext. + unsigned char ciphered[padded_plain_len]; // Encrypted ciphertext (including padding). + unsigned char deciphered[padded_plain_len]; // Decrypted plaintext (including padding). + + // Ensure key size is valid (AES supports only 128, 192, or 256 bits). assert(test->keysize == 128 || test->keysize == 192 || test->keysize == 256); - assert(test->nblocks == 4); - from_hex(iv, 16, test->iv); - from_hex(plain, test->nblocks * 16, test->plain); - from_hex(cipher, test->nblocks * 16, test->cipher); + + // Convert hex-encoded test data into byte arrays. + from_hex(iv, block_size, test->iv); // Convert IV from hex string to byte array. + from_hex(plain, plain_len, test->plain); // Convert plaintext from hex string to byte array. + from_hex(cipher, cipher_len, test->cipher); // Convert expected ciphertext from hex string to byte array. + switch (test->keysize) { case 128: { AES128_CBC_ctx ctx; - from_hex(key, 16, test->key); - AES128_CBC_init(&ctx, key, iv); - AES128_CBC_encrypt(&ctx, test->nblocks, ciphered, plain); - AES128_CBC_init(&ctx, key, iv); - AES128_CBC_decrypt(&ctx, test->nblocks, deciphered, cipher); + AES128_CBC_init(&ctx, key, iv, /*cipher_len*/0, plain_len); + AES128_CBC_encrypt(&ctx, blocks, ciphered, plain); + + // Initialize decryption with the ciphertext length. + AES128_CBC_init(&ctx, key, iv, ctx.data.ciphertext_len, /*plain_len*/0); + assert(AES128_CBC_decrypt(&ctx, blocks, deciphered, ciphered)); + assert(ctx.data.plaintext_len == plain_len); break; } case 192: { AES192_CBC_ctx ctx; - from_hex(key, 24, test->key); - AES192_CBC_init(&ctx, key, iv); - AES192_CBC_encrypt(&ctx, test->nblocks, ciphered, plain); - AES192_CBC_init(&ctx, key, iv); - AES192_CBC_decrypt(&ctx, test->nblocks, deciphered, cipher); + AES192_CBC_init(&ctx, key, iv, /*cipher_len*/0, plain_len); + AES192_CBC_encrypt(&ctx, blocks, ciphered, plain); + + // Initialize decryption with the ciphertext length. + AES192_CBC_init(&ctx, key, iv, ctx.data.ciphertext_len, /*plain_len*/0); + assert(AES192_CBC_decrypt(&ctx, blocks, deciphered, ciphered)); + assert(ctx.data.plaintext_len == plain_len); break; } case 256: { AES256_CBC_ctx ctx; - from_hex(key, 32, test->key); - AES256_CBC_init(&ctx, key, iv); - AES256_CBC_encrypt(&ctx, test->nblocks, ciphered, plain); - AES256_CBC_init(&ctx, key, iv); - AES256_CBC_decrypt(&ctx, test->nblocks, deciphered, cipher); + AES256_CBC_init(&ctx, key, iv, /*cipher_len*/0, plain_len); + AES256_CBC_encrypt(&ctx, blocks, ciphered, plain); + + // Initialize decryption with the ciphertext length. + AES256_CBC_init(&ctx, key, iv, ctx.data.ciphertext_len, /*plain_len*/0); + assert(AES256_CBC_decrypt(&ctx, blocks, deciphered, ciphered)); + assert(ctx.data.plaintext_len == plain_len); break; } } - if (memcmp(cipher, ciphered, test->nblocks * 16)) { + if (memcmp(cipher, ciphered, cipher_len)) { fprintf(stderr, "E(key=\"%s\", plain=\"%s\") != \"%s\"\n", test->key, test->plain, test->cipher); fail++; } - if (memcmp(plain, deciphered, test->nblocks * 16)) { + if (memcmp(plain, deciphered, plain_len)) { fprintf(stderr, "D(key=\"%s\", cipher=\"%s\") != \"%s\"\n", test->key, test->cipher, test->plain); fail++; }