/* BlackRock cipher (h/t Marsh Ray @marshray for this idea) This is a randomization/reshuffling function based on a crypto "Feistel network" as described in the paper: 'Ciphers with Arbitrary Finite Domains' by John Black and Phillip Rogaway http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf This is a crypto-like construction that encrypts an arbitrary sized range. Given a number in the range [0..9999], it'll produce a mapping to a distinct different number in the same range (and back again). In other words, it randomizes the order of numbers in a sequence. For example, it can be used to randomize the sequence [0..9]: 0 -> 6 1 -> 4 2 -> 8 3 -> 1 4 -> 9 5 -> 3 6 -> 0 7 -> 5 8 -> 2 9 -> 7 As you can see on the right hand side, the numbers are in random order, and they don't repeat. This is create for port scanning. We can take an index variable and increment it during a scan, then use this function to randomize it, yet be assured that we've probed every IP and port within the range. The cryptographic strength of this construction depends upon the number of rounds, and the exact nature of the inner "READ()" function. Because it's a Feistel network, that "READ()" function can be almost anything. We don't care about cryptographic strength, just speed, so we are using a trivial READ() function. This is a class of "format-preserving encryption". There are probably better constructions than what I'm using. */ #include "crypto-blackrock.h" #include "pixie-timer.h" #include "util-malloc.h" #include #include #include #include #include #include #include #if defined(_MSC_VER) #define inline _inline #endif /*************************************************************************** * It's an s-box. You gotta have an s-box ***************************************************************************/ const unsigned char sbox[256] = { 0x91, 0x58, 0xb3, 0x31, 0x6c, 0x33, 0xda, 0x88, 0x57, 0xdd, 0x8c, 0xf2, 0x29, 0x5a, 0x08, 0x9f, 0x49, 0x34, 0xce, 0x99, 0x9e, 0xbf, 0x0f, 0x81, 0xd4, 0x2f, 0x92, 0x3f, 0x95, 0xf5, 0x23, 0x00, 0x0d, 0x3e, 0xa8, 0x90, 0x98, 0xdd, 0x20, 0x00, 0x03, 0x69, 0x0a, 0xca, 0xba, 0x12, 0x08, 0x41, 0x6e, 0xb9, 0x86, 0xe4, 0x50, 0xf0, 0x84, 0xe2, 0xb3, 0xb3, 0xc8, 0xb5, 0xb2, 0x2d, 0x18, 0x70, 0x0a, 0xd7, 0x92, 0x90, 0x9e, 0x1e, 0x0c, 0x1f, 0x08, 0xe8, 0x06, 0xfd, 0x85, 0x2f, 0xaa, 0x5d, 0xcf, 0xf9, 0xe3, 0x55, 0xb9, 0xfe, 0xa6, 0x7f, 0x44, 0x3b, 0x4a, 0x4f, 0xc9, 0x2f, 0xd2, 0xd3, 0x8e, 0xdc, 0xae, 0xba, 0x4f, 0x02, 0xb4, 0x76, 0xba, 0x64, 0x2d, 0x07, 0x9e, 0x08, 0xec, 0xbd, 0x52, 0x29, 0x07, 0xbb, 0x9f, 0xb5, 0x58, 0x6f, 0x07, 0x55, 0xb0, 0x34, 0x74, 0x9f, 0x05, 0xb2, 0xdf, 0xa9, 0xc6, 0x2a, 0xa3, 0x5d, 0xff, 0x10, 0x40, 0xb3, 0xb7, 0xb4, 0x63, 0x6e, 0xf4, 0x3e, 0xee, 0xf6, 0x49, 0x52, 0xe3, 0x11, 0xb3, 0xf1, 0xfb, 0x60, 0x48, 0xa1, 0xa4, 0x19, 0x7a, 0x2e, 0x90, 0x28, 0x90, 0x8d, 0x5e, 0x8c, 0x8c, 0xc4, 0xf2, 0x4a, 0xf6, 0xb2, 0x19, 0x83, 0xea, 0xed, 0x6d, 0xba, 0xfe, 0xd8, 0xb6, 0xa3, 0x5a, 0xb4, 0x48, 0xfa, 0xbe, 0x5c, 0x69, 0xac, 0x3c, 0x8f, 0x63, 0xaf, 0xa4, 0x42, 0x25, 0x50, 0xab, 0x65, 0x80, 0x65, 0xb9, 0xfb, 0xc7, 0xf2, 0x2d, 0x5c, 0xe3, 0x4c, 0xa4, 0xa6, 0x8e, 0x07, 0x9c, 0xeb, 0x41, 0x93, 0x65, 0x44, 0x4a, 0x86, 0xc1, 0xf6, 0x2c, 0x97, 0xfd, 0xf4, 0x6c, 0xdc, 0xe1, 0xe0, 0x28, 0xd9, 0x89, 0x7b, 0x09, 0xe2, 0xa0, 0x38, 0x74, 0x4a, 0xa6, 0x5e, 0xd2, 0xe2, 0x4d, 0xf3, 0xf4, 0xc6, 0xbc, 0xa2, 0x51, 0x58, 0xe8, 0xae, }; /*************************************************************************** ***************************************************************************/ void blackrock_init(struct BlackRock *br, uint64_t range, uint64_t seed, unsigned rounds) { double foo = sqrt(range * 1.0); /* This algorithm gets very non-random at small numbers, so I'm going * to try to fix some constants here to make it work. It doesn't have * to be good, since it's kinda pointless having ranges this small */ switch (range) { case 0: br->a = 0; br->b = 0; break; case 1: br->a = 1; br->b = 1; break; case 2: br->a = 1; br->b = 2; break; case 3: br->a = 2; br->b = 2; break; case 4: case 5: case 6: br->a = 2; br->b = 3; break; case 7: case 8: br->a = 3; br->b = 3; break; default: br->range = range; br->a = (uint64_t)(foo - 2); br->b = (uint64_t)(foo + 3); break; } while (br->a * br->b <= range) br->b++; br->rounds = rounds; br->seed = seed; br->range = range; } /*************************************************************************** * The inner round/mixer function. In DES, it's a series of S-box lookups, * which ***************************************************************************/ static inline uint64_t READ(uint64_t r, uint64_t R, uint64_t seed) { uint64_t r0, r1, r2, r3; #define GETBYTE(R,n) ((((R)>>(n*8))^seed^r)&0xFF) R ^= (seed << r) ^ (seed >> (64 - r)); r0 = sbox[GETBYTE(R,0)]<< 0 | sbox[GETBYTE(R,1)]<< 8; r1 = (sbox[GETBYTE(R,2)]<<16UL | sbox[GETBYTE(R,3)]<<24UL)&0x0ffffFFFFUL; r2 = sbox[GETBYTE(R,4)]<< 0 | sbox[GETBYTE(R,5)]<< 8; r3 = (sbox[GETBYTE(R,6)]<<16UL | sbox[GETBYTE(R,7)]<<24UL)&0x0ffffFFFFUL; R = r0 ^ r1 ^ r2<<23UL ^ r3<<33UL; return R; } /*************************************************************************** * * NOTE: * the names in this function are cryptic in order to match as closely * as possible the pseudocode in the following paper: * http://www.cs.ucdavis.edu/~rogaway/papers/subset.pdf * Read that paper in order to understand this code. ***************************************************************************/ static inline uint64_t ENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed) { uint64_t L, R; unsigned j; uint64_t tmp; L = m % a; R = m / a; for (j=1; j<=r; j++) { if (j & 1) { tmp = (L + READ(j, R, seed)) % a; } else { tmp = (L + READ(j, R, seed)) % b; } L = R; R = tmp; } if (r & 1) { return a * L + R; } else { return a * R + L; } } /*************************************************************************** ***************************************************************************/ static inline uint64_t UNENCRYPT(unsigned r, uint64_t a, uint64_t b, uint64_t m, uint64_t seed) { uint64_t L, R; unsigned j; uint64_t tmp; if (r & 1) { R = m % a; L = m / a; } else { L = m % a; R = m / a; } for (j=r; j>=1; j--) { if (j & 1) { tmp = READ(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = a - (tmp%a); if (tmp == a) tmp = 0; } else { tmp = (R - tmp); tmp %= a; } } else { tmp = READ(j, L, seed); if (tmp > R) { tmp = (tmp - R); tmp = b - (tmp%b); if (tmp == b) tmp = 0; } else { tmp = (R - tmp); tmp %= b; } } R = L; L = tmp; } return a * R + L; } /*************************************************************************** ***************************************************************************/ uint64_t blackrock_shuffle(const struct BlackRock *br, uint64_t m) { uint64_t c; c = ENCRYPT(br->rounds, br->a, br->b, m, br->seed); while (c >= br->range) c = ENCRYPT(br->rounds, br->a, br->b, c, br->seed); return c; } /*************************************************************************** ***************************************************************************/ uint64_t blackrock_unshuffle(const struct BlackRock *br, uint64_t m) { uint64_t c; c = UNENCRYPT(br->rounds, br->a, br->b, m, br->seed); while (c >= br->range) c = UNENCRYPT(br->rounds, br->a, br->b, c, br->seed); return c; } /*************************************************************************** * This function called only during selftest/regression-test. ***************************************************************************/ static unsigned blackrock_verify(struct BlackRock *br, uint64_t max) { unsigned char *list; uint64_t i; unsigned is_success = 1; uint64_t range = br->range; /* Allocate a list of 1-byte counters */ list = CALLOC(1, (size_t)((range