crypton-2.2.5: Cryptography Primitives
LicenseBSD-style
MaintainerVincent Hanquez <vincent@snarc.org>
Stabilitystable
Portabilitygood
Safe HaskellNone
LanguageHaskell2010

Crypto.Random

Description

Random bytes, drawn either from the system or from a generator whose seed you hold.

Drawing from the system

getRandomBytes in IO is the ordinary way to get bytes nobody can predict. The length is in bytes and the type is any ByteArray, so the result is usually pinned down by where it goes:

import Crypto.Random (getRandomBytes)
import Data.ByteString (ByteString)

nonce <- getRandomBytes 12 :: IO ByteString

Everything in this library that needs randomness takes it the same way, through a MonadRandom m => constraint, so running it in IO is the whole of choosing this source:

import qualified Crypto.PubKey.RSA as RSA

(pub, priv) <- RSA.generate 256 0x10001

Where the operating system offers getrandom(2) or getentropy(3) the bytes come from a ChaCha20 generator belonging to the operating system thread the call runs on, seeded from the system and reseeded as it goes; see Crypto.Random.SysDRG for what that is and what it is not. Where it does not -- Windows, for now -- they come from the system on every call, as they always did. Either way the caller writes the same line.

Any Haskell thread may draw, and none has to say which generator it wants:

import Control.Concurrent (forkIO)

mapM_ (\_ -> forkIO (getRandomBytes 32 >>= use)) [1 .. 64 :: Int]

The generator belongs to the operating system thread that happens to be carrying the Haskell thread when the call is made, and is found through that thread's own storage rather than by anything the caller passes. A forkIO thread moves between capabilities, so two draws from one Haskell thread may be answered by two generators; they are seeded independently of each other, so it makes no difference which one answers.

Drawing from a generator you hold

A generator of your own is the way to draw many times from one seed. The system is asked once, when the generator is made, and not again. That is how tls does a connection: seedNew when it opens, and everything the handshake needs afterwards from the generator, whose state the connection carries from one draw to the next.

import Crypto.Random (ChaChaDRG, drgNewSeed, seedNew, withDRG)

data Connection = Connection { connRNG :: ChaChaDRG }

newConnection :: IO Connection
newConnection = do
    seed <- seedNew                   -- the only draw from the system
    return (Connection (drgNewSeed seed))

