|
|
|
@ -25,16 +25,18 @@ void xxtea128(uint32_t const key[4], uint32_t block[4]) {
|
|
|
|
|
// key accesses, since key is only 4 words long
|
|
|
|
|
//
|
|
|
|
|
// 3. Go through each word in the block and derive its new
|
|
|
|
|
// value based on next (y) and previous word (z),
|
|
|
|
|
// wrapping around as needed, as well as the round
|
|
|
|
|
// constant(s) and the key.
|
|
|
|
|
// value based on its current value (v[p]), the next (y)
|
|
|
|
|
// and the previous word (z), wrapping around the ends
|
|
|
|
|
// of the block as needed.
|
|
|
|
|
//
|
|
|
|
|
// The function for deriving the new value of a block is a
|
|
|
|
|
// xor of sums of xors. The first sum adds together
|
|
|
|
|
// combinations of the next and previous block, and the
|
|
|
|
|
// second sum adds together previous/bext combined with a
|
|
|
|
|
// value dependant on the round constant. The key is also
|
|
|
|
|
// mixed into the block in the first xor of second sum.
|
|
|
|
|
// The function for deriving the new value of a word is a
|
|
|
|
|
// xor of sums of xors, followed by an in-place addition.
|
|
|
|
|
// The first sum adds together combinations of the next and
|
|
|
|
|
// previous word, and the second sum adds together
|
|
|
|
|
// previous/next combined with a value dependant on the
|
|
|
|
|
// round constant. The key is also mixed into the word in
|
|
|
|
|
// the first xor of second sum. After this the result is
|
|
|
|
|
// added back into the original word.
|
|
|
|
|
//
|
|
|
|
|
// I have changed the operand order in the second xor of
|
|
|
|
|
// first add and in the second add. This is to keep the
|
|
|
|
@ -56,32 +58,12 @@ uint32_t bytes2word(unsigned char const bytes[4]) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void word2bytes(unsigned char *bytes, uint32_t word) {
|
|
|
|
|
bytes[0] = word & 0xff;
|
|
|
|
|
bytes[1] = word>>8 & 0xff;
|
|
|
|
|
bytes[2] = word>>16 & 0xff;
|
|
|
|
|
bytes[0] = word;
|
|
|
|
|
bytes[1] = word>>8;
|
|
|
|
|
bytes[2] = word>>16;
|
|
|
|
|
bytes[3] = word>>24;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void derive_subkey(uint32_t key[4], uint32_t nonce[6], uint32_t subkey[4]) {
|
|
|
|
|
// We are using an extended nonce construction with 192 bit nonces.
|
|
|
|
|
// The first 128 bits of nonce are encrypted using xxtea128 with
|
|
|
|
|
// the provided key, in order to derive a subkey that is then used
|
|
|
|
|
// alongside the remaining nonce to do the actual encryption.
|
|
|
|
|
//
|
|
|
|
|
// This is, as far as I can tell, not a standard construction. I
|
|
|
|
|
// have based it on xchacha20, with the understanding that it
|
|
|
|
|
// should not matter if the function used to derive the subkey is
|
|
|
|
|
// reversible or not, since an attacker doesn't know the original
|
|
|
|
|
// key and the original key is used only in this derivation.
|
|
|
|
|
|
|
|
|
|
subkey[0] = nonce[0];
|
|
|
|
|
subkey[1] = nonce[1];
|
|
|
|
|
subkey[2] = nonce[2];
|
|
|
|
|
subkey[3] = nonce[3];
|
|
|
|
|
|
|
|
|
|
xxtea128(key, subkey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct hashstate {
|
|
|
|
|
// A_n and B_n of the MDC-2 algorithm
|
|
|
|
|
uint32_t a[4];
|
|
|
|
@ -130,36 +112,36 @@ void compress_hash(struct hashstate *state) {
|
|
|
|
|
message[2] = bytes2word(&state->buffer[8]);
|
|
|
|
|
message[3] = bytes2word(&state->buffer[12]);
|
|
|
|
|
|
|
|
|
|
// A_i, B_i
|
|
|
|
|
uint32_t a[4], b[4];
|
|
|
|
|
memcpy(a, state->a, sizeof(a));
|
|
|
|
|
memcpy(b, state->b, sizeof(b));
|
|
|
|
|
|
|
|
|
|
// V_i = M_i ^ E(M_i, A_i)
|
|
|
|
|
xxtea128(message, a);
|
|
|
|
|
a[0] ^= message[0];
|
|
|
|
|
a[1] ^= message[1];
|
|
|
|
|
a[2] ^= message[2];
|
|
|
|
|
a[3] ^= message[3];
|
|
|
|
|
// Note: In this description A_i is the *key*, not the plaintext
|
|
|
|
|
uint32_t v[4];
|
|
|
|
|
memcpy(v, message, sizeof(v));
|
|
|
|
|
xxtea128(state->a, v);
|
|
|
|
|
v[0] ^= message[0];
|
|
|
|
|
v[1] ^= message[1];
|
|
|
|
|
v[2] ^= message[2];
|
|
|
|
|
v[3] ^= message[3];
|
|
|
|
|
|
|
|
|
|
// W_i = M_i ^ E(M_i, B_i);
|
|
|
|
|
xxtea128(message, b);
|
|
|
|
|
b[0] ^= message[0];
|
|
|
|
|
b[1] ^= message[1];
|
|
|
|
|
b[2] ^= message[2];
|
|
|
|
|
b[3] ^= message[3];
|
|
|
|
|
uint32_t w[4];
|
|
|
|
|
memcpy(w, message, sizeof(w));
|
|
|
|
|
xxtea128(state->b, w);
|
|
|
|
|
w[0] ^= message[0];
|
|
|
|
|
w[1] ^= message[1];
|
|
|
|
|
w[2] ^= message[2];
|
|
|
|
|
w[3] ^= message[3];
|
|
|
|
|
|
|
|
|
|
// A_{i+1} = V_i^L || W_i^R
|
|
|
|
|
state->a[0] = a[0];
|
|
|
|
|
state->a[1] = a[1];
|
|
|
|
|
state->a[2] = b[2];
|
|
|
|
|
state->a[3] = b[3];
|
|
|
|
|
// A_{i+1} = Vwi^L || W_i^R
|
|
|
|
|
state->a[0] = v[0];
|
|
|
|
|
state->a[1] = v[1];
|
|
|
|
|
state->a[2] = w[2];
|
|
|
|
|
state->a[3] = w[3];
|
|
|
|
|
|
|
|
|
|
// B_{i+1} = W_i^L || V_i^R
|
|
|
|
|
state->b[0] = b[0];
|
|
|
|
|
state->b[1] = b[1];
|
|
|
|
|
state->b[2] = a[2];
|
|
|
|
|
state->b[3] = a[3];
|
|
|
|
|
state->b[0] = v[0];
|
|
|
|
|
state->b[1] = v[1];
|
|
|
|
|
state->b[2] = w[2];
|
|
|
|
|
state->b[3] = w[3];
|
|
|
|
|
|
|
|
|
|
// Mark that we have consumed the buffer
|
|
|
|
|
state->length = 0;
|
|
|
|
@ -169,7 +151,7 @@ void feed_hash(struct hashstate *state, unsigned char input[], size_t length) {
|
|
|
|
|
// Invariant: The buffer will be filled somewhere between 0 and 15
|
|
|
|
|
// when we enter this loop. This is because once it reaches 16, the
|
|
|
|
|
// hash compression function is executed.
|
|
|
|
|
for (size_t i; i < length; i++) {
|
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
|
|
|
// Must not overflow the internat counter. In practice we will not
|
|
|
|
|
// hit this.
|
|
|
|
|
assert(state->totalbits <= UINT64_MAX - 8);
|
|
|
|
@ -200,8 +182,15 @@ void finalize_hash(struct hashstate *state, unsigned char hash[32]) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the number of bits, and do one last compression
|
|
|
|
|
word2bytes(&state->buffer[state->length+=4], state->totalbits>>32);
|
|
|
|
|
word2bytes(&state->buffer[state->length+=4], state->totalbits & 0xffffffffUL);
|
|
|
|
|
state->buffer[8] = state->totalbits >> 56;
|
|
|
|
|
state->buffer[9] = state->totalbits >> 48;
|
|
|
|
|
state->buffer[10] = state->totalbits >> 40;
|
|
|
|
|
state->buffer[11] = state->totalbits >> 32;
|
|
|
|
|
state->buffer[12] = state->totalbits >> 24;
|
|
|
|
|
state->buffer[13] = state->totalbits >> 16;
|
|
|
|
|
state->buffer[14] = state->totalbits >> 8;
|
|
|
|
|
state->buffer[15] = state->totalbits;
|
|
|
|
|
state->length += 8;
|
|
|
|
|
compress_hash(state);
|
|
|
|
|
|
|
|
|
|
// Extract the hash state
|
|
|
|
|