If you need random numbers for crypto, you need *real* random numbers, and there's only one safe place to get them: the OS, which watches the hardware interrupts and has access to nanosecond-resolution timers to give you unpredictable values.

Windows |
UNIX |

#pragma comment(lib, "advapi32.lib") BYTE buf[32]; // however much you need H CRYPTPROV h; if (!CryptAcquireContext(&h,0,0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) die("CryptAquire failed"); if (!CryptGenRandom(h,sizeof(buf),buf)) die("CryptGen failed"); |
Read bytes from the file /dev/urandom (Don't bother with /dev/random) |

(If you don't trust your OS, your program is doomed.)

Name |
Usage |
Timing |
Comments |

Mersenne
Twister |
std::mt19937 generator; next=generator(); |
12ns |
Initialization is slow, and the algorithm is
complicated (but fast). |

C++11 default_random_engine |
std::default_random_engine generator; next=generator(); |
12ns |
This is just the 1988 version of Park-Miller,
minstd_rand0. It cycles after two billion steps. |

Park-Miller |
next = (last * 48271) % 2147483647UL; or std::minstd_rand. |
5ns |
Gets stuck if given an input of zero. Surprisingly fast because the compiler transforms the mod to a multiply. |

drand48 |
48-bit arithmetic: next = last*0x5DEECE66DuL + 13; |
10ns |
Low hex digits cycle through this sequence: 0d6bc92785e341af |

randu |
31-bit arithmetic: next = last * 65539; |
5ns |
Only useful with odd numbers. Gets stuck if given an input of zero. Low hex digits cycle through 1,3,9,B. All values fall into 15 planes in 3D (fails spectral test). |

There's an exhaustive table of random number generator performance at the Boost site.

Blum Blum Shub (BBS) is designed to be provably robust to this sort of attack. Generation is simply as follows:

next = (last * last) % (P*Q);

Your random number stream consists of the low few bits of "next".

P and Q are two large prime numbers, with thousands of bits each, meaning this requires multi-precision arithmetic, and generating random numbers is very slow. But there is a proof reducing the problem of reconstructing this random number stream from its low order bits to factoring the product P*Q, which appears to be intractable on current machines (although not on quantum computers!).

srand(3); // the key std::string plaintext=bar, ciphertext=""; for (unsigned int i=0;i<plaintext.size();i++) { int keyi=rand()&0xff; // 8 bits of pseudorandom data keyi&=0x1f; // drop to 5 bits to stay in printable ASCII char c=plaintext[i]; // extract char from input c=c^keyi; // encryption! (or decryption, same thing) ciphertext+=c; // add char to output } return ciphertext;Note we only XOR the low 5 bits of the character data, solely to avoid the hassle of reading and writing binary data. For a real cipher, you would use all 8 bits, resulting in binary garbage as ciphertext, which can't be safely printed to the screen.

But the plaintext "To_be_or_not_to_be" still becomes the ciphertext " N~GjtCgb^~ep^uVjj", which actually isn't too bad at first glance. However, note that decryption is the same operation as encryption, which is our first bug:

- If the attacker can feed ciphertext back in as plaintext to be
encrypted, we will decrypt it for them. (XOR is a
symmetric operation)

- Plaintext and ciphertext bits match up 1-1, which is disaster.

- The key size is too small.

unsigned long randS ,mulBy ,addBy ;The integers at the top are generated from a combination of entropy sources like a high-resolution timer and an expanded version of the passphrase. randS and randS2 get stored to the file. Because there are six integers, a total of 192 bits, we're actually pretty good on total entropy--it'd be hard to enumerate all the keys. Now we encrypt the data by combining two pseudorandom streams:// In the 1990's, these were 4 bytes each

unsigned long randS2,mulBy2,addBy2;

randS=(long)newDialog;randS2=(long)*myH;// OS memory handles as entropy source

randS2^=TickCount();//60ths of a second as entropy source

mulBy =184038653L;addBy =103701121L;//start with good, arbitrary numbers

mulBy2=194625377L;addBy2=203465495L;

short len=strlen(i);

for (short i=len;i<41;i++)// repeat passphrase to fill up buffer

pass[i]=pass[i%len]-i;

for (short i=0;i<8;i++)

{//pervert these numbers by the password in a deterministic way.

//Recall that the low four bits of the password are the most random ones.

//hence, we will xor by the low two bits each iteration (masking the passwd)

mulBy^=(long(pass[i<<2])<<(i<<1))+pass[i+3];

addBy^=(long(pass[(i<<2)+1])<<(i<<1))+pass[i+1];

mulBy2^=(long(pass[(i<<2)+2])<<(i<<1))+pass[i+2];

addBy2^=(long(pass[(i<<2)+3])<<(i<<1))+pass[i+0];

}

dat[i]^=(randS =randS *mulBy +addBy )We do have a potential differential cryptanalysis problem because we're just using XOR to combine the keys with the data, but the randS and randS2 entropy sources make this hard to pull off in practice. There was also a checksum, designed to prevent data modification attacks, but the checksum is also a simple XOR of the data, so it'd be easy enough for an attacker to modify the checksum as well.

^(randS2=randS2*mulBy2+addBy2);

The big hole is that I don't do

Don't use this!