-- every later draw advances the connection's own generator
connRandom :: Int -> Connection -> (ByteString, Connection)
connRandom n conn =
    let (bytes, rng') = withDRG (connRNG conn) (getRandomBytes n)
     in (bytes, conn{connRNG = rng'})

Inside withDRG the same getRandomBytes resolves to the instance for MonadPseudoRandom rather than the one for IO, so it touches neither the system nor the per-thread generator: it advances the generator it was given and hands it back. Keep that generator and the bytes are reproducible from the seed, which is what makes a test repeatable -- and what makes a generator unfit for keys unless its seed came from the system.

drgNew is seedNew and drgNewSeed in one step; drgNewTest takes four numbers instead of a seed, for a test that must give the same answer twice.

Drawing from a monad stacked on IO

There are two instances, one for IO and one for MonadPseudoRandom, and none for the transformers, so a ReaderT env IO or a StateT s IO reaches the system through liftIO:

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Reader (ReaderT)

newKey :: ReaderT env IO ByteString
newKey = liftIO (getRandomBytes 32)

What that draws is what the first section describes, no more and no less: the IO instance, the generator of the operating system thread the call lands on, seeded from the system and reseeded as it goes. liftIO says which monad the draw happens in and nothing about where the bytes come from.

Writing an instance for the stack instead would make the choice invisible at the call site, and the class cannot tell a strong source from a weak one -- see MonadRandom.

When the system will not give any

Drawing randomness is the one thing here with nothing to fall back on, so the failure is an exception rather than a value: there is no sensible Maybe to return and no partial answer worth having. Since 2.2 it is an EntropyError and not a Control.Exception.ErrorCall or an Control.Exception.IOException, which is worth knowing if you were catching one of those:

import Control.Exception (catch)
import Crypto.Random (EntropyError (..), getRandomBytes)

key <- getRandomBytes 32 `catch` \e -> case e of
    NoEntropySource   -> fail "this system offers no randomness at all"
    EntropyShort w g  -> fail (show w ++ " bytes wanted, " ++ show g ++ " arrived")
    EntropySourceLost s -> fail ("the source " ++ s ++ " went away")

Catching it at all is a decision rather than a default. A program that cannot get randomness cannot make a key, and stopping is usually the honest thing; the reason to catch is to say so in the program's own terms rather than in crypton's.

Synopsis

Drawing from the system

class Monad m => MonadRandom (m :: Type -> Type) where Source #

A monad constraint that allows to generate random bytes

Everything in this library that draws a key, a nonce or a signature's randomness draws it through this class, and cannot tell a strong source from a weak one. The two instances below are the ones to reach for: IO reads the system entropy source, and MonadPseudoRandom runs a DRG seeded from it.

An instance of your own is held to the same standard. A generator that another party can predict, or that repeats, yields keys and signatures that give away what they are meant to keep -- so the deliberately repeatable instance that makes a test reproducible is not one to ship with.

Methods

getRandomBytes :: ByteArray byteArray => Int -> m byteArray Source #

Instances

Instances details
MonadRandom IO Source #

Through the generator of SysDRG: a ChaCha20 generator per operating system thread, reseeded from the system. Where that cannot be seeded -- a system with no getrandom(2) or getentropy(3), which includes Windows for now -- this is the system entropy source directly, as it was before the generator existed.

Instance details

Defined in Crypto.Random.Types

Methods

getRandomBytes :: ByteArray byteArray => Int -> IO byteArray Source #

DRG gen => MonadRandom (MonadPseudoRandom gen) Source # 
Instance details

Defined in Crypto.Random.Types

Methods

getRandomBytes :: ByteArray byteArray => Int -> MonadPseudoRandom gen byteArray Source #

Drawing from a generator you hold

data Seed Source #

The material a deterministic generator is built from. Two generators made from one seed produce the same bytes, which is what drgNewSeed is for and why a seed kept anywhere is as good as the keys drawn from it.

Instances

Instances details
ByteArrayAccess Seed Source # 
Instance details

Defined in Crypto.Random

Methods

length :: Seed -> Int #

withByteArray :: Seed -> (Ptr p -> IO a) -> IO a #

copyByteArrayToPtr :: Seed -> Ptr p -> IO () #

seedNew :: MonadRandom randomly => randomly Seed Source #

Create a new Seed from system entropy

seedFromInteger :: Integer -> Seed Source #

Convert an integer to a Seed

seedToInteger :: Seed -> Integer Source #

Convert a Seed to an integer

seedFromBinary :: ByteArrayAccess b => b -> CryptoFailable Seed Source #

Convert a binary to a seed

drgNewSeed :: Seed -> ChaChaDRG Source #

Create a new DRG from a seed

drgNew :: MonadRandom randomly => randomly ChaChaDRG Source #

Create a new DRG from system entropy

drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG Source #

Create a new DRG from 5 Word64.

This is a convenient interface to create deterministic interface for quickcheck style testing.

It can also be used in other contexts provided the input has been properly randomly generated.

Note that the Arbitrary instance provided by QuickCheck for Word64 does not have a uniform distribution. It is often better to use instead arbitraryBoundedRandom.

System endianness impacts how the tuple is interpreted and therefore changes the resulting DRG.

withDRG :: DRG gen => gen -> MonadPseudoRandom gen a -> (a, gen) Source #

Run a pure computation with a Deterministic Random Generator in the MonadPseudoRandom

withRandomBytes :: (ByteArray ba, DRG g) => g -> Int -> (ba -> a) -> (a, g) Source #

Generate len random bytes and mapped the bytes to the function f.

This is equivalent to use Control.Arrow first with randomBytesGenerate

data MonadPseudoRandom gen a Source #

A simple Monad class very similar to a State Monad with the state being a DRG.

Instances

Instances details
DRG gen => Applicative (MonadPseudoRandom gen) Source # 
Instance details

Defined in Crypto.Random.Types

Methods

pure :: a -> MonadPseudoRandom gen a #

(<*>) :: MonadPseudoRandom gen (a -> b) -> MonadPseudoRandom gen a -> MonadPseudoRandom gen b #

liftA2 :: (a -> b -> c) -> MonadPseudoRandom gen a -> MonadPseudoRandom gen b -> MonadPseudoRandom gen c #

(*>) :: MonadPseudoRandom gen a -> MonadPseudoRandom gen b -> MonadPseudoRandom gen b #

(<*) :: MonadPseudoRandom gen a -> MonadPseudoRandom gen b -> MonadPseudoRandom gen a #

DRG gen => Functor (MonadPseudoRandom gen) Source # 
Instance details

Defined in Crypto.Random.Types

Methods

fmap :: (a -> b) -> MonadPseudoRandom gen a -> MonadPseudoRandom gen b #

(<$) :: a -> MonadPseudoRandom gen b -> MonadPseudoRandom gen a #

DRG gen => Monad (MonadPseudoRandom gen) Source # 
Instance details

Defined in Crypto.Random.Types

Methods

(>>=) :: MonadPseudoRandom gen a -> (a -> MonadPseudoRandom gen b) -> MonadPseudoRandom gen b #

(>>) :: MonadPseudoRandom gen a -> MonadPseudoRandom gen b -> MonadPseudoRandom gen b #

return :: a -> MonadPseudoRandom gen a #

DRG gen => MonadRandom (MonadPseudoRandom gen) Source # 
Instance details

Defined in Crypto.Random.Types

Methods

getRandomBytes :: ByteArray byteArray => Int -> MonadPseudoRandom gen byteArray Source #

class DRG gen where Source #

A Deterministic Random Generator (DRG) class

Methods

randomBytesGenerate :: ByteArray byteArray => Int -> gen -> (byteArray, gen) Source #

Generate N bytes of randomness from a DRG

Instances

Instances details
DRG ChaChaDRG Source # 
Instance details

Defined in Crypto.Random.ChaChaDRG

Methods

randomBytesGenerate :: ByteArray byteArray => Int -> ChaChaDRG -> (byteArray, ChaChaDRG) Source #

DRG SystemDRG Source # 
Instance details

Defined in Crypto.Random.SystemDRG

Methods

randomBytesGenerate :: ByteArray byteArray => Int -> SystemDRG -> (byteArray, SystemDRG) Source #

The generators

data ChaChaDRG Source #

ChaCha Deterministic Random Generator

Instances

Instances details
DRG ChaChaDRG Source # 
Instance details

Defined in Crypto.Random.ChaChaDRG

Methods

randomBytesGenerate :: ByteArray byteArray => Int -> ChaChaDRG -> (byteArray, ChaChaDRG) Source #

NFData ChaChaDRG Source # 
Instance details

Defined in Crypto.Random.ChaChaDRG

Methods

rnf :: ChaChaDRG -> () #

data SystemDRG Source #

A referentially transparent System representation of the random evaluated out of the system.

Holding onto a specific DRG means that all the already evaluated bytes will be consistently replayed.

There's no need to reseed this DRG, as only pure entropy is represented here.

Instances

Instances details
DRG SystemDRG Source # 
Instance details

Defined in Crypto.Random.SystemDRG

Methods

randomBytesGenerate :: ByteArray byteArray => Int -> SystemDRG -> (byteArray, SystemDRG) Source #

getSystemDRG :: IO SystemDRG Source #

Grab one instance of the System DRG

When the system will not give any

data EntropyError Source #

The system would not give the entropy it was asked for.

This is the one failure in the library with nothing to fall back on and nothing sensible to return: a key drawn from bytes that are not random is worse than no key. It used to be reported with error and fail, which left a caller no way to tell it from a bug in the library, and no way to say anything useful about it.

Constructors

NoEntropySource

The system offers no source of entropy at all. On Unix that means no getrandom(2), no getentropy(3) and no /dev to read from; a container built from nothing would look like this.

EntropyShort Int Int

The sources between them gave fewer bytes than were asked for, three times over. The two numbers are how many were wanted and how many arrived.

EntropySourceLost String

A source that could be opened once could not be opened again. The string names it.