{-# OPTIONS_GHC -fno-warn-unused-imports #-}
module Crypto.TripleSec.Tutorial
    ( -- * Quickstart
      -- $quickstart

      -- * Efficient Cipher Use
      -- $efficiency

      -- * mtl
      -- $mtl
    ) where

import Control.Exception

import Crypto.TripleSec
-- $setup
-- >>> import Data.ByteString (ByteString)

-- $quickstart
--    There are 3 different ways to use this library which differ /only/ in how they deal with failure and where
--    they obtain a source of randomness (for generating the salt and IVs).
--
--    This quickstart will go through the same basics of encrypting, decrypting, and error handling for each of the 3
--    provided ways to use the library.
--
--    Let's say we have a password and a message to encrypt:
--
-- >>> let password = "my secret password" :: ByteString
-- >>> let message = "message that will be encrypted" :: ByteString
--
--    - Failure: Runtime exceptions (Control.Exception)
--
--    - Randomness: IO
--
-- >>> encryptIO password message :: IO ByteString
-- ...
--
-- >>> :{
--  encryptIO password "" `catch` \(e :: TripleSecException) -> do
--    print e
--    return ""
-- :}
-- EncryptionException ZeroLengthPlaintext
-- ""
--
-- >>> encryptIO password message >>= decryptIO password
-- "message that will be encrypted"
--
--    - Failure: @ Either TripleSecException a @
--
--    - Randomness: IO
--
-- >>> runTripleSecIO (encrypt password message :: TripleSecIOM ByteString) :: IO (Either TripleSecException ByteString)
-- ...
--
-- >>> :{
--  do
--    result <- runTripleSecIO (encrypt password "")
--    case result of Left err               -> print err
--                   Right encryptedMessage -> print encryptedMessage
-- :}
-- EncryptionException ZeroLengthPlaintext
--
-- >>> runTripleSecIO (encrypt password message >>= decrypt password)
-- Right "message that will be encrypted"
--
--    - Failure: @ Either TripleSecException a @
--
--    - Randomness: @ SystemDRG @ (obtained from 'IO' somewhere along the way)
--
-- >>> generator <- getSystemDRG :: IO SystemDRG
-- >>> evalTripleSecM (encrypt password message :: TripleSecM ByteString) generator :: Either TripleSecException ByteString
-- ...
--
-- >>> :{
--  do
--    generator <- getSystemDRG
--    let (result, newGenerator) = runTripleSecM (encrypt password "") generator
--    case result of Left err               -> print err
--                   Right encryptedMessage -> print encryptedMessage
-- :}
-- EncryptionException ZeroLengthPlaintext
--
-- >>> generator <- getSystemDRG
-- >>> evalTripleSecM (encrypt password message >>= decrypt password) generator
-- Right "message that will be encrypted"


-- $efficiency
--    The functions shown above are exactly what you need for one-off encryption and/or decryption. If this is your use
--    case, you can stop reading now.
--
--    However, if you need to encrypt or decrypt /many/ things at one time, the functions shown above may not be the
--    best way to go. The problem is, each call to 'encrypt' or 'decrypt' rebuilds the cipher (a purposely /very/
--    expensive operation).
--
--    All three monads shown above ('IO', 'TripleSecIOM', 'TripleSecM') provide a way create a cipher once for multiple
--    uses. The examples below will only show how this is done with 'TripleSecIOM' for brevity.
--
--    Note: When creating a cipher for multiple encryptions, please make sure you understand the trade-off that comes
--    from re-using a cipher salt. The potential downsides of this for TripleSec are /exactly/ the same with any other
--    encryption.
--
-- >>> :{
--  runTripleSecIO $ do
--    cipher <- newCipher "mypassword" :: TripleSecIOM (TripleSec ByteString)
--    mapM (encryptWithCipher cipher) ["message1", "message2", "message3"] :: TripleSecIOM [ByteString]
-- :}
-- ...
--
--    Failures are short circuiting.
--
-- >>> :{
--  runTripleSecIO $ do
--    cipher <- newCipher "mypassword" :: TripleSecIOM (TripleSec ByteString)
--    mapM (encryptWithCipher cipher) ["message1", "", "message3"]
-- :}
-- Left (EncryptionException ZeroLengthPlaintext)
--
--    Decryption works the same way.
--
-- >>> :{
--  runTripleSecIO $ do
--    cipher <- newCipher "mypassword" :: TripleSecIOM (TripleSec ByteString)
--    encryptedList <- mapM (encryptWithCipher cipher) ["message1", "message2", "message3"]
--    mapM (decryptWithCipher cipher) encryptedList
-- :}
-- Right ["message1","message2","message3"]
--
--    Keep in mind, @ newCipher @ uses a random salt. @ decryptWithCipher @ will fail if given a cipher with a
--    salt that doesn't match the salt stored in the ciphertext.
--
-- >>> :{
--  runTripleSecIO $ do
--    cipher <- newCipher "mypassword" :: TripleSecIOM (TripleSec ByteString)
--    encryptedList <- mapM (encryptWithCipher cipher) ["message1", "message2", "message3"]
--    secondCipher <- newCipher "mypassword"  -- Note: Same password!
--    mapM (decryptWithCipher secondCipher) encryptedList
-- :}
-- Left (DecryptionException MisMatchedCipherSalt)
--
--    Assuming you /know/ a batch of encrypted messages were all encrypted with the same cipher, you can reconstruct
--    that cipher from one of the messages.
--
-- >>> :{
--  runTripleSecIO $ do
--    cipher <- newCipher "mypassword" :: TripleSecIOM (TripleSec ByteString)
--    encryptedList@(e1:_) <- mapM (encryptWithCipher cipher) ["message1", "message2", "message3"]
--    (_, firstCipherSalt, _) <- checkPrefix e1
--    secondCipher <- newCipherWithSalt "mypassword" firstCipherSalt
--    mapM (decryptWithCipher secondCipher) encryptedList
-- :}
-- Right ["message1","message2","message3"]

-- $mtl
--
--    The tutorial has shown you how to work with both 'TripleSecIOM' and 'TripleSecM'. In reality these are just type
--    aliases for @ TripleSecIOT IO @ and @ TripleSecT Identity @ respectively.
--
--    This library exports these monad transformers themselves for your convenience. It also exports @ runTripleSecT @
--    and @ evalTripleSecT @ for use with 'TripleSecT'.
--
--    However, at this time, only 'MonadTrans' instances of these transformers have been implemented in this library.