{-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | -- Module : Crypto.Random -- License : BSD-style -- Maintainer : Vincent Hanquez <vincent@snarc.org> -- Stability : stable -- Portability : good -- -- 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. module Crypto.Random ( -- * Drawing from the system MonadRandom (..), -- * Drawing from a generator you hold Seed, seedNew, seedFromInteger, seedToInteger, seedFromBinary, drgNewSeed, drgNew, drgNewTest, withDRG, withRandomBytes, MonadPseudoRandom, DRG (..), -- * The generators ChaChaDRG, SystemDRG, getSystemDRG, -- * When the system will not give any EntropyError (..), ) where import Crypto.Error import Crypto.Internal.Imports import Crypto.Random.ChaChaDRG import Crypto.Random.Entropy (EntropyError (..)) import Crypto.Random.SystemDRG import Crypto.Random.Types import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes) import qualified Data.ByteArray as B import qualified Crypto.Number.Serialize as Serialize #ifdef INSECURE_ENTROPY import Crypto.Hash (SHA512, Context) import Crypto.Hash.IO import Data.Memory.PtrMethods (memSet) import Foreign.Ptr (Ptr, castPtr) #endif -- | 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. newtype Seed = Seed ScrubbedBytes deriving (Seed -> Int (Seed -> Int) -> (forall p a. Seed -> (Ptr p -> IO a) -> IO a) -> (forall p. Seed -> Ptr p -> IO ()) -> ByteArrayAccess Seed forall p. Seed -> Ptr p -> IO () forall ba. (ba -> Int) -> (forall p a. ba -> (Ptr p -> IO a) -> IO a) -> (forall p. ba -> Ptr p -> IO ()) -> ByteArrayAccess ba forall p a. Seed -> (Ptr p -> IO a) -> IO a $clength :: Seed -> Int length :: Seed -> Int $cwithByteArray :: forall p a. Seed -> (Ptr p -> IO a) -> IO a withByteArray :: forall p a. Seed -> (Ptr p -> IO a) -> IO a $ccopyByteArrayToPtr :: forall p. Seed -> Ptr p -> IO () copyByteArrayToPtr :: forall p. Seed -> Ptr p -> IO () ByteArrayAccess) -- Length for ChaCha DRG seed seedLength :: Int seedLength :: Int seedLength = Int 40 -- | Create a new Seed from system entropy seedNew :: MonadRandom randomly => randomly Seed #ifdef INSECURE_ENTROPY -- The degree of its randomness depends on the source, e.g. for iOS we -- have to compile with DoNotUseEntropy flag, as iOS doesn't allow -- using getentropy, and on some other systems it can be also -- potentially comprisable sources. Hashing of entropy before using -- it as a seed is a common mitigation for attacks via RNG/entropy -- source. seedNew = (Seed . scrubbedHash512) `fmap` getRandomBytes 64 scrubbedHash512 :: ScrubbedBytes -> ScrubbedBytes scrubbedHash512 = B.take seedLength . hash512 where hash512 ba = B.unsafeCreate (hashDigestSize (undefined :: SHA512)) $ hashIO ba hashIO ba ptr = do ctx <- hashMutableInit hashMutableUpdate (ctx :: MutableContext SHA512) ba B.withByteArray ctx $ \pctx -> do hashInternalFinalize (castPtr pctx :: Ptr (Context SHA512)) ptr memSet pctx 0 $ hashInternalContextSize (undefined :: SHA512) #else seedNew :: forall (randomly :: * -> *). MonadRandom randomly => randomly Seed seedNew = ScrubbedBytes -> Seed Seed (ScrubbedBytes -> Seed) -> randomly ScrubbedBytes -> randomly Seed forall a b. (a -> b) -> randomly a -> randomly b forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b `fmap` Int -> randomly ScrubbedBytes forall byteArray. ByteArray byteArray => Int -> randomly byteArray forall (m :: * -> *) byteArray. (MonadRandom m, ByteArray byteArray) => Int -> m byteArray getRandomBytes Int seedLength #endif -- | Convert a Seed to an integer seedToInteger :: Seed -> Integer seedToInteger :: Seed -> Integer seedToInteger (Seed ScrubbedBytes b) = ScrubbedBytes -> Integer forall ba. ByteArrayAccess ba => ba -> Integer Serialize.os2ip ScrubbedBytes b -- | Convert an integer to a Seed seedFromInteger :: Integer -> Seed seedFromInteger :: Integer -> Seed seedFromInteger Integer i = ScrubbedBytes -> Seed Seed (ScrubbedBytes -> Seed) -> ScrubbedBytes -> Seed forall a b. (a -> b) -> a -> b $ Int -> Integer -> ScrubbedBytes forall ba. ByteArray ba => Int -> Integer -> ba Serialize.i2ospOf_ Int seedLength (Integer i Integer -> Integer -> Integer forall a. Integral a => a -> a -> a `mod` Integer 2 Integer -> Int -> Integer forall a b. (Num a, Integral b) => a -> b -> a ^ (Int seedLength Int -> Int -> Int forall a. Num a => a -> a -> a * Int 8)) -- | Convert a binary to a seed seedFromBinary :: ByteArrayAccess b => b -> CryptoFailable Seed seedFromBinary :: forall b. ByteArrayAccess b => b -> CryptoFailable Seed seedFromBinary b b | b -> Int forall ba. ByteArrayAccess ba => ba -> Int B.length b b Int -> Int -> Bool forall a. Eq a => a -> a -> Bool /= Int 40 = CryptoError -> CryptoFailable Seed forall a. CryptoError -> CryptoFailable a CryptoFailed (CryptoError CryptoError_SeedSizeInvalid) | Bool otherwise = Seed -> CryptoFailable Seed forall a. a -> CryptoFailable a CryptoPassed (Seed -> CryptoFailable Seed) -> Seed -> CryptoFailable Seed forall a b. (a -> b) -> a -> b $ ScrubbedBytes -> Seed Seed (ScrubbedBytes -> Seed) -> ScrubbedBytes -> Seed forall a b. (a -> b) -> a -> b $ b -> ScrubbedBytes forall bin bout. (ByteArrayAccess bin, ByteArray bout) => bin -> bout B.convert b b -- | Create a new DRG from system entropy drgNew :: MonadRandom randomly => randomly ChaChaDRG drgNew :: forall (randomly :: * -> *). MonadRandom randomly => randomly ChaChaDRG drgNew = Seed -> ChaChaDRG drgNewSeed (Seed -> ChaChaDRG) -> randomly Seed -> randomly ChaChaDRG forall a b. (a -> b) -> randomly a -> randomly b forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b `fmap` randomly Seed forall (randomly :: * -> *). MonadRandom randomly => randomly Seed seedNew -- | Create a new DRG from a seed drgNewSeed :: Seed -> ChaChaDRG drgNewSeed :: Seed -> ChaChaDRG drgNewSeed (Seed ScrubbedBytes seed) = ScrubbedBytes -> ChaChaDRG forall seed. ByteArrayAccess seed => seed -> ChaChaDRG initialize ScrubbedBytes seed -- | 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. drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG drgNewTest = (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG initializeWords -- | Generate @len random bytes and mapped the bytes to the function @f. -- -- This is equivalent to use Control.Arrow 'first' with 'randomBytesGenerate' withRandomBytes :: (ByteArray ba, DRG g) => g -> Int -> (ba -> a) -> (a, g) withRandomBytes :: forall ba g a. (ByteArray ba, DRG g) => g -> Int -> (ba -> a) -> (a, g) withRandomBytes g rng Int len ba -> a f = (ba -> a f ba bs, g rng') where (ba bs, g rng') = Int -> g -> (ba, g) forall byteArray. ByteArray byteArray => Int -> g -> (byteArray, g) forall gen byteArray. (DRG gen, ByteArray byteArray) => Int -> gen -> (byteArray, gen) randomBytesGenerate Int len g rng