AES has maximum block size of 128, and key sizes like 128, 196 & 256.
I have implemented the aes algorithm like so:
int main()
{
unsigned char key[KEY_128] = "very strong key";
unsigned char plaintext[16] = "this is a test";
unsigned char ciphertext[16];
unsigned char decptext[16];
aes_ctx_t *ctx;
virtualAES::Initialize();
ctx = virtualAES::AllocateCTX(key, sizeof(key));
virtualAES::Encrypt(ctx, plaintext, ciphertext);
cout << "encrypted: " << ciphertext << endl;
virtualAES::Encrypt(ctx, ciphertext, decptext);
cout << "decrypted: " << decptext << endl;
return 0;
}
but I want to encrypt larger data than 128bits, for example string that's 512 bits long. I need somekind of a loop that splits the strings into 128bit blocks and then encrypts & joins them again, but I have hard time doing this. Could someone provide an example?
I am more familiar with C#, which has several modes of encryption exposed through the System.Security.Cryptography namespace. However I know how Cipher Block Chaining works. I'll explain it to you, but keep in mind it is really easy to mess up crypto, so this is informational only, and I hope you will find a library that does what you need done.
With cipher block chaining (CBC) here is what you do. Take your data and break it into block sizes. 128 bits is 16 bytes, so there you go. If you have less than 16 bytes in your last block, you must pad. The commonest way I know of is PKCS7 padding, which means for example if you need 3 bytes of padding at the end of your last block, you would add 0x03, 0x03, 0x03 to make it a full block.
So now you are ready to encrypt. You should have an initialization vector (IV) to start off with. Bitwise XOR that IV with your first block of plain text. Then encrypt the result the way you normally would encrypt a single block of data (ECB mode). The result is your first block of cipher text. But it is also equivalent to the IV for the next block you want to encrypt. Bitwise XOR it with the second block and encrypt. Take that encrypted block, record it, and also use it to XOR with the third block. And so on.
This process makes it so that the exact same text appearing, let's say 5 times in a document will look totally different each time it appears. So it adds more security. Despite this, the IV does not need to be kept secret at all. Passwords and salts do, IVs do not.
Related
I'm trying to perform known-text attack to obtain 32 byte key.
BlockSize is 16 byte.
Regarding this: https://crypto.stackexchange.com/a/12512
Or this: https://security.stackexchange.com/a/102110
As far as I understood:
1) Encrypt known 15 byte block
2) Encrypt known 256 16 byte blocks with different tailing byte
3) Compare blocks and get one byte of secret
void test() {
unsigned char KnownText[15];
memset(KnownText, 'A', 15);
unsigned char EncryptedText[32];
int result_size = AES_ECB.EncryptBlock(EncryptedText, KnownText, 15);
unsigned char CKnownText[16];
for (int i = 0; i < 256; ++i) {
memset(CKnownText, 'A', 16);
CKnownText[15] = i;
unsigned char Encrypted[32];
int enc_result = AES_ECB.EncryptBlock(Encrypted, CKnownText, 16);
if(memcmp(EncryptedText, Encrypted, 16) == 0) {
//match found
}
}
}
I get only one match when i=0 (suppose because 0 was appended to first 15 byte block) and it is not even any of secret key bytes.
I can encrypt any length of any known data and get encrypted result.
How can I get the key using this attack?
EncryptBlock probably does what it says is does: encrypt one block. The idea of the 15 byte first message is that you then concatenate the secret key block to it. I don't see where this happens (unless EncryptBlock is terribly badly named).
Currently the 16th byte that is encrypted is likely simply set to zero (using zero padding) by the EncryptBlock function. You may need to create a function that mimics what the server should do, including adding the server's secret to the initial message and possibly handle encrypting multiple blocks (assuming that the function doesn't already do this).
Note that this is not about retrieving the key from the block cipher, but retrieving a secret from the plaintext. This secret could have been added as some ill attempt to perform message authentication.
Up to this point, I used to decrypt files (located on an USB stick) with AES as follows:
FILE * fp = fopen(filePath, "r");
vector<char> encryptedChars;
if (fp == NULL) {
//Could not open file
continue;
}
while(true) {
int nextEncryptedChar = fgetc(fp);
if (nextEncryptedChar == EOF) {
break;
}
encryptedChars.push_back(nextEncryptedChar);
}
fclose(fp);
char encryptedFileArray[encryptedChars.size()];
int encryptedByteCount = encryptedChars.size();
for (int x = 0; x < aantalChars; x++) {
encryptedFileArray[x] = encryptedChars[x];
}
encryptedChars.clear();
AES aes;
//Decrypt the message in-place
aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
aes.decrypt(encryptedFileArray, sizeof(encryptedFileArray));
aes.clear();
This works perfectly for small files. At this point, I am opening a file from a USB stick and storing all characters into a vector and copying the vector to an array. I know that &encryptedChars[0] can be used as an array pointer as well and will save some memory.
Now I want to decrypt a file of 256Kb (as opposed to 1Kb). Copying the data into a source array will require at least 256Kb of RAM. I however only have 100Kb at my disposal and therefore, cannot create a source array containing the encrypted data.
So I tried to use the FILE * that fopen gives me as a FILE pointer, and created a new file on the same USB stick as a destination pointer. I was hoping that the decryption rounds would use the memory of the USB stick as opposed to available memory on the heap.
FILE * fp = fopen(encryptedFilePath, "r");
FILE * fpDecrypt = fopen(decryptedFilePath, "w+");
if (fp == NULL || fpDecrypt == NULL) {
//Could not open file!?
return;
}
AES aes;
//Decrypt the message in-place
aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
aes.decrypt((const char*)fp, fpDecrypt, firmwareSize);
aes.clear();
Unfortunately, the system locks up (no idea why).
Does anybody know if I can pass a FILE * to a function that expects a const char * as source and a void * as a destination?
I am using the following library: https://os.mbed.com/users/neilt6/code/AES/docs/tip/AES_8h_source.html
Thanks!
A lot of crypto libraries provide "incremental" APIs that allow a stream of data to be en/decrypted piece by piece, without having to load the stream into memory. Unfortunately, it appears that the library you're using doesn't (or, at least, does not explicitly document it).
However, if you know how CBC mode encryption works, it's possible to roll your own. Basically, all you need to do is take the last AES block (i.e. the last 16 bytes) of the previous chunk of ciphertext and use it as the IV when decrypting (or encrypting) the next block, something like this:
char buffer[1024]; // this needs to be a multiple of 16 bytes!
char ivTemp[16];
while(true) {
int bytesRead = fread(buffer, 1, sizeof(buffer), inputFile);
// save last 16 bytes of ciphertext as IV for next block
if (bytesRead == sizeof(buffer)) memcpy(ivTemp, buffer + bytesRead - 16, 16);
// decrypt the message in-place
AES aes;
aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
aes.decrypt(buffer, bytesRead);
aes.clear();
// write out decrypted data (todo: check for write errors!)
fwrite(buffer, 1, bytesRead, outputFile);
// use the saved last 16 bytes of ciphertext as IV for next block
if (bytesRead == sizeof(buffer)) memcpy(iv, ivTemp, 16);
if (bytesRead < sizeof(buffer)) break; // end of file (or read error)
}
Note that this code will overwrite the iv array. That should be OK, though, since you should never use the same IV twice anyway. (In fact, with CBC mode, the IV should be chosen by the encryptor at random, using a cryptographically secure RNG, and sent alongside the message. The usual way to do that is to simply prepend the IV to the message file.)
Also, the code above is somewhat less efficient than it needs to be, since it calls aes.setup() and thus re-runs the whole AES key expansion for each chunk. Unfortunately, I couldn't find any documented way to tell your crypto library to change the IV without re-running the setup.
However, looking at the implementation of your library, as linked by Sister Fister in the comments below, it looks like it's already replacing its internal copy of the IV with the last ciphertext block. Thus, it looks like all you really need to do is call aes.decrypt() for each block without a setup call in between, something like this:
char buffer[1024]; // this needs to be a multiple of 16 bytes!
AES aes;
aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
while(true) {
int bytesRead = fread(buffer, 1, sizeof(buffer), inputFile);
// decrypt the chunk of data in-place (continuing from previous chunk)
aes.decrypt(buffer, bytesRead);
// write out decrypted data (todo: check for write errors!)
fwrite(buffer, 1, bytesRead, outputFile);
if (bytesRead < sizeof(buffer)) break; // end of file (or read error)
}
aes.clear();
Note that this code is relying on a feature of the crypto library that does not seem to be explicitly documented, namely that calling aes.decrypt() multiple times will cause the decryptions to be chained correctly. (That's actually a pretty reasonable thing to do, for CBC mode, but you can never be sure without reading the code or finding explicit documentation saying so.) You should make sure to have a comprehensive test suite for this, and to re-run the tests whenever you upgrade the library.
Also note that I haven't tested either of these examples, so there obviously could be bugs or typos. Also, the docs for your crypto library are somewhat sparse, so it's possible that it might not work exactly like I'm assuming it does. Please test anything based on this code throughly before using it!
In general, if something doesn't fit to memory, you can resort to:
Random accessing files. Use fseek to find the position and read or write what you need. Memory requirement minimal.
Processing in batches that will fit in to memory. Memory requirement is adjustable, but the algorithm must be suitable for this.
System virtual memory, which allows you to reserve as big blocks as your system can address, you have free disk space and your system settings. This is usually transparent depending on your system.
Other paged memory mechanisms.
Since AES encryption is made in blocks of 128 bits, and you're short of memory, you should probably use random access on your file.
AES has maximum block size of 128, and key sizes like 128, 196 & 256.
I have implemented the aes algorithm like so:
int main()
{
unsigned char key[KEY_128] = "very strong key";
unsigned char plaintext[16] = "this is a test";
unsigned char ciphertext[16];
unsigned char decptext[16];
aes_ctx_t *ctx;
virtualAES::Initialize();
ctx = virtualAES::AllocateCTX(key, sizeof(key));
virtualAES::Encrypt(ctx, plaintext, ciphertext);
cout << "encrypted: " << ciphertext << endl;
virtualAES::Encrypt(ctx, ciphertext, decptext);
cout << "decrypted: " << decptext << endl;
return 0;
}
but I want to encrypt larger data than 128bits, for example string that's 512 bits long.
How to achieve?
I posted this answer elsewhere, but as it applies here as well, here you go:
I am more familiar with C#, which has several modes of encryption exposed through the System.Security.Cryptography namespace. However I know how Cipher Block Chaining works. I'll explain it to you, but keep in mind it is really easy to mess up crypto, so this is informational only, and I hope you will find a library that does what you need done.
With cipher block chaining (CBC) here is what you do. Take your data and break it into block sizes. 128 bits is 16 bytes, so there you go. If you have less than 16 bytes in your last block, you must pad. The commonest way I know of is PKCS7 padding, which means for example if you need 3 bytes of padding at the end of your last block, you would add 0x03, 0x03, 0x03 to make it a full block.
So now you are ready to encrypt. You should have an initialization vector (IV) to start off with. Bitwise XOR that IV with your first block of plain text. Then encrypt the result the way you normally would encrypt a single block of data (ECB mode). The result is your first block of cipher text. But it is also equivalent to the IV for the next block you want to encrypt. Bitwise XOR it with the second block and encrypt. Take that encrypted block, record it, and also use it to XOR with the third block. And so on.
This process makes it so that the exact same text appearing, let's say 5 times in a document will look totally different each time it appears. So it adds more security. Despite this, the IV does not need to be kept secret at all. Passwords and salts do, IVs do not.
I am using this simple function for decrypting a AES Encrypted string
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
{
int p_len = *len, f_len = 0;
unsigned char *plaintext = (unsigned char*)malloc(p_len + 128);
memset(plaintext,0,p_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
*len = p_len + f_len;
return plaintext;
}
The problem is that len is returning a value that does not match the entire decoded string. What could be the problem ?
When you say "string", I assume you mean a zero-terminated textual string. The encryption process is dependent on a cipher block size, and oftentimes padding. What's actually being encoded and decoded is up to the application... it's all binary data to the cipher. If you're textual string is smaller than what's returned from the decrypt process, your application needs to determine the useful part. So for example if you KNOW your string inside the results is zero-terminated, you can get the length doing a simple strlen. That's risky of course if you can't guarantee the input... probably better off searching the results for a null up to the decoded length...
If you are using cipher in ECB, CBC or some other chaining modes, you must pad plain text to the length, which is multiple of cipher block length. You can see a PKCS#5 standard for example. High-level functions like in OpenSSL can perform padding transparently for programmer. So, encrypted text can be larger than plain text up to additional cipher block size.
I'm building some code to read a RIFF wav file and I've bumped into something odd.
The first 4 bytes of the file header are the word RIFF in big-endian ascii coding:
0x5249 0x4646
I read this first element using:
char *fileID = new char[4];
filestream.read(fileID,4);
When I write this to screen the results are as expected:
std::cout << fileID << std::endl;
>> RIFF
Now, the next 4 bytes give the size of the file, but crucially they're little-endian.
So, I write a little function to flip the bytes, based on a union:
int flip4bytes(char* input){
union flip {int flip_int; char flip_char[4];};
flip.flip_char[0] = input[3];
flip.flip_char[1] = input[2];
flip.flip_char[2] = input[1];
flip.flip_char[3] = input[0];
return flip.flip_int;
}
This looks good to me, except when I call it, the value returned is totally wrong. Interestingly, the following code (where the bytes are not reversed!) works correctly:
int flip4bytes(char* input){
union flip {int flip_int; char flip_char[4];};
flip.flip_char[0] = input[0];
flip.flip_char[1] = input[1];
flip.flip_char[2] = input[2];
flip.flip_char[3] = input[3];
return flip.flip_int;
}
This has thoroughly confused me. Is the union somehow reversing the bytes for me?! If not, how are the bytes being converted to int correctly without being reversed?
I think there's some facet of endian-ness here that I'm ignorant to..
You are simply on a little-endian machine, and the "RIFF" string is just a string and thus neither little- nor big-endian, but just a sequence of chars. You don't need to reverse the bytes on a little-endian machine, but you need to when operating on a big-endian.
You need to figure of the endianess of your machine. #include <sys/param.h> will help you do that.
You could also use the fact that network byte order is big ended (if my memory serves me correctly - you need to check). In which case convert to big ended and use the ntohs function. That should work on any machine that you compile the code on.