raaz-0.2.1: The raaz cryptographic library.

Safe Haskell | None
Language | Haskell2010

Raaz.Random

Contents

- Cryptographically secure randomness.
  - Types that can be generated randomly
- Low level access to randomness.
- Internals

Description

Interface for cryptographically secure random byte generators.

Synopsis

- data RT mem a
- type RandM = RT VoidMemory
- randomByteString :: LengthUnit l => l -> RT mem ByteString
- random :: (RandomStorable a, Memory mem) => RT mem a
- randomiseCell :: RandomStorable a => RT (MemoryCell a) ()
- class Storable a => RandomStorable a where
  - fillRandomElements :: Memory mem => Int -> Ptr a -> RT mem ()
- unsafeFillRandomElements :: (Memory mem, Storable a) => Int -> Ptr a -> RT mem ()
- fillRandomBytes :: LengthUnit l => l -> Pointer -> RT mem ()
- reseed :: RT mem ()

# Cryptographically secure randomness.

The raaz library gives a relatively high level interface to cryptographically secure randomness. The combinator `random` generates pure value of certain types where as a sequence of random bytes can be generated using the combinator `randomByteString`. Both these combinators returns actions in the `RandM`, the monad that captures actions that generate/use cryptographically secure random bytes.

# Running a random action

There are two ways to run an `RandM` action, either `securely` or `insecurely`. The only difference in these two is how they store their seed in memory. The combinator `securely` ensures that the seed of the CS-PRG is in a locked memory which is wiped clean after the action is done. It is only needed in certain special cases where the generated bytes needs to be kept secret, like for example when generating long term keys. Even in those special cases, we need to take extra precautions to ensure that no data is leaked (details follow). For almost all other cases, we just need to use `insecurely` as demonstrated below.

    -- Generate a pair of random Word8's
    import Raaz
    import Data.Word

    main :: IO ()
    main = insecurely rPair >>= print
       where rPair :: RandM (Word8, Word8)
             rPair = (,) <$> random <$> random

    -- A version of hello world that has gone nuts. Printed in base16
    -- to save some terminal grief.

    main = insecurely who >>= \ w -> putStrLn $ "hello " ++ showBase16 w
      where who :: RandM ByteString
            who = randomByteString 10

# Generating sensitive data

The pseudo-random generator exposed here is cryptographically strong enough to be used in generating long term private key for a PKI system. However, special care is to be taken when generating such data as we do not want the data to be swapped out of the memory. The `securely` combinator is precisely for this scenario. However, it is not enough to just wrap a `RandM` action in `securely` to avoid leaking sensitive data. The use of `securely` instead of `insecurely` in the following code gives us no additional security.

    -- Could as well use insecurely
    genWord64 :: IO Word64
    genWord64 = securely random

    -- Could as well use insecurely
    genRandomPassword :: IO ByteString
    genRandomPassword = securely $ randomByteString 42

Running a random action like `randomByteString`, using the combinator `securely` only guarantees the seed is kept in locked memory whereas the generated pure value resides in the unlocked Haskell heap. It is not feasible to ensure that the value is stored in locked memory as the garbage collector often moves values around. In general, it is not good to generate sensitive values as a pure Haskell values. The solution we now describe gets into the guts of the memory system of raaz. We recommend users who are not developers of raaz or crypto-protocols on top of the raaz library, to look for more high level solutions that hopefully raaz will export.

The monad `RandM` is a specialisation of the more general random monad ``RT` mem` which captures a random actions that use an additional memory element of type `mem` (See the `Memory` module for a description of the memory subsystem of raaz). The main idea is to generate the random data directly into the additional memory used by the action. A typical situation is to generate a random element into a memory cell. This can be achieved by using `randomiseCell` which, together with appropriate uses of `onSubMemory`, should take care of most use cases of generating sensitive data. Here is an example where we generate a key and iv at randomly and perform an action with it.

    type SensitiveInfo = (MemoryCell Key, Memory IV)

    main :: IO ()
    main = securely $ do

        -- Initialisation
        onSubMemory fst randomiseCell -- randomise key
        onSubMemory snd randomiseCell -- randomise iv

        doSomethingWithKeyIV

More complicated interactions might require direct use of low level buffer filling operations `fillRandomBytes` and `fillRandomElements`. We recommend that this be avoided as much as possible as they are prone to all the problems with pointer functions.

data RT mem a Source #

A batch of actions on the memory element `mem` that uses some randomness.

#### Instances

Instances details

