Subject: | Key generation issues |
This is recording some of the issues I've found and fixed in KeyChain/generate_params while writing Crypt::DSA::GMP. Most of this is the difference between OpenSSL (1.0.1) and the Perl code, but there is also a critical defect in the 'p' generation where we will output composites.
OpenSSL silently uses min(bits, 512), hence if we ask for 256 bits we actually get 512. On the other hand, the Perl code does not do this -- it will accept any bit size other than 0. This means:
= bit sizes < 0 will output: Can't locate object method "copy" via package "NaN"
= bit sizes <= 160 will always create keys with composite p.
= bit sizes not much larger than 160 will often create keys with composite p.
= bit sizes from 1 to 511 will create different results from OpenSSL (we honor the Size, OpenSSL does not).
My solution was to (1) not use OpenSSL anywhere, which solves a host of problems, (2) croak if Size < 256, and (3) redo the q/p selection if the counter overflows, as specified in FIPS 186-3/4.
If we are unlucky with the p selection and take 4096 tries, we will output composite p values. This happens regardless of bit size. Solution #3 above takes care of this.
OpenSSL bumps the 'q' size to 256 when Size >= 2048. The Perl code is always 160 since it is using the obsolete SHA1 method (FIPS 186-3/4 A.1.1.1). This leads to fundamentally different keys.
My solution (not ideal, suggestions welcome) was to:
1) if a Seed is given and its length is 20, use the old SHA-1 method with 'q' size 160. This makes t/06-fips.t pass, and should retain backward compatibility.
2) if a Seed is not given or its size is not 20, then use the approved A.1.1.1 method using SHA256 as the hash, and set the 'q' size as 160 if <= 2048, 256 otherwise.
I may later add a parameter for setting the 'q' size directly.
Other RT's already mention two other issues which are solved in Crypt::DSA::GMP:
- the Perl code does not use strong enough primality testing for FIPS compliance (I'm using the Lucas+MR tests by default, and an option for a proof of q, p, or both).
- we should use a strong randomness source (e.g. /dev/random or Win32's API).