While reading the ChaCha20 cipher's source code, I noticed something unusual. The algorithm's constants (like 0x61707865) aren't converted for endianness, yet this doesn't cause issues across different endian architectures.
Logically, 0x61707865 would be stored as 65 78 70 61 on little-endian and 61 70 78 65 on big-endian systems, which should produce different encryption results. However, the code makes no endianness adjustments for these constants (unlike the key loading with LOAD32_LE).
Why doesn't this cause problems?
#define LOAD32_LE(SRC) load32_le(SRC)
static inline uint32_t
load32_le(const uint8_t src[4])
{
#ifdef NATIVE_LITTLE_ENDIAN
uint32_t w;
memcpy(&w, src, sizeof w);
return w;
#else
uint32_t w = (uint32_t) src[0];
w |= (uint32_t) src[1] << 8;
w |= (uint32_t) src[2] << 16;
w |= (uint32_t) src[3] << 24;
return w;
#endif
}
#define U32C(v) (v##U)
static void
chacha_keysetup(chacha_ctx *ctx, const uint8_t *k)
{
ctx->input[0] = U32C(0x61707865); // Why doesn't there need to be special handling here?
ctx->input[1] = U32C(0x3320646e);
ctx->input[2] = U32C(0x79622d32);
ctx->input[3] = U32C(0x6b206574);
ctx->input[4] = LOAD32_LE(k + 0);
ctx->input[5] = LOAD32_LE(k + 4);
ctx->input[6] = LOAD32_LE(k + 8);
ctx->input[7] = LOAD32_LE(k + 12);
ctx->input[8] = LOAD32_LE(k + 16);
ctx->input[9] = LOAD32_LE(k + 20);
ctx->input[10] = LOAD32_LE(k + 24);
ctx->input[11] = LOAD32_LE(k + 28);
}
Constants / literals in C are always displayed with the most significant bit to the left, regardless of the architecture. We're humans, we expect more significant digits to the left, less significant to the right when displaying numbers (when using the Arabic decimal system anyway). However, the bit index starts at the right, so in that sense it is actually little endian.
Memory is usually shown with the lowest index to the right instead. So when storing the value the byte with the lowest index within the word can be directly mapped to the lowest index in memory. This will visually reverse the order. Of course, the computer doesn't know about left and right and it doesn't need to perform any reversal.
If you think that's weird, then don't forget that all byte values displayed in hexadecimals are always shown as with the most significant bit to the left (i.e. index 0 to the right), even when number values are shown in memory on a little endian machine. So if you would look at a number in 4 bytes, you'd first get the following indexing for the 4 bit nibbles 10 32 54 76, e.g. 1 would be 01 00 00 00
in hexadecimal. Yeah.
Don't worry though, the actual CPU or memory modules don't have any notion of "left" and "right" when addressing bits / bytes / words and whatnot.