|
![]() |
![]()
|
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |
PGPdisk (formerly CryptDisk) source code review
Will Price sent me the source code to CryptDisk for
review, and I've finally had a chance to take a look at it. For those who
haven't heard of it, CryptDisk is an encrypted partition driver, an
equivalent of Secure Drive for the Macintosh. I have been over the encryption
code in detail, and it looks good. Note that this review is based on the
source for version 1.0 -- [as the current version is later] some of my
complaints may have already been addressed. Anyhow, here's what I found in
the source:
The program comes in "demo" and "non-demo" versions, the difference being
whether or not the crypto functions are enabled. The program is $20
shareware, and source code is an additional $20. A large fraction of the
program is written in C++, which means that poor slobs like me still stuck
with Think C 5 can't compile it. Oh, well.
There are two IDEA keys: the user key and a session key. The data on the
disk itself is encrypted with the session key, which is randomly generated
when the disk is initialized. The session key is encrypted with the user key,
which is generated from the MD5 hash of a pass phrase and a random salt. When
the pass phrase for the disk is changed, the user key changes, but not the
session key. The random salt is also set at disk initialization and does not
change. Pseudocode for key generation looks like this:
The repeated calls to MD5Update are as they should be (rather than repeatedly
performing a full MD5 transformation on the key buffer), since that avoids
the possibility of any stable fixed points in MD5. And adding the counter
field to the hash makes it harder for an attacker to precompute the addition
steps in MD5. This is all good. But it's not at all clear why the key is only
hashed 164 times. Given that this step is only going to be taken once, when
the disk is mounted, one could easily afford to make the loop much longer,
making brute-force pass phrase attacks that much more difficult. Especially
considering that the difficulty of brute-forcing a pass phrase only goes up
linearly with the number of iterations of the hash, 164 is too small.
The disk is encrypted in 512-byte blocks with IDEA in cipher-feedback mode.
The cipher-feedback scheme is really very clever, evidently the same one used
by Peter Gutmann for in SFS: the initial vector is constructed from the
plaintext buffer, the same random salt used to construct the user key, and
the block number of the block being encrypted. The iv is stored on the disk
by xor'ing it into the plaintext and the whole thing is encrypted. Here's the
C code for the encryption and decryption steps for a block of data (this is
subtle and pretty):
static void DecryptBlock(CryptStruct *disk, long blkNum, uchar *data)
{
short i;
ulong iv[2];
iv[0]=*(ulong *)data;
iv[1]=*(ulong *)(data+4);
ideaCFBReinit(&disk->cfb,(uchar *)iv);
ideaCFBDecrypt(&disk->cfb,data+8,data+8,504);
iv[0]=*(ulong *)(data+504);
iv[1]=*(ulong *)(data+508);
ideaCFBReinit(&disk->cfb,(uchar *)iv);
ideaCFBDecrypt(&disk->cfb,data,data,8);
iv[0]=iv[1]=0;
for(i=0;i504;i+=4)
{
iv[1]+=*(ulong *)(data + i);
iv[0]+=iv[1];
}
iv[0] ^= *(ulong *)disk->salt;
iv[1] ^= *(ulong *)(disk->salt+4);
iv[0] += blkNum;
iv[1] += blkNum;
*(ulong *)(data+504) ^= iv[0];
*(ulong *)(data+508) ^= iv[1];
}
(Source code © 1995 Will Price, all rights reserved, excerpted with
permission from the author.)
Random numbers are generated using Colin Plumb's "randpool" code, the same
basic scheme that is used for PGP (see Colin's article "Truly Random Numbers"
in Dr. Dobb's Journal, November 1994, p. 113 for a discussion of the
method). True randoms are collected from the mouse position and the system
clock -- this could be improved somewhat, as the clock is read via the
TickCount() function, which has a resolution of 1/60 second. Better would be
to call the Time Manager, which allows measurement to the microsecond. Also,
on a somewhat pedantic note, there appears to be no attempt to directly
estimate the amount of entropy actually collected before a session key is
generated.
Buffers in memory containing the IDEA keys are not protected from being
swapped to disk by virtual memory. This means that people who have VM turned
on on their machine could inadvertently leave a copy of their key lying
around in the VM space on their disk. This really needs to be fixed (and it's
an easy thing to do).
Oddly, the 68K assembler version of the IDEA core routine is present in the
source but commented out. This is really fast code written by Colin Plumb (I
use the same routine in the forthcoming 2.0 release of Curve Encrypt). It's
too bad the CryptDisk isn't using it, as it makes a substantial speed
difference. (Evidently Will is having trouble getting the code to work!?).
The bottom line is that, aside from a couple of pretty niggling details,
CryptDisk looks very solid from a cryptographic standpoint. The only caveat
is that it appears to be quite insecure on machines using virtual memory.
Otherwise, it's a thorough and well constructed piece of code. A very nice
job. It's good to see someone finally come out with a product like this for
the Mac. It fills a real need.
-- Will (the other one)
I write, regarding hashing the pass phrase:
Will Price replies:
Agreed. It should be noted that neither PGP in conventional encryption mode
nor Curve Encrypt 1.x hash the pass phrase more than _once_ . If you choose a
good pass phrase, this step it utterly unnecessary. It only adds security to
pass phrases which have less than 128 bits of entropy in the first place.
With the other fixes in place, this product's security gets a ringing
endorsement from me!
|
Have you found errors nontrivial or marginal, factual, analytical and illogical, arithmetical, temporal, or even typographical? Please let me know; drop me email. Thanks! |