/* Code for breaking enRUPT in block cipher mode with a related key attack. Breaks the full enRUPT for 256-bit keys, 128-bit block with about 2^65 chosen plaintexts. The block hashing mode of enRUPT is also likely affected, but I didn't check. Some explanation here... Both XXTEA and enRUPT are m[i] += f(m[i-1], m[i+1], ...) I broke XXTEA with an attack that uses a delta so that f(m[i-1], m[i+1], ...) == f(m[i-1]+delta, m[i+1], ...) and f(m[i-1], m[i+1], ...) == f(m[i-1], m[i+1]+delta, ...) with a relatively high probability. This attack can't be directly used for enRUPT: - there's only one such delta for left-to-right propagation: 0x80000000. - there's no such delta for right-to-left propagation. But, we can apply such a delta in the key! So, I rigged spiffysolver, which is my special root finder designed for crypto, to find such deltas. Here are the best ones I found: 02128292 29849/16777216 92021282 17197/16777216 49010941 14545/16777216 90109434 13031/16777216 96021282 11070/16777216 82920212 10772/16777216 01094149 10714/16777216 The first one is clearly the best, so we'll use that. The round keys are used cyclically. For 256-bit key & 128-bit block each key word is used only 8 times. We have to pass 7. The probability that the best delta works is roughly 2^-9.14, though it's pretty weakly estimated. Nonetheless, it's clear that it's definitely enough to break enRUPT. Running multithreaded on a Core i7 920, I was able to break 40 of 64 rounds in a few hours. 04012009: initial release */ #include #include #include #include #include // a PRNG void hrng_init(uint32_t seed); uint32_t hrng(); /* Note: we lower the number of rounds with this. The default number of rounds for 256-bit keys, 128-bit block is 64. */ #define RNDS 32 #define rot(a,b) (((a)<<(b))|((a)>>(32-(b)))) #define rotr(a,b) rot(a,32-(b)) #define er1(k) (rotr(2*x[(r-1)%xw]^x[(r+1)%xw]^k^r,8)*9^k) void enRUPT(uint32_t *x, const uint32_t xw, uint32_t *key, const uint32_t kw) { uint32_t r, s=4, n=s*(2*xw+kw); for (r=1; r<=RNDS; r++) x[r%xw] ^= er1(key[r%kw]); } #define LEN 4 #define KLEN 8 /* This is the function for requesting chosen plaintexts. */ void encrypt(uint32_t *m, uint32_t *relation) { // This is the key we'll try to recover by requesting encrypted blocks: uint32_t k[KLEN]={0xCAFEBABE, 0x56756756, 0x55555555, 0xDEADBEEF, 0x46354646, 0xab78f783, 0x12345678, 0x87654318}; int i; for(i=0;i=16) { int i; for(i=0;i<4*4;i+=4) { // manual unroll for great glory // each chunk generates one dword hrng_state[0]^= hrng_state[2]; hrng_state[3]+= hrng_state[0]; hrng_state[2] =~hrng_state[2]; hrng_state[3] = rot(hrng_state[3],8)-hrng_state[0]; hrng_out[i+0] = hrng_state[0]; hrng_state[1]^= hrng_state[3]; hrng_state[0]+= hrng_state[1]; hrng_state[3] =~hrng_state[3]; hrng_state[0] = rot(hrng_state[0],8)-hrng_state[1]; hrng_out[i+1] = hrng_state[1]; hrng_state[2]^= hrng_state[0]; hrng_state[1]+= hrng_state[2]; hrng_state[0] =~hrng_state[0]; hrng_state[1] = rot(hrng_state[1],8)-hrng_state[2]; hrng_out[i+2] = hrng_state[2]; hrng_state[3]^= hrng_state[1]; hrng_state[2]+= hrng_state[3]; hrng_state[1] =~hrng_state[1]; hrng_state[2] = rot(hrng_state[2],8)-hrng_state[3]; hrng_out[i+3] = hrng_state[3]; } hrng_pos=0; } return hrng_out[hrng_pos++]; }