-- | 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
  , delete
  , withGenerator
  , nonce128
  , nonce128url
  , nonce128urlT
  ) where

import Control.Monad (liftM)
import Control.Monad.IO.Class (MonadIO, liftIO)
import qualified System.Entropy as Entropy
import Data.Typeable (Typeable)
import Control.Monad.IO.Unlift (MonadUnliftIO)
import UnliftIO.Exception (bracket)

import qualified Data.ByteString as B
import qualified Data.ByteString.Base64.URL as B64URL
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE


-- | An encapsulated nonce generator.
newtype Generator = G Entropy.CryptHandle
  deriving (Typeable)

instance Show Generator where
  show :: Generator -> String
show Generator
_ = String
"<NonceGenerator>"


-- | Create a new nonce generator using the system entropy.
new :: MonadIO m => m Generator
new :: forall (m :: * -> *). MonadIO m => m Generator
new = (CryptHandle -> Generator) -> m CryptHandle -> m Generator
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM CryptHandle -> Generator
G (m CryptHandle -> m Generator)
-> (IO CryptHandle -> m CryptHandle)
-> IO CryptHandle
-> m Generator
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO CryptHandle -> m CryptHandle
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO CryptHandle -> m Generator) -> IO CryptHandle -> m Generator
forall a b. (a -> b) -> a -> b
$ IO CryptHandle
Entropy.openHandle


-- | Release the given generator's resources. The generator won't be
-- usable afterwards.
delete :: MonadIO m => Generator -> m ()
delete :: forall (m :: * -> *). MonadIO m => Generator -> m ()
delete (G CryptHandle
v) = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ CryptHandle -> IO ()
Entropy.closeHandle CryptHandle
v


-- | An exception-safe convenience function.
--
-- @
-- withGenerator = bracket new delete
-- @
--
withGenerator :: MonadUnliftIO m => (Generator -> m a) -> m a
withGenerator :: forall (m :: * -> *) a.
MonadUnliftIO m =>
(Generator -> m a) -> m a
withGenerator = m Generator -> (Generator -> m ()) -> (Generator -> m a) -> m a
forall (m :: * -> *) a b c.
MonadUnliftIO m =>
m a -> (a -> m b) -> (a -> m c) -> m c
bracket m Generator
forall (m :: * -> *). MonadIO m => m Generator
new Generator -> m ()
forall (m :: * -> *). MonadIO m => Generator -> m ()
delete


-- | (Internal) Generate the given number of bytes from the DRG.
genBytes :: MonadIO m => Int -> Generator -> m B.ByteString
genBytes :: forall (m :: * -> *). MonadIO m => Int -> Generator -> m ByteString
genBytes Int
n (G CryptHandle
v) = IO ByteString -> m ByteString
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ByteString -> m ByteString) -> IO ByteString -> m ByteString
forall a b. (a -> b) -> a -> b
$ CryptHandle -> Int -> IO ByteString
Entropy.hGetEntropy CryptHandle
v Int
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 :: forall (m :: * -> *). MonadIO m => Generator -> m ByteString
nonce128 = Int -> Generator -> m ByteString
forall (m :: * -> *). MonadIO m => Int -> Generator -> m ByteString
genBytes Int
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 :: forall (m :: * -> *). MonadIO m => Generator -> m ByteString
nonce128url = (ByteString -> ByteString) -> m ByteString -> m ByteString
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM ByteString -> ByteString
B64URL.encode (m ByteString -> m ByteString)
-> (Generator -> m ByteString) -> Generator -> m ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Generator -> m ByteString
forall (m :: * -> *). MonadIO m => Int -> Generator -> m ByteString
genBytes Int
18


-- | Same as 'nonce128url', but returns its result as 'T.Text'
-- instead.
nonce128urlT :: MonadIO m => Generator -> m T.Text
nonce128urlT :: forall (m :: * -> *). MonadIO m => Generator -> m Text
nonce128urlT = (ByteString -> Text) -> m ByteString -> m Text
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM ByteString -> Text
TE.decodeUtf8 (m ByteString -> m Text)
-> (Generator -> m ByteString) -> Generator -> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Generator -> m ByteString
forall (m :: * -> *). MonadIO m => Generator -> m ByteString
nonce128url