triplesec-0.2.2.0: TripleSec is a simple, triple-paranoid, symmetric encryption library

Safe HaskellNone
LanguageHaskell2010

Crypto.TripleSec.Tutorial

Contents

Synopsis

    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"
    

    Decryption Is Easier

    The TripleSec protocol requires random inputs for creating a new cipher (random salt) and actually encrypting data (IVs). There is notably no need for randomness during decryption. To make your life a little easier, a "decryption only" monad (and transformer) is included so you can drop the requirement of randomness from areas of your code that are only concerned with decrypting things.

    Here's how to use it.

    >>> encrypted <- encryptIO "my password" "purity rocks!" :: IO ByteString -- Nothing new here
    >>> let decrypted = runTripleSecDecryptM (decrypt "my password" encrypted) :: Either TripleSecException ByteString
    >>> print decrypted
    Right "purity rocks!"
    

    Efficient Cipher Use

    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.