]> WPIA git - cassiopeia.git/blobdiff - lib/openssl/MacOS/Randomizer.cpp
add: execute openssl fetcher to fetch openssl 1.0.1j
[cassiopeia.git] / lib / openssl / MacOS / Randomizer.cpp
diff --git a/lib/openssl/MacOS/Randomizer.cpp b/lib/openssl/MacOS/Randomizer.cpp
new file mode 100644 (file)
index 0000000..cceb6bd
--- /dev/null
@@ -0,0 +1,476 @@
+/* 
+------- Strong random data generation on a Macintosh (pre - OS X) ------
+               
+--     GENERAL: We aim to generate unpredictable bits without explicit
+       user interaction. A general review of the problem may be found
+       in RFC 1750, "Randomness Recommendations for Security", and some
+       more discussion, of general and Mac-specific issues has appeared
+       in "Using and Creating Cryptographic- Quality Random Numbers" by
+       Jon Callas (www.merrymeet.com/jon/usingrandom.html).
+
+       The data and entropy estimates provided below are based on my
+       limited experimentation and estimates, rather than by any
+       rigorous study, and the entropy estimates tend to be optimistic.
+       They should not be considered absolute.
+
+       Some of the information being collected may be correlated in
+       subtle ways. That includes mouse positions, timings, and disk
+       size measurements. Some obvious correlations will be eliminated
+       by the programmer, but other, weaker ones may remain. The
+       reliability of the code depends on such correlations being
+       poorly understood, both by us and by potential interceptors.
+
+       This package has been planned to be used with OpenSSL, v. 0.9.5.
+       It requires the OpenSSL function RAND_add. 
+
+--     OTHER WORK: Some source code and other details have been
+       published elsewhere, but I haven't found any to be satisfactory
+       for the Mac per se:
+
+       * The Linux random number generator (by Theodore Ts'o, in
+         drivers/char/random.c), is a carefully designed open-source
+         crypto random number package. It collects data from a variety
+         of sources, including mouse, keyboard and other interrupts.
+         One nice feature is that it explicitly estimates the entropy
+         of the data it collects. Some of its features (e.g. interrupt
+         timing) cannot be reliably exported to the Mac without using
+         undocumented APIs.
+
+       * Truerand by Don P. Mitchell and Matt Blaze uses variations
+         between different timing mechanisms on the same system. This
+         has not been tested on the Mac, but requires preemptive
+         multitasking, and is hardware-dependent, and can't be relied
+         on to work well if only one oscillator is present.
+
+       * Cryptlib's RNG for the Mac (RNDMAC.C by Peter Gutmann),
+         gathers a lot of information about the machine and system
+         environment. Unfortunately, much of it is constant from one
+         startup to the next. In other words, the random seed could be
+         the same from one day to the next. Some of the APIs are
+         hardware-dependent, and not all are compatible with Carbon (OS
+         X). Incidentally, the EGD library is based on the UNIX entropy
+         gathering methods in cryptlib, and isn't suitable for MacOS
+         either.
+
+       * Mozilla (and perhaps earlier versions of Netscape) uses the
+         time of day (in seconds) and an uninitialized local variable
+         to seed the random number generator. The time of day is known
+         to an outside interceptor (to within the accuracy of the
+         system clock). The uninitialized variable could easily be
+         identical between subsequent launches of an application, if it
+         is reached through the same path.
+
+       * OpenSSL provides the function RAND_screen(), by G. van
+         Oosten, which hashes the contents of the screen to generate a
+         seed. This is not useful for an extension or for an
+         application which launches at startup time, since the screen
+         is likely to look identical from one launch to the next. This
+         method is also rather slow.
+
+       * Using variations in disk drive seek times has been proposed
+         (Davis, Ihaka and Fenstermacher, world.std.com/~dtd/;
+         Jakobsson, Shriver, Hillyer and Juels,
+         www.bell-labs.com/user/shriver/random.html). These variations
+         appear to be due to air turbulence inside the disk drive
+         mechanism, and are very strongly unpredictable. Unfortunately
+         this technique is slow, and some implementations of it may be
+         patented (see Shriver's page above.) It of course cannot be
+         used with a RAM disk.
+
+--     TIMING: On the 601 PowerPC the time base register is guaranteed
+       to change at least once every 10 addi instructions, i.e. 10
+       cycles. On a 60 MHz machine (slowest PowerPC) this translates to
+       a resolution of 1/6 usec. Newer machines seem to be using a 10
+       cycle resolution as well.
+       
+       For 68K Macs, the Microseconds() call may be used. See Develop
+       issue 29 on the Apple developer site
+       (developer.apple.com/dev/techsupport/develop/issue29/minow.html)
+       for information on its accuracy and resolution. The code below
+       has been tested only on PowerPC based machines.
+
+       The time from machine startup to the launch of an application in
+       the startup folder has a variance of about 1.6 msec on a new G4
+       machine with a defragmented and optimized disk, most extensions
+       off and no icons on the desktop. This can be reasonably taken as
+       a lower bound on the variance. Most of this variation is likely
+       due to disk seek time variability. The distribution of startup
+       times is probably not entirely even or uncorrelated. This needs
+       to be investigated, but I am guessing that it not a majpor
+       problem. Entropy = log2 (1600/0.166) ~= 13 bits on a 60 MHz
+       machine, ~16 bits for a 450 MHz machine.
+
+       User-launched application startup times will have a variance of
+       a second or more relative to machine startup time. Entropy >~22
+       bits.
+
+       Machine startup time is available with a 1-second resolution. It
+       is predictable to no better a minute or two, in the case of
+       people who show up punctually to work at the same time and
+       immediately start their computer. Using the scheduled startup
+       feature (when available) will cause the machine to start up at
+       the same time every day, making the value predictable. Entropy
+       >~7 bits, or 0 bits with scheduled startup.
+
+       The time of day is of course known to an outsider and thus has 0
+       entropy if the system clock is regularly calibrated.
+
+--     KEY TIMING: A  very fast typist (120 wpm) will have a typical
+       inter-key timing interval of 100 msec. We can assume a variance
+       of no less than 2 msec -- maybe. Do good typists have a constant
+       rhythm, like drummers? Since what we measure is not the
+       key-generated interrupt but the time at which the key event was
+       taken off the event queue, our resolution is roughly the time
+       between process switches, at best 1 tick (17 msec). I  therefore
+       consider this technique questionable and not very useful for
+       obtaining high entropy data on the Mac.
+
+--     MOUSE POSITION AND TIMING: The high bits of the mouse position
+       are far from arbitrary, since the mouse tends to stay in a few
+       limited areas of the screen. I am guessing that the position of
+       the mouse is arbitrary within a 6 pixel square. Since the mouse
+       stays still for long periods of time, it should be sampled only
+       after it was moved, to avoid correlated data. This gives an
+       entropy of log2(6*6) ~= 5 bits per measurement.
+
+       The time during which the mouse stays still can vary from zero
+       to, say, 5 seconds (occasionally longer). If the still time is
+       measured by sampling the mouse during null events, and null
+       events are received once per tick, its resolution is 1/60th of a
+       second, giving an entropy of log2 (60*5) ~= 8 bits per
+       measurement. Since the distribution of still times is uneven,
+       this estimate is on the high side.
+
+       For simplicity and compatibility across system versions, the
+       mouse is to be sampled explicitly (e.g. in the event loop),
+       rather than in a time manager task.
+
+--     STARTUP DISK TOTAL FILE SIZE: Varies typically by at least 20k
+       from one startup to the next, with 'minimal' computer use. Won't
+       vary at all if machine is started again immediately after
+       startup (unless virtual memory is on), but any application which
+       uses the web and caches information to disk is likely to cause
+       this much variation or more. The variation is probably not
+       random, but I don't know in what way. File sizes tend to be
+       divisible by 4 bytes since file format fields are often
+       long-aligned. Entropy > log2 (20000/4) ~= 12 bits.
+       
+--     STARTUP DISK FIRST AVAILABLE ALLOCATION BLOCK: As the volume
+       gets fragmented this could be anywhere in principle. In a
+       perfectly unfragmented volume this will be strongly correlated
+       with the total file size on the disk. With more fragmentation
+       comes less certainty. I took the variation in this value to be
+       1/8 of the total file size on the volume.
+
+--     SYSTEM REQUIREMENTS: The code here requires System 7.0 and above
+       (for Gestalt and Microseconds calls). All the calls used are
+       Carbon-compatible.
+*/
+
+/*------------------------------ Includes ----------------------------*/
+
+#include "Randomizer.h"
+
+// Mac OS API
+#include <Files.h>
+#include <Folders.h>
+#include <Events.h>
+#include <Processes.h>
+#include <Gestalt.h>
+#include <Resources.h>
+#include <LowMem.h>
+
+// Standard C library
+#include <stdlib.h>
+#include <math.h>
+
+/*---------------------- Function declarations -----------------------*/
+
+// declared in OpenSSL/crypto/rand/rand.h
+extern "C" void RAND_add (const void *buf, int num, double entropy);
+
+unsigned long GetPPCTimer (bool is601);        // Make it global if needed
+                                       // elsewhere
+
+/*---------------------------- Constants -----------------------------*/
+
+#define kMouseResolution 6             // Mouse position has to differ
+                                       // from the last one by this
+                                       // much to be entered
+#define kMousePositionEntropy 5.16     // log2 (kMouseResolution**2)
+#define kTypicalMouseIdleTicks 300.0   // I am guessing that a typical
+                                       // amount of time between mouse
+                                       // moves is 5 seconds
+#define kVolumeBytesEntropy 12.0       // about log2 (20000/4),
+                                       // assuming a variation of 20K
+                                       // in total file size and
+                                       // long-aligned file formats.
+#define kApplicationUpTimeEntropy 6.0  // Variance > 1 second, uptime
+                                       // in ticks  
+#define kSysStartupEntropy 7.0         // Entropy for machine startup
+                                       // time
+
+
+/*------------------------ Function definitions ----------------------*/
+
+CRandomizer::CRandomizer (void)
+{
+       long    result;
+       
+       mSupportsLargeVolumes =
+               (Gestalt(gestaltFSAttr, &result) == noErr) &&
+               ((result & (1L << gestaltFSSupports2TBVols)) != 0);
+       
+       if (Gestalt (gestaltNativeCPUtype, &result) != noErr)
+       {
+               mIsPowerPC = false;
+               mIs601 = false;
+       }
+       else
+       {
+               mIs601 = (result == gestaltCPU601);
+               mIsPowerPC = (result >= gestaltCPU601);
+       }
+       mLastMouse.h = mLastMouse.v = -10;      // First mouse will
+                                               // always be recorded
+       mLastPeriodicTicks = TickCount();
+       GetTimeBaseResolution ();
+       
+       // Add initial entropy
+       AddTimeSinceMachineStartup ();
+       AddAbsoluteSystemStartupTime ();
+       AddStartupVolumeInfo ();
+       AddFiller ();
+}
+
+void CRandomizer::PeriodicAction (void)
+{
+       AddCurrentMouse ();
+       AddNow (0.0);   // Should have a better entropy estimate here
+       mLastPeriodicTicks = TickCount();
+}
+
+/*------------------------- Private Methods --------------------------*/
+
+void CRandomizer::AddCurrentMouse (void)
+{
+       Point mouseLoc;
+       unsigned long lastCheck;        // Ticks since mouse was last
+                                       // sampled
+
+#if TARGET_API_MAC_CARBON
+       GetGlobalMouse (&mouseLoc);
+#else
+       mouseLoc = LMGetMouseLocation();
+#endif
+       
+       if (labs (mLastMouse.h - mouseLoc.h) > kMouseResolution/2 &&
+           labs (mLastMouse.v - mouseLoc.v) > kMouseResolution/2)
+               AddBytes (&mouseLoc, sizeof (mouseLoc),
+                               kMousePositionEntropy);
+       
+       if (mLastMouse.h == mouseLoc.h && mLastMouse.v == mouseLoc.v)
+               mMouseStill ++;
+       else
+       {
+               double entropy;
+               
+               // Mouse has moved. Add the number of measurements for
+               // which it's been still. If the resolution is too
+               // coarse, assume the entropy is 0.
+
+               lastCheck = TickCount() - mLastPeriodicTicks;
+               if (lastCheck <= 0)
+                       lastCheck = 1;
+               entropy = log2l
+                       (kTypicalMouseIdleTicks/(double)lastCheck);
+               if (entropy < 0.0)
+                       entropy = 0.0;
+               AddBytes (&mMouseStill, sizeof (mMouseStill), entropy);
+               mMouseStill = 0;
+       }
+       mLastMouse = mouseLoc;
+}
+
+void CRandomizer::AddAbsoluteSystemStartupTime (void)
+{
+       unsigned long   now;            // Time in seconds since
+                                       // 1/1/1904
+       GetDateTime (&now);
+       now -= TickCount() / 60;        // Time in ticks since machine
+                                       // startup
+       AddBytes (&now, sizeof (now), kSysStartupEntropy);
+}
+
+void CRandomizer::AddTimeSinceMachineStartup (void)
+{
+       AddNow (1.5);                   // Uncertainty in app startup
+                                       // time is > 1.5 msec (for
+                                       // automated app startup).
+}
+
+void CRandomizer::AddAppRunningTime (void)
+{
+       ProcessSerialNumber PSN;
+       ProcessInfoRec          ProcessInfo;
+       
+       ProcessInfo.processInfoLength = sizeof (ProcessInfoRec);
+       ProcessInfo.processName = nil;
+       ProcessInfo.processAppSpec = nil;
+       
+       GetCurrentProcess (&PSN);
+       GetProcessInformation (&PSN, &ProcessInfo);
+
+       // Now add the amount of time in ticks that the current process
+       // has been active
+
+       AddBytes (&ProcessInfo, sizeof (ProcessInfoRec),
+                       kApplicationUpTimeEntropy);
+}
+
+void CRandomizer::AddStartupVolumeInfo (void)
+{
+       short                   vRefNum;
+       long                    dirID;
+       XVolumeParam    pb;
+       OSErr                   err;
+       
+       if (!mSupportsLargeVolumes)
+               return;
+               
+       FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
+                       &vRefNum, &dirID);
+       pb.ioVRefNum = vRefNum;
+       pb.ioCompletion = 0;
+       pb.ioNamePtr = 0;
+       pb.ioVolIndex = 0;
+       err = PBXGetVolInfoSync (&pb);
+       if (err != noErr)
+               return;
+               
+       // Base the entropy on the amount of space used on the disk and
+       // on the next available allocation block. A lot else might be
+       // unpredictable, so might as well toss the whole block in. See
+       // comments for entropy estimate justifications.
+
+       AddBytes (&pb, sizeof (pb),
+               kVolumeBytesEntropy +
+               log2l (((pb.ioVTotalBytes.hi - pb.ioVFreeBytes.hi)
+                               * 4294967296.0D +
+                       (pb.ioVTotalBytes.lo - pb.ioVFreeBytes.lo))
+                               / pb.ioVAlBlkSiz - 3.0));
+}
+
+/*
+       On a typical startup CRandomizer will come up with about 60
+       bits of good, unpredictable data. Assuming no more input will
+       be available, we'll need some more lower-quality data to give
+       OpenSSL the 128 bits of entropy it desires. AddFiller adds some
+       relatively predictable data into the soup.
+*/
+
+void CRandomizer::AddFiller (void)
+{
+       struct
+       {
+               ProcessSerialNumber psn;        // Front process serial
+                                               // number
+               RGBColor        hiliteRGBValue; // User-selected
+                                               // highlight color
+               long            processCount;   // Number of active
+                                               // processes
+               long            cpuSpeed;       // Processor speed
+               long            totalMemory;    // Total logical memory
+                                               // (incl. virtual one)
+               long            systemVersion;  // OS version
+               short           resFile;        // Current resource file
+       } data;
+       
+       GetNextProcess ((ProcessSerialNumber*) kNoProcess);
+       while (GetNextProcess (&data.psn) == noErr)
+               data.processCount++;
+       GetFrontProcess (&data.psn);
+       LMGetHiliteRGB (&data.hiliteRGBValue);
+       Gestalt (gestaltProcClkSpeed, &data.cpuSpeed);
+       Gestalt (gestaltLogicalRAMSize, &data.totalMemory);
+       Gestalt (gestaltSystemVersion, &data.systemVersion);
+       data.resFile = CurResFile ();
+       
+       // Here we pretend to feed the PRNG completely random data. This
+       // is of course false, as much of the above data is predictable
+       // by an outsider. At this point we don't have any more
+       // randomness to add, but with OpenSSL we must have a 128 bit
+       // seed before we can start. We just add what we can, without a
+       // real entropy estimate, and hope for the best.
+
+       AddBytes (&data, sizeof(data), 8.0 * sizeof(data));
+       AddCurrentMouse ();
+       AddNow (1.0);
+}
+
+//-------------------  LOW LEVEL ---------------------
+
+void CRandomizer::AddBytes (void *data, long size, double entropy)
+{
+       RAND_add (data, size, entropy * 0.125); // Convert entropy bits
+                                               // to bytes
+}
+
+void CRandomizer::AddNow (double millisecondUncertainty)
+{
+       long time = SysTimer();
+       AddBytes (&time, sizeof (time), log2l (millisecondUncertainty *
+                       mTimebaseTicksPerMillisec));
+}
+
+//----------------- TIMING SUPPORT ------------------
+
+void CRandomizer::GetTimeBaseResolution (void)
+{      
+#ifdef __powerc
+       long speed;
+       
+       // gestaltProcClkSpeed available on System 7.5.2 and above
+       if (Gestalt (gestaltProcClkSpeed, &speed) != noErr)
+               // Only PowerPCs running pre-7.5.2 are 60-80 MHz
+               // machines.
+               mTimebaseTicksPerMillisec =  6000.0D;
+       // Assume 10 cycles per clock update, as in 601 spec. Seems true
+       // for later chips as well.
+       mTimebaseTicksPerMillisec = speed / 1.0e4D;
+#else
+       // 68K VIA-based machines (see Develop Magazine no. 29)
+       mTimebaseTicksPerMillisec = 783.360D;
+#endif
+}
+
+unsigned long CRandomizer::SysTimer (void)     // returns the lower 32
+                                               // bit of the chip timer
+{
+#ifdef __powerc
+       return GetPPCTimer (mIs601);
+#else
+       UnsignedWide usec;
+       Microseconds (&usec);
+       return usec.lo;
+#endif
+}
+
+#ifdef __powerc
+// The timebase is available through mfspr on 601, mftb on later chips.
+// Motorola recommends that an 601 implementation map mftb to mfspr
+// through an exception, but I haven't tested to see if MacOS actually
+// does this. We only sample the lower 32 bits of the timer (i.e. a
+// few minutes of resolution)
+
+asm unsigned long GetPPCTimer (register bool is601)
+{
+       cmplwi  is601, 0        // Check if 601
+       bne     _601            // if non-zero goto _601
+       mftb    r3              // Available on 603 and later.
+       blr                     // return with result in r3
+_601:
+       mfspr r3, spr5          // Available on 601 only.
+                               // blr inserted automatically
+}
+#endif