MemoryThread RT Source # |
Instance details Defined in Raaz.Random Methods securely :: Memory mem => RT mem a -> IO a Source # insecurely :: Memory mem => RT mem a -> IO a Source # liftMT :: MT mem a -> RT mem a Source # onSubMemory :: (mem -> submem) -> RT submem a -> RT mem a Source #
Monad (RT mem) Source # |
Instance details Defined in Raaz.Random Methods (>>=) :: RT mem a -> (a -> RT mem b) -> RT mem b Source # (>>) :: RT mem a -> RT mem b -> RT mem b Source # return :: a -> RT mem a Source #
Functor (RT mem) Source # |
Instance details Defined in Raaz.Random Methods fmap :: (a -> b) -> RT mem a -> RT mem b Source # (<$) :: a -> RT mem b -> RT mem a Source #
Applicative (RT mem) Source # |
Instance details Defined in Raaz.Random Methods pure :: a -> RT mem a Source # (<*>) :: RT mem (a -> b) -> RT mem a -> RT mem b Source # liftA2 :: (a -> b -> c) -> RT mem a -> RT mem b -> RT mem c Source # (*>) :: RT mem a -> RT mem b -> RT mem b Source # (<*) :: RT mem a -> RT mem b -> RT mem a Source #
MonadIO (RT mem) Source # |
Instance details Defined in Raaz.Random Methods liftIO :: IO a -> RT mem a Source #

type RandM = RT VoidMemory Source #

The monad for generating cryptographically secure random data.

randomByteString :: LengthUnit l => l -> RT mem ByteString Source #

Generate a random byteString.

random :: (RandomStorable a, Memory mem) => RT mem a Source #

Generate a random element from an instance of a RandomStorable element.

randomiseCell :: RandomStorable a => RT (MemoryCell a) () Source #

Randomise the contents of a memory cell. Equivalent to ``random` >>= liftMT . initialise` but ensures that no data is transferred to unlocked memory.

## Types that can be generated randomly

class Storable a => RandomStorable a where Source #

Instances of `Storable` which can be randomly generated. It might appear that all instances of the class `Storable` should be be instances of this class, after all we know the size of the element, why not write that many random bytes. In fact, this module provides an `unsafeFillRandomElements` which does that. However, we do not give a blanket definition for all storables because for certain refinements of a given type, like for example, Word8's modulo 10, `unsafeFillRandomElements` introduces unacceptable skews.

Methods

fillRandomElements Source #

Arguments

:: Memory mem |
=> Int | number of elements to fill
-> Ptr a | The buffer to fill
-> RT mem () |

Fill the buffer with so many random elements of type a.

#### Instances

Instances details

RandomStorable Int Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Int -> RT mem () Source #
RandomStorable Int8 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Int8 -> RT mem () Source #
RandomStorable Int16 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Int16 -> RT mem () Source #
RandomStorable Int32 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Int32 -> RT mem () Source #
RandomStorable Int64 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Int64 -> RT mem () Source #
RandomStorable Word Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Word -> RT mem () Source #
RandomStorable Word8 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Word8 -> RT mem () Source #
RandomStorable Word16 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Word16 -> RT mem () Source #
RandomStorable Word32 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Word32 -> RT mem () Source #
RandomStorable Word64 Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr Word64 -> RT mem () Source #
RandomStorable KEY Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr KEY -> RT mem () Source #
RandomStorable IV Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr IV -> RT mem () Source #
RandomStorable IV Source # |
Instance details Defined in Raaz.Cipher.AES.Internal Methods fillRandomElements :: Memory mem => Int -> Ptr IV -> RT mem () Source #
RandomStorable KEY256 Source # |
Instance details Defined in Raaz.Cipher.AES.Internal Methods fillRandomElements :: Memory mem => Int -> Ptr KEY256 -> RT mem () Source #
RandomStorable KEY192 Source # |
Instance details Defined in Raaz.Cipher.AES.Internal Methods fillRandomElements :: Memory mem => Int -> Ptr KEY192 -> RT mem () Source #
RandomStorable KEY128 Source # |
Instance details Defined in Raaz.Cipher.AES.Internal Methods fillRandomElements :: Memory mem => Int -> Ptr KEY128 -> RT mem () Source #
RandomStorable w => RandomStorable (BE w) Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr (BE w) -> RT mem () Source #
RandomStorable w => RandomStorable (LE w) Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr (LE w) -> RT mem () Source #
(Dimension d, Unbox w, RandomStorable w) => RandomStorable (Tuple d w) Source # |
Instance details Defined in Raaz.Random Methods fillRandomElements :: Memory mem => Int -> Ptr (Tuple d w) -> RT mem () Source #

