kernel/eka/kernel/random.cpp
branchRCL_3
changeset 62 4a8fed1c0ef6
parent 0 a41df078684a
child 268 345b1ca54e88
--- a/kernel/eka/kernel/random.cpp	Tue Feb 02 01:24:03 2010 +0200
+++ b/kernel/eka/kernel/random.cpp	Sat Feb 20 00:10:51 2010 +0200
@@ -16,52 +16,23 @@
 //
 
 #include <kernel/kern_priv.h>
-
-//
-// Generate nth random number using the following algorithm
-//
-// X[n] = ((X[n-j] rotl r1) + (X[n-k] rotl r2)) modulo 2^b               
-//
-// k=17, j=10, r1=5, r2=3
-//
-
-const TInt KBufferSize=17;
-const TInt KBufferSizeInBits=KBufferSize*32;
-const TInt KPointer2Offset=10;  // Must be less than KBufferSize
-
-TUint32 RandomBuffer[KBufferSize];
-TInt RandomPointer=1;
-TInt RandomSaltPointer=KBufferSizeInBits-1;
-
-inline TUint32 RotateLeft5(TUint32 aVal)
-	{ return (aVal << 5) | (aVal >> 27); }
+#include "securerng.h"
+#include "execs.h"
 
-inline TUint32 RotateLeft3(TUint32 aVal)
-	{ return (aVal << 3) | (aVal >> 29); }
-
-void K::Randomize()
-//
-// Initialise the random pool
-//
-	{
-	TUint64 seed=K::TickQ->iRtc;
-	TInt i;
-	for (i=0; i<KBufferSize; i++)
-		{
-		RandomBuffer[i] = TUint32(seed);
-		seed= ((seed<<5) + (seed>>59)) + 97;
-		}
-	NKern::LockSystem();
-	for (i=0; i<50; i++)
-		Kern::Random();
-	NKern::UnlockSystem();
-	}
-	
+// The global pointer to the RNG instance
+DSecureRNG* SecureRNG;
 
 /**
-	Generate the next random number.
+	Gets 32 random bits from the kernel's random number generator.
+
+	The returned random data may or may not be cryptographically secure but should be of a high quality for
+	non-cryptographic purposes.
 
-	@return The generated random number.
+	This function uses a cryptographically strong random number generator to generate the random data, which can
+	be slower than insecure generators. If security is not important, you may wish to use a trivial RNG instead
+	for performance.
+
+	@return The 32 random bits.
 
 	@pre Kernel Lock must not be held.
 	@pre No fast mutex should be held
@@ -70,42 +41,116 @@
 */
 EXPORT_C TUint32 Kern::Random()
 	{
-	TBool alreadyLocked = TheScheduler.iLock.HeldByCurrentThread();
-	if (!alreadyLocked)
-		NKern::LockSystem();
-	TInt p1 = RandomPointer;
-	if(--p1<0)
-		p1 = KBufferSize-1;
-	RandomPointer = p1;
-	TInt p2 = p1+KPointer2Offset;
-	if(p2>KBufferSize-1)
-		p2 -= KBufferSize-1;
-	TUint32 r = RandomBuffer[p1] = RotateLeft5(RandomBuffer[p2])+RotateLeft3(RandomBuffer[p1]);
-	if (!alreadyLocked)
-		NKern::UnlockSystem();
-	return r;
+	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD, "Kern::Random()");
+	TBuf8<4> randomBuffer;
+	randomBuffer.SetMax();
+	SecureRNG->GenerateRandomNumber(randomBuffer);
+	return *((const TUint32*)randomBuffer.Ptr());
+	}
+
+/**
+	Fills the provided descriptor with random data up to its current length. The number of random bytes required
+	should be specified by setting the length of the descriptor that is passed to the function.
+
+	If the returned random data cannot be guaranteed to be cryptographically secure, the function will return
+	KErrNotReady, but data will still be provided suitable for non-cryptographic purposes.
+
+	The security strength of the cryptograpically strong random number generator is 256 bits.
+
+	@param aRandomValue  on return, the descriptor is filled with the requested number of random bytes.
+
+	@return KErrArgument	if more than 65536 bytes are requested in a single call.
+			KErrNotReady	if the returned random data cannot be guaranteed to be cryptographically secure.
+			KErrNone		if the returned random data is believed to be cryptographically secure.
+		
+	@pre Kernel Lock must not be held
+	@pre No fast mutex should be held
+	@pre Interrupts should be enabled
+	@pre Can be used in a device driver.
+*/
+EXPORT_C TInt Kern::SecureRandom(TDes8& aRandomValue)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD, "Kern::SecureRandom(TDes8&)");
+	return SecureRNG->GenerateRandomNumber(aRandomValue);
 	}
 
 /**
-	Adds a bit to the random pool used to generate random numbers.
-	This method should be used by any sources of entropy to improve the quality of
-	random number generation.
+	Adds the given entropy input to the entropy pool used for random number generation.
+	This is the only version of Kern::RandomSalt which allows entropy samples larger than 8 bytes to be added.
+
+	Entropy estimates should be chosen carefully to reflect the minimum entropy that the sample may contain.
 
-	@param aBitOfSalt The least significant bit of this value is added to the random pool.
+	@param aEntropyData			Pointer to the entropy data.
+	@param aEntropyDataLength	Length of the entropy data in bytes.
+	@param aBitsOfEntropy		The amount of entropy (in bits) present in the entropy data.
 
-  	@pre Can be used in a device driver.
+	@pre Kernel Lock must not be held
+	@pre No fast mutex should be held
+	@pre Interrupts should be enabled
+	@pre Can be used in a device driver.
 */
