Skip to content

Instantly share code, notes, and snippets.

@erev0s
Last active November 3, 2023 07:49
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erev0s/4c13a5173b607bcea2a1f273981ccdfc to your computer and use it in GitHub Desktop.
Save erev0s/4c13a5173b607bcea2a1f273981ccdfc to your computer and use it in GitHub Desktop.

Example of AES in CBC mode with PKCS7 padding using the tiny-AES implementation of kokke.

Files needed besides the test.c found below

Once you have all the files simply run make

This gist is part of an article about tiny AES written here https://erev0s.com/blog/tiny-aes-cbc-mode-pkcs7-padding-written-c/

Update 16/10/2020
  • As noted by Sarah there is a small bug in the code of pkcs7_padding.c. I have attached to this gist a corrected version of it. The line 41 is the only thing that was changed.
#include "pkcs7_padding.h"
int pkcs7_padding_pad_buffer( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus ){
uint8_t pad_byte = modulus - ( data_length % modulus ) ;
if( data_length + pad_byte > buffer_size ){
return -pad_byte;
}
int i = 0;
while( i < pad_byte){
buffer[data_length+i] = pad_byte;
i++;
}
return pad_byte;
}
int pkcs7_padding_valid( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus ){
uint8_t expected_pad_byte = modulus - ( data_length % modulus ) ;
if( data_length + expected_pad_byte > buffer_size ){
return 0;
}
int i = 0;
while( i < expected_pad_byte ){
if( buffer[data_length + i] != expected_pad_byte){
return 0;
}
i++;
}
return 1;
}
size_t pkcs7_padding_data_length( uint8_t * buffer, size_t buffer_size, uint8_t modulus ){
/* test for valid buffer size */
if( buffer_size % modulus != 0 ||
buffer_size < modulus ){
return 0;
}
uint8_t padding_value;
padding_value = buffer[buffer_size-1];
/* test for valid padding value */
if( padding_value < 1 || padding_value > modulus ){
return buffer_size;
}
/* buffer must be at least padding_value + 1 in size */
if( buffer_size < padding_value + 1 ){
return 0;
}
uint8_t count = 1;
buffer_size --;
for( ; count < padding_value ; count++){
buffer_size --;
if( buffer[buffer_size] != padding_value ){
return 0;
}
}
return buffer_size;
}
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define CBC 1
#include "aes.h"
#include "pkcs7_padding.c"
static void test_encrypt_cbc(void);
int main(void)
{
int exit=0;
#if defined(AES256)
printf("\nTesting AES256\n\n");
#elif defined(AES192)
printf("\nTesting AES192\n\n");
#elif defined(AES128)
printf("\nTesting AES128\n\n");
#else
printf("You need to specify a symbol between AES128, AES192 or AES256. Exiting");
return 0;
#endif
test_encrypt_cbc();
return exit;
}
static void test_encrypt_cbc(void)
{
//Initialization Vector
uint8_t iv[] = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };
uint8_t i;
char* report = "my super secret thing that needs to remain that way!";
char* key = "thisIstheKey";
int dlen = strlen(report);
int klen = strlen(key);
printf("THE PLAIN TEXT STRING = ");
for (i=0; i<dlen;i++){
printf("%c", report[i]);
}
printf("\n");
//Proper Length of report
int dlenu = dlen;
if (dlen % 16) {
dlenu += 16 - (dlen % 16);
printf("The original length of the STRING = %d and the length of the padded STRING = %d\n", dlen, dlenu);
}
//Proper length of key
int klenu = klen;
if (klen % 16) {
klenu += 16 - (klen % 16);
printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
}
// Make the uint8_t arrays
uint8_t hexarray[dlenu];
uint8_t kexarray[klenu];
// Initialize them with zeros
memset( hexarray, 0, dlenu );
memset( kexarray, 0, klenu );
// Fill the uint8_t arrays
for (int i=0;i<dlen;i++) {
hexarray[i] = (uint8_t)report[i];
}
for (int i=0;i<klen;i++) {
kexarray[i] = (uint8_t)key[i];
}
int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );
printf("The padded STRING in hex is = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
printf("The padded key in hex is = ");
for (i=0; i<klenu;i++){
printf("%02x",kexarray[i]);
}
printf("\n");
// In case you want to check if the padding is valid
int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
printf("Is the pkcs7 padding valid report = %d | key = %d\n", valid, valid2);
//start the encryption
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, kexarray, iv);
// encrypt
AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);
printf("the encrypted STRING = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
// reset the iv !! important to work!
AES_ctx_set_iv(&ctx,iv);
// start decryption
AES_CBC_decrypt_buffer(&ctx, hexarray, dlenu);
size_t actualDataLength = pkcs7_padding_data_length( hexarray, dlenu, 16);
printf("The actual data length (without the padding) = %ld\n", actualDataLength);
printf("the decrypted STRING in hex = ");
for (i=0; i<actualDataLength;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
}
@mrdc
Copy link

mrdc commented Oct 3, 2021

Hello!

We should also add

  if ( data_length % modulus == 0) {
    return 0;
  }

to pkcs7_padding_pad_buffer for cases when data_length % modulus == 0.

@theloukou
Copy link

Hey there, i found your code while trying to implement some kind of super basic AES in my code. I use ECB (i know it is unsafe).
I noticed that the AES_ECB_encrypt() function works only in a single block of data. I added a simple for loop to go through the full buffer like this.

chunk_len = 16; // 16 is based on 128bit key, might need adjust for longer keys, have not tested
uint8_t blocks = buffer_len / chunk_len;
for (uint8_t chunk=0; chunk<blocks; chunk++){
   AES_ECB_encrypt(&ctx, buffer+(chunk*chunk_len));
}

@takeyoshi
Copy link

I noticed that the AES_ECB_encrypt() function works only in a single block of data

Then I'd say the author was very prudent implementing it that way. As you have noticed, ECB-mode can leak info that could eventually lead to discovering the key... however, if used ONLY on a single block, ECB is perfectly safe :-)
The danger comes from >1 blocks.

@ashraf267
Copy link

ashraf267 commented Nov 3, 2023

Hello @erev0s Thanks for your detailed article. The above codes works perfectly when I compile using the "Makefile." However, I would like to compile it without using the Makefile. I am saying something like: "gcc test.c -o testaes" to get a "testaes.exe". I have tried it but I keep getting:
C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x3b8): undefined reference to pkcs7_padding_pad_buffer' C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x3e1): undefined reference to pkcs7_padding_pad_buffer'
C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x4d0): undefined reference to pkcs7_padding_valid' C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x4f9): undefined reference to pkcs7_padding_valid'
C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x539): undefined reference to AES_init_ctx_iv' C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x558): undefined reference to AES_CBC_encrypt_buffer'
C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x5ce): undefined reference to AES_ctx_set_iv' C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x5ed): undefined reference to AES_CBC_decrypt_buffer'
C:\Users\anon\AppData\Local\Temp\cckK4TmB.o:test.c:(.text+0x60a): undefined reference to `pkcs7_padding_data_length'
collect2.exe: error: ld returned 1 exit status

I would appreciate any help to resolve this. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment