-- | Usage of this module is very simple. Here is a sample GHCi run: -- -- @ -- *Crypto.Nonce> g <- new -- *Crypto.Nonce> nonce128 g -- \"c\\164\\252\\162f\\207\\245\\ESC`\\180p\\DC4\\234\\223QP\" -- *Crypto.Nonce> nonce128 g -- \"\\203C\\190\\138aI\\158\\194\\146\\&7\\208\\&7\\ETX0\\f\\229\" -- *Crypto.Nonce> nonce128url g -- \"3RP-iEFT-6NrpCMsxigondMC\" -- *Crypto.Nonce> nonce128url g -- \"MVZH3Gi5zSKXJY-_qdtznxla\" -- *Crypto.Nonce> nonce128url g -- \"3f3cVNfuZT62-uGco1CBThci\" -- *Crypto.Nonce> nonce128urlT g -- \"iGMJyrRkw2QMp09SRy59s4Jx\" -- *Crypto.Nonce> nonce128urlT g -- \"WsHs0KwYiex3tsqQZ8b0119_\" -- *Crypto.Nonce> nonce128urlT g -- \"JWkLSX7qSFGu1Q3PHuExwurF\" -- @ -- -- The functions that generate nonces are not pure on purpose, -- since that makes it a lot harder to reuse the same nonce. module Crypto.Nonce ( Generator , new , nonce128 , nonce128url , nonce128urlT ) where import Control.Monad (liftM) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Tuple (swap) import Data.Typeable (Typeable) import qualified Crypto.Random as R import qualified Crypto.Random.AESCtr as AESCtr import qualified Data.ByteString as B import qualified Data.ByteString.Base64.URL as B64URL import qualified Data.IORef as I import qualified Data.Text as T import qualified Data.Text.Encoding as TE -- | An encapsulated nonce generator. data Generator = G (I.IORef AESCtr.AESRNG) deriving (Typeable) instance Show Generator where show _ = "" -- | Create a new nonce generator using the system entropy. new :: MonadIO m => m Generator new = liftM G . liftIO $ AESCtr.makeSystem >>= I.newIORef -- | (Internal) Generate the given number of bytes from the AES CPRNG. genBytes :: MonadIO m => Int -> Generator -> m B.ByteString genBytes n (G v) = liftIO $ I.atomicModifyIORef v $ swap . R.cprgGenerate n -- | Generate a 128 bit nonce as a 'B.ByteString' of 16 bytes. -- Each byte may have any value from @0@ to @255@. nonce128 :: MonadIO m => Generator -> m B.ByteString nonce128 = genBytes 16 -- | Generate a 128 bit nonce as a 'B.ByteString' of 24 bytes. -- Each byte is either a letter (upper or lowercase), a digit, a -- dash (@-@) or an underscore (@_@), which is the set of -- characters from the base64url encoding. In order to avoid any -- issues with padding, the generated nonce actually has 144 bits. nonce128url :: MonadIO m => Generator -> m B.ByteString nonce128url = liftM B64URL.encode . genBytes 18 -- | Same as 'nonce128url', but returns its result as 'T.Text' -- instead. nonce128urlT :: MonadIO m => Generator -> m T.Text nonce128urlT = liftM TE.decodeUtf8 . nonce128url