-EXPORT_C void Kern::RandomSalt(TUint32 aBitOfSalt)
+EXPORT_C void Kern::RandomSalt(const TUint8* aEntropyData, TUint aEntropyDataLength, TUint aBitsOfEntropy)
 	{
-	TInt p=RandomSaltPointer; // protect RandomSaltPointer from re-entrantrancy
-	if (aBitOfSalt&=1)
+	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD, "Kern::RandomSalt(const TUint8*, TUint, TUint)");
+
+	// Check if the claimed entropy estimation (in bits) is not more than the maximum value.
+	__ASSERT_ALWAYS(aBitsOfEntropy <= aEntropyDataLength*8, K::PanicKernExec(EEntropyEstimateOutOfRange));
+
+	// If the Secure RNG system is not in idle mode, add the collected entropy to the entropy pool.
+	if (!SecureRNG->SecureRNGIdle())
 		{
-		TInt word=p >> 5;
-		TInt bit=p & 0x1f;
-		RandomBuffer[word] ^= aBitOfSalt << bit;
+		SecureRNG->AddEntropy(aEntropyData, aEntropyDataLength, aBitsOfEntropy);
 		}
-	if (--p<0)
-		p=KBufferSizeInBits-1;
-	RandomSaltPointer=p;
 	}
 
+/*
+ * Exec handler function for obtaining secure random numbers
+ */
+TInt ExecHandler::MathSecureRandom(TDes8& aRandomValue)
+	{
+	TInt randomValueLength = 0;
+	TInt randomValueMaxLength = 0;
+	//Gets information about the user specified descriptor.
+	TUint8* kernelPtr = (TUint8*)Kern::KUDesInfo(aRandomValue, randomValueLength, randomValueMaxLength);
+	if(randomValueMaxLength == -1) //user passed descriptor is not writable
+		{
+		K::PanicKernExec(EKUDesSetLengthInvalidType);
+		}
+
+	// The random number generator requires a temporary buffer to write the data to, before we write it back to
+	// userspace's buffer. The buffer is allocated here on the stack to avoid having to heap-allocate, and if
+	// the requested amount of data is larger, a loop is used. 2048 bytes will always fit onto the stack in an
+	// exec handler.
+	const TInt KRandomBufferSize = 2048;
+	TBuf8<KRandomBufferSize> randomBuffer;
+
+	TInt err = KErrNone;
+	TBool isKErrNotReadyTrue = EFalse;
+	while(randomValueLength > 0)
+		{
+		TInt noOfBytesToGenerate = (randomValueLength > KRandomBufferSize) ? KRandomBufferSize : randomValueLength;
+		randomBuffer.SetLength(noOfBytesToGenerate);
+		// Generate random numbers 
+		err = Kern::SecureRandom(randomBuffer);
+		if(err == KErrNotReady)
+			{
+			isKErrNotReadyTrue = ETrue;
+			}
+		else if (err != KErrNone)
+			{
+			return err; // any other system wide error code needs to be returned immediately.
+			}
+		// Copy the generated random numbers to the user descriptor.
+		umemput(kernelPtr, randomBuffer.Ptr(), noOfBytesToGenerate);
+		kernelPtr += KRandomBufferSize;
+		randomValueLength -= KRandomBufferSize;
+		}
+
+	// Atleast one KErrNotReady error was generated during processing the request, so return the state as not ready
+	// indicating the internal states was not secure during random number generation. 
+	if(isKErrNotReadyTrue)
+		{
+		return KErrNotReady;
+		}
+	return err;
+	}