PGPdisk (formerly CryptDisk) source code review

  Locations of visitors to this page
be notified of website changes? subscribe
Mac OS

 

Apple

MacBook Pro

PowerBook

Newton

QuickTake

Lombard/Wall Street woes

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

PGPdisk (formerly CryptDisk) source code review

CryptDisk: a source code review


From: "W. Kinney" 
Subject: CryptDisk 1.0 Source Review
Date: Wed, 15 Feb 1995 09:42:09 -0700 (MST)

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:

 = randoms
 = MD5[]
 = Randoms
MD5Init[]
MD5Update[]
for (i = 0 to 163)
        MD5Update[]
        MD5Update[(byte)i]
 = MD5Final[]

= first 8 bytes of

and are then encrypted with IDEA in ECB mode, using as the key. is used to verify that a pass phrase entered when mounting the disk is correct.

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 EncryptBlock(CryptStruct *disk, long blkNum, uchar *data)
{
        short i;
        ulong iv[2];
        
        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];
        iv[0]=*(ulong *)(data+504);
        iv[1]=*(ulong *)(data+508);
        ideaCFBReinit(&disk->cfb,(uchar *)iv);
        ideaCFBEncrypt(&disk->cfb,data,data,512);
}

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)


From: "W. Kinney" 
Subject: Re: CryptDisk 1.0 Source Review
Date: Thu, 16 Feb 1995 11:45:10 -0700 (MST)

I write, regarding hashing the pass phrase:

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.

Will Price replies:

This will be addressed in a future version. The passphrase will be hashed for 1 second and then the number of iterations will be stored in the clear in the CryptDisk file. The file formats will be compatible. I would note that I do not consider the current implementation much less secure. Some Macs aren't going to MD5 hash many more times in 1 second anyway.

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!
 

What's New?  •  Search this Site  •  Website Map
Travel  •  Burning Man  •  San Francisco
Kilts! Kilts! Kilts!  •  Macintosh  •  Technology  •  CU-SeeMe
This page is copyrighted 1993-2008 by Lila, Isaac, Rose, and Mickey Sattler. All rights reserved.