static void sha_mpint(SHA_State * s, Bignum b) { unsigned char lenbuf[4]; int len; len = (bignum_bitcount(b) + 8) / 8; PUT_32BIT(lenbuf, len); SHA_Bytes(s, lenbuf, 4); while (len-- > 0) { lenbuf[0] = bignum_byte(b, len); SHA_Bytes(s, lenbuf, 1); } memset(lenbuf, 0, sizeof(lenbuf)); }
/* * The Mines (among others) game descriptions contain the location of every * mine, and can therefore be used to cheat. * * It would be pointless to attempt to _prevent_ this form of * cheating by encrypting the description, since Mines is * open-source so anyone can find out the encryption key. However, * I think it is worth doing a bit of gentle obfuscation to prevent * _accidental_ spoilers: if you happened to note that the game ID * starts with an F, for example, you might be unable to put the * knowledge of those mines out of your mind while playing. So, * just as discussions of film endings are rot13ed to avoid * spoiling it for people who don't want to be told, we apply a * keyless, reversible, but visually completely obfuscatory masking * function to the mine bitmap. */ void obfuscate_bitmap(unsigned char *bmp, int bits, int decode) { int bytes, firsthalf, secondhalf; struct step { unsigned char *seedstart; int seedlen; unsigned char *targetstart; int targetlen; } steps[2]; int i, j; /* * My obfuscation algorithm is similar in concept to the OAEP * encoding used in some forms of RSA. Here's a specification * of it: * * + We have a `masking function' which constructs a stream of * pseudorandom bytes from a seed of some number of input * bytes. * * + We pad out our input bit stream to a whole number of * bytes by adding up to 7 zero bits on the end. (In fact * the bitmap passed as input to this function will already * have had this done in practice.) * * + We divide the _byte_ stream exactly in half, rounding the * half-way position _down_. So an 81-bit input string, for * example, rounds up to 88 bits or 11 bytes, and then * dividing by two gives 5 bytes in the first half and 6 in * the second half. * * + We generate a mask from the second half of the bytes, and * XOR it over the first half. * * + We generate a mask from the (encoded) first half of the * bytes, and XOR it over the second half. Any null bits at * the end which were added as padding are cleared back to * zero even if this operation would have made them nonzero. * * To de-obfuscate, the steps are precisely the same except * that the final two are reversed. * * Finally, our masking function. Given an input seed string of * bytes, the output mask consists of concatenating the SHA-1 * hashes of the seed string and successive decimal integers, * starting from 0. */ bytes = (bits + 7) / 8; firsthalf = bytes / 2; secondhalf = bytes - firsthalf; steps[decode ? 1 : 0].seedstart = bmp + firsthalf; steps[decode ? 1 : 0].seedlen = secondhalf; steps[decode ? 1 : 0].targetstart = bmp; steps[decode ? 1 : 0].targetlen = firsthalf; steps[decode ? 0 : 1].seedstart = bmp; steps[decode ? 0 : 1].seedlen = firsthalf; steps[decode ? 0 : 1].targetstart = bmp + firsthalf; steps[decode ? 0 : 1].targetlen = secondhalf; for (i = 0; i < 2; i++) { SHA_State base, final; unsigned char digest[20]; char numberbuf[80]; int digestpos = 20, counter = 0; SHA_Init(&base); SHA_Bytes(&base, steps[i].seedstart, steps[i].seedlen); for (j = 0; j < steps[i].targetlen; j++) { if (digestpos >= 20) { sprintf(numberbuf, "%d", counter++); final = base; SHA_Bytes(&final, numberbuf, strlen(numberbuf)); SHA_Final(&final, digest); digestpos = 0; } steps[i].targetstart[j] ^= digest[digestpos++]; }
int main(void) { struct MD5Context md5c; SHA_State sha1s; unsigned char keybuf[20], testbuf[64]; int i, j; char *p; static char *test[]={ "", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "12345678901234567890123456789012345678901234567890123456789012345678901234567890", }; static char *md5[]={ "d41d8cd98f00b204e9800998ecf8427e", "0cc175b9c0f1b6a831c399e269772661", "900150983cd24fb0d6963f7d28e17f72", "f96b697d7cb7938d525a2f31aaf161d0", "c3fcd3d76192e4007dfb496cca67e13b", "d174ab98d277d9f5a5611c2c9f419d9f", "57edf4a22be3c955ac49da2e2107b67a", }; static char *sha1[]={ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a9993e364706816aba3e25717850c26c9cd0d89d", "c12252ceda8be8994d5fa0290a47231c1d16aae3", "32d10c7b8cf96570ca04ce37f2a19d84240d3a89", "761c457bf73b14d27e9e9265c46f4b4dda11f940", "50abf5706a150990a08b2c5ea40fa0e585554732", }; printf("testing MD5...\n"); for (i = 0; i < sizeof(test) / sizeof(char *); i++) { MD5Init(&md5c); MD5Update(&md5c, test[i], strlen(test[i])); MD5Final(keybuf, &md5c); for (j = 0, p = testbuf; j < 16; j++, p += 2) sprintf(p, "%02x", keybuf[j]); printf("test %d %s!\n", i + 1, strncmp(md5[i], testbuf, sizeof(testbuf)) ? "failed" : "ok"); } printf("\ntesting SHA1...\n"); for (i = 0; i < sizeof(test) / sizeof(char *); i++) { SHA_Init(&sha1s); SHA_Bytes(&sha1s, test[i], strlen(test[i])); SHA_Final(&sha1s, keybuf); for (j = 0, p = testbuf; j < 20; j++, p += 2) sprintf(p, "%02x", keybuf[j]); printf("test %d %s!\n", i + 1, strncmp(sha1[i], testbuf, sizeof(testbuf)) ? "failed" : "ok"); } return 0; }