I cannot understand this behavior of pointers to structure and XOR

This is the first time I've been working with structure pointers, and I can't figure out what is going on here. My test uses the main property of xor, which says x ^ y ^ y = x, but not in C?

The code below is in my main program and accurately recovers all the letters of the "test" (which I go to print on the screen, but I emptied a lot of garbage so that this question is short (er)). The structure "aes" refers to this definition:

typedef uint32_t word; struct aes { word iv[4]; word key[8]; word state[4]; word schedule[56]; }; 

As the context may suggest, an encapsulating project is an implementation of AES (I'm trying to speed up my current one by trying new methods).

In my testing, make_string and make_state work reliably, even in the functions in question, but for the sake of reference:

 void make_string (word in[], char out[]) { for (int i = 0; i < 4; i++) { out[(i * 4) + 0] = (char) (in[i] >> 24); out[(i * 4) + 1] = (char) (in[i] >> 16); out[(i * 4) + 2] = (char) (in[i] >> 8); out[(i * 4) + 3] = (char) (in[i] ); } } void make_state(word out[], char in[]) { for (int i = 0; i < 4; i++) { out[i] = (word) (in[(i * 4) + 0] << 24) ^ (word) (in[(i * 4) + 1] << 16) ^ (word) (in[(i * 4) + 2] << 8) ^ (word) (in[(i * 4) + 3] ); } } 

Anyway, here is a block that works. This is the kind of functionality that I am trying to do in a modular way by clipping it in a function:

 char test[16] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p' }; aes cipher; struct aes * work; work = &cipher; make_state(work->state, test); work->state[0] ^= 0xbc6378cd; work->state[0] ^= 0xbc6378cd; make_string(work->state, test); 

And although this code works, doing the same thing by passing it to a function does not:

 void encipher_block (struct aes * work, char in[]) { make_state(work->state, in); work->state[0] ^= 0xff00cd00; make_string(work->state, in); } void decipher_block (struct aes * work, char in[]) { make_state(work->state, in); work->state[0] ^= 0xff00cd00; make_string(work->state, in); } 

However, by removing make_state and make_string calls for both encryption and decryption, it works as expected!

 make_state(work->state, test); encipher_block(&cipher, test); decipher_block(&cipher, test); make_string(work->state, test); 

So, to clarify, I have no problem! I just want to understand this behavior.

+4
source share
2 answers

Change char to unsigned char . char can be signed and probably located on your system, which causes problems when converting to other integer types and during porting.

In the expression (char) (in[i] >> 24) in make_string 32-bit integer is converted to an 8-bit integer (in your C implementation). This expression can convert values ​​to char that are not represented in char , in particular values ​​from 128 to 255. According to C 2011 6.3.1.3 3, the result is determined by the implementation or the signal determined by the implementation is raised.

In the expression (word) (in[(i * 4) + 3] ) in make_state , in[…] is a char , which is a signed 8-bit integer (in your C implementation). This char converted to int for regular whole promotions defined in C 2011 6.3.1.1 2. If char negative, then the resulting int is negative. Then, when it is converted to a word that is unsigned, the effect is that the bit sign is replicated in high 24 bits. For example, if the char value is -166 (0x90), the result will be 0xffffff90 , but you want 0x00000090 .

Change char to unsigned char throughout this code.

Also, in make_state , in[(i * 4) + 0] should be dropped to word before shifting to the left. This is because it starts as an unsigned char , which advances to int before the shift. If it has some value with a high bit set, for example 0x80, then shifting it to the left by 24 bits gives a value that cannot be represented in int , for example 0x80000000. Per C 2011 6.5.7 4, the behavior is then undefined.

This will not be a problem in most C implementations; two additions are usually used for signed integers, and the result will be completed as desired. In addition, I expect this to be a model situation that compiler developers are developing, since this is a very common code structure. However, to improve portability, casting to word avoids overflow.

+2
source

The make_state() function overwrites the array passed in the first argument. If you put the bodies of encipher_block() and decipher_block() in a line, you get the following:

 /* encipher_block inline */ make_state(work->state, in); work->state[0] ^= 0xff00cd00; make_string(work->state, in); /* decipher_block inline */ make_state(work->state, in); /* <-- Here the problem */ work->state[0] ^= 0xff00cd00; make_string(work->state, in); 
0
source

All Articles