AES_CBC_encrypt_buffer() and buffer size
skater-boy opened this issue ยท 11 comments
Hi,
I'm testing tiny-AES-c into an embbed system (stm32), setup is for 128-bit AES.
Invoking AES_CBC_encrypt_buffer() as in test.c yields to an output stream of 64 byte which overwrite the input buffer in[].
Anyway a test with an online AES calculator produces 80 output byte.
I don't understand where my error is. Maybe a #define in aes.h?
https://www.dropbox.com/s/ogattv0xrjxqiby/tiny-AES128-stm32.png?dl=0
Can you point me to the right direction?
Thank you
Hi @skater-boy
Can you point me to the right direction?
I hope so, but I think I need more information to understand the problem entirely.
AES CBC is a block cipher. That means it takes data in certain-sized chunks and encrypts them without changing the size of the chunk. I.e. a message encrypted by AES-CBC has the same size encrypted as unencrypted.
You mention that two different implementations give 64 and 80 bytes output - what was the input?
Another common error for newcomers, is to assume that ASCII text input yields ASCII text output.
This is not the case :) So if you are using strlen() or something similar to determine "the size of the output", this is a wrong approach.
You mention that two different implementations give 64 and 80 bytes output - what was the input?
Same input for both test.
stm32: the 64 byte input buffer had been pre-loaded with the data as in test.c line208 (0x6b, 0xc1, 0xbe, ..., 0x10)
This same data had been used as input for the on-line AES calculator (URL cryptii.com/pipes/aes-encryption) as in previous attachment Dropbox link.
Another common error for newcomers, is to assume that ASCII text input yields ASCII text output.
Right, without an hex or base64 encoding of the output the output itself can't be assumed as ASCII data.
This is not the case :) So if you are using strlen() or something similar to determine "the size of the output", this is a wrong approach.
stm32: I used an input buffer larger than 64 byte and filled byte from 64th onward with known data. Since there is no salt, 2 consecutive encryption of the same input with the same IV and same key should give the same output. So I tested with both 0x00 and 0xff as filler (as said: locatios 64th onward only)
Byte0 to byte63 filled with the test pattern discussed above, of course.
Call to aes_cbc_encrypt_buffer() done with 64 as buffer's size.
With a debugger is easy to inspect the buffer content after the encryption.
On-line AES: the web page itself says "80 byte encoded" and I effectively see 80 bytes on its right pane.
stm32: I used an input buffer larger than 64 byte and filled byte from 64th onward with known data. Since there is no salt, 2 consecutive encryption of the same input with the same IV and same key should give the same output.
The IV is the salt here, but yes you should always see the same output keeping key, iv and input constant.
So I tested with both 0x00 and 0xff as filler (as said: locatios 64th onward only)
No need for allocating (or filling/padding) more than 64 bytes, if your input is 64 bytes long.
On-line AES: the web page itself says "80 byte encoded" and I effectively see 80 bytes on its right pane.
Looking at the web page, it's clear to me that a comparison is not meaningful.
You've input 64 bytes of data (in hex-format), but the page says it has "encoded" 80 bytes.
If I open https://cryptii.com/pipes/aes-encryption and look at the default view, I see 16 bytes of data:
"Input data = 6b c1 be e2 2e 40 9f 96 e9 3d 7e 11 73 93 17 2a" .... but the text says "encoded 32 bytes".
It looks like the web page is encrypting an ASCII text/hex representation of the data, instead of the binary data itself or something.
I don't know what the web page does underneath the bonnet, but you cannot meaningfully compare the outputs of encrypting 64 bytes with this library and 80 bytes using the web page.
I cannot help you any further, so long as I don't understand how the web page works.
Using web pages with input forms for AES encryption for comparison, is often a source of confusion, since the web pages often do something slightly different from this library (e.g. work with BASE64-encoded data instead of binary data, or apply null-termination to strings automatically or apply a form of padding) without clearly specifying all details to the user.
Here is an example of the above: #133
I recommend that you find another oracle to compare against, like e.g. Python if you know that language.
Here are two code snippets, demoing the example you've tried on the web page.
E.g. the Python code and the C code give the same output "1F37A5A9636458C8703CD82B9FC19778363B6E36DEDDFD6F28E1BDA2C59749284EA244BF4EB73B9F1C80C7F54BD536B5107985055D6B7DD797C203B742D8310D"
from Crypto.Cipher import AES
msg = "\x6b\xca\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10"
iv = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c"
cipher = AES.new(key, AES.MODE_CBC, iv)
output = cipher.encrypt(msg)
print("".join(["%.02X" % ord(o) for o in output]))
# output = 1F37A5A9636458C8703CD82B9FC19778363B6E36DEDDFD6F28E1BDA2C59749284EA244BF4EB73B9F1C80C7F54BD536B5107985055D6B7DD797C203B742D8310D
and the C version
#include "aes.h"
#include <assert.h>
int main()
{
uint8_t input[] = { 0x6b, 0xca, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 };
assert(sizeof(input) == 64);
uint8_t iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
assert(sizeof(iv) == 16);
uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
assert(sizeof(key) == 16);
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CBC_encrypt_buffer(&ctx, input, sizeof(input));
printf("\n");
int i;
for (i = 0; i < 64; ++i)
{
printf("%.02X", input[i]);
}
printf("\n");
// gcc t.c aes.c -DCBC=1 && ./a.out
// >1F37A5A9636458C8703CD82B9FC19778363B6E36DEDDFD6F28E1BDA2C59749284EA244BF4EB73B9F1C80C7F54BD536B5107985055D6B7DD797C203B742D8310D
return 0;
}
The IV is the salt here, but yes you should always see the same output keeping key, iv and input constant.
ok
No need for allocating (or filling/padding) more than 64 bytes, if your input is 64 bytes long.
ok
Looking at the web page, it's clear to me that a comparison is not meaningful.
You've input 64 bytes of data (in hex-format), but the page says it has "encoded" 80 bytes.
Ok, I'm now agree with you.
I was put on wrong direction due to 1. my limited known of AES and 2. the first part of the conversion that was the same in the web page result as in result of tiny-AES
If I open https://cryptii.com/pipes/aes-encryption and look at the default view, I see 16 bytes of data:
"Input data = 6b c1 be e2 2e 40 9f 96 e9 3d 7e 11 73 93 17 2a" .... but the text says "encoded 32 bytes".
For a reason unknown to me, the web page reported by me starts with this data already loaded. Really don't know why.
Anyway I ran my final test on that web-page with the data on the screenshot as in my first post (64 bytes as input yieldings to "wrong" 80 bytes output)
I cannot help you any further, so long as I don't understand how the web page works.
I ran with more tests but still unexpected output on that wab-page.
Using web pages with input forms for AES encryption for comparison, is often a source of confusion, since the web pages often do something slightly different from this library (e.g. work with BASE64-encoded data instead of binary data, or apply null-termination to strings automatically or apply a form of padding) without clearly specifying all details to the user.
I'm now of the same opinion.
Switched to openssl because it supports a -nosalt option. Still experimenting with openssl.
My final goal is AES-encode data with PC (I'm under GNU-Linux) and decode the data with tiny-aes-c.
I'm pretty sure that along the encoded data, the embedded system with tiny-aes-c will also need the IV to successfully decode.
Am I right?
Thank you for your professional support.
I recommend that you find another oracle to compare against, like e.g. Python if you know that language.
Here are two code snippets, demoing the example you've tried on the web page.
E.g. the Python code and the C code give the same output "1F37A5A9636458C8703CD82B9FC19778363B6E36DEDDFD6F28E1BDA2C59749284EA244BF4EB73B9F1C80C7F54BD536B5107985055D6B7DD797C203B742D8310D"
Thank you, very useful reference!
Hi @skater-boy
Thank you for your professional support.
No problem! I will gladly help :)
My final goal is AES-encode data with PC (I'm under GNU-Linux) and decode the data with tiny-aes-c.
There are several other stand-alone libraries, if the OpenSSL-dependency is annoying you. This is indeed possible, and you could also just use the tiny-AES-code in both ends...
I'm pretty sure that along the encoded data, the embedded system with tiny-aes-c will also need the IV to successfully decode.
Am I right?
Yes you are right. The IV is not secret though. It can be sent in plain-text and shared without compromising secrecy. The IV should change regularly between sessions and maybe/preferably be random.
Note that qua the way CBC-algorithms work (it's a so-called "mode of operation" of a block-cipher, see the wikipedia-page) the IV changes for each 16-byte block. It gets passed the result of each encrypt/decrypt-calls. Be mindful of that when you use the CBC algorithm. Use one of these functions, to control the IV:
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
Let me know what else I can do for you
I was put on wrong direction due to 1. my limited known of AES and 2. the first part of the conversion that was the same in the web page result as in result of tiny-AES
Oh, and regarding 1), I really think the wikipedia article can give you a deeper knowledge about how each block-cipher modes of operation really work. I find the pictures really helpfuld to my understanding.
Let me know what else I can do for you
I finally managed it to work with both tiny-aes-c and openssl. Your support had been very useful!
With openssl I do the encryption and with tiny-aes-c the decryption.
Have the things working with openssl is good because this tool can be found almost everywhere, not just on GNU-Linux machines.
This is the openssl side:
echo -ne "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10" | openssl enc -aes-128-cbc -K '2b7e151628aed2a6abf7158809cf4f3c' -iv '000102030405060708090a0b0c0d0e0f' -nosalt -nopad | xxd -ps -c 256
echo's -ne switches are used to suppress the final new-line char (-n) and to ask "echo" to process the hex codes (-e) introduced by incoming \x
openssl's -K switch is used to pass the key in hex format (note that 'K' is upper case here)
Also note that here the -nopad switch is used just as safety check: openssl in fact will arise an error if one specify a key which isn't of the right size (aes-128 = 128-bits = 16 bytes, aes-192 = 192-bits = 24 bytes, ...). Without the -nopad option a too short key, input data, ... will be automatically padded by openssl.
Final pipe into xxd is used to convert the binary (unreadable) output of openssl into readable hex, of course if one needs the binary data he/she can just rediect the output to a file.
Thanks for the thorough explanation @skater-boy :)
I will take note of this as a good reference for how to interoperate with OpenSSL ๐