unsafeFillRandomElements :: (Memory mem, Storable a) => Int -> Ptr a -> RT mem () Source #

This is a helper function that has been exported to simplify the definition of a `RandomStorable` instance for `Storable` types. However, there is a reason why we do not give a blanket instance for all instances `Storable` and why this function is unsafe? This function generates a random element of type `a` by generating `n` random bytes where `n` is the size of the elements of `a`. For instances that range the entire `n` byte space this is fine. However, if the type is actually a refinement of such a type, (consider a ``Word8`` modulo `10` for example) this function generates an unacceptable skew in the distribution. Hence this function is prefixed unsafe.

# Low level access to randomness.

fillRandomBytes :: LengthUnit l => l -> Pointer -> RT mem () Source #

Fill the given input pointer with random bytes.

reseed :: RT mem () Source #

Reseed from the system entropy pool. There is never a need to explicitly seed your generator. The insecurely and securely calls makes sure that your generator is seed before starting. Furthermore, the generator also reseeds after every few GB of random bytes that it generates. Generating random data from the system entropy is usually an order of magnitude slower than using a fast stream cipher. Reseeding often can slow your program considerably without any additional security advantage.

# Internals

Note: Only for developers and reviewers.

Generating unpredictable stream of bytes is one task that has burnt the fingers of a lot of programmers. Unfortunately, getting it correct is something of a black art. We give the internal details of the cryptographic pseudo-random generator used in raaz. Note that none of the details here are accessible or tuneable by the user. This is a deliberate design choice to insulate the user from things that are pretty easy to mess up.

The pseudo-random generator in Raaz uses the chacha20 stream cipher. We more or less follow the fast key erasure technique (https:/blog.cr.yp.to20170723-random.html) which is used in the arc4random implementation in OpenBSD. The two main steps in the generation of the required random bytes are the following:

Seeding:
  Setting the internal state of of the chacha20 cipher, i.e. its key, iv, and counter.
Sampling:
  Pre-computing a few blocks of the chacha20 key stream in an auxiliary buffer which in turn is used to satisfy the requests for random bytes.

The internal chacha20 state and auxilary buffer used to cache generated random bytes is part of the memory used in the `RT` monad and hence using `securely` will ensure that they are locked.

## Seeding.

We use the system entropy source to seed the (key, iv) of the chacha20 cipher. Reading the system entropy source is a costly affair as it often involves a system call. Therefore, seeding is done at the beginning of the operation and once every 1G blocks (64GB) of data generated. No direct access to the system entropy is provided to the user except through the `reseed` combinator which itself is not really recommended. This is a deliberate design choice to avoid potential confusion and the resulting error for the user.

User level libraries have very little access to actual entropy sources and it is very difficult to ascertain the quality of the ones that we do have. Therefore, we believe it is better to rely on the operating system for the entropy needed for seeding. As a result, security of PRG is crucially dependent on the quality of system entropy source. If the seed is predictable then everything till the next seeding (an infrequent event as explained above) is deterministic and hence compromised. Be warned that the entropy in many systems are quite low at certain epochs, like at the time of startup. This can cause the PRG to be compromised. We try to mitigate this by using the best know source for each supported operating system. Given below is the list of our choice of entropy source.

OpenBSD/NetBSD:
  The arc4random call.
Linux:
  Defaults to `/dev/urandom` but has experimental support for `getrandom` (needs testing). The `getrandom` call is better but many current systems do not have support for this (needs kernel > 3.17 and libc > 2.25).
Other Posix:
  Uses `/dev/urandom`
Windows:
  Support using CryptGenRandom from Wincrypt.h.

## Sampling.

Instead of running the chacha20 cipher for every request, we generate 16 blocks of ChaCha20 key stream in an auxiliary buffer and satisfy requests for random bytes from this buffer. To ensure that the compromise of the PRG state does not compromise the random data already generated and given out, we do the following.

1. At each sampling, we re-initialise the (key,iv) pair using the key size + iv size bytes from the auxiliary buffer. This ensures that there is no way to know which key,iv pairs was used to generate the current contents in the auxiliary buffer.
2. Every use of data from the auxiliary buffer, whether it is to satisfy a request for random bytes or to reinitialise the (key,iv) pair in step 1 is wiped out immediately.

Assuming the security of the chacha20 stream cipher we have the following security guarantee.

Security Guarantee:
  At any point of time, a compromise of the cipher state (i.e. key iv pair) and/or the auxiliary buffer does not reveal the random data that is given out previously.