-- | -- Module : Crypto.Saltine.Core.AEAD -- Copyright : (c) Thomas DuBuisson 2017 -- License : MIT -- -- Maintainer : me@jspha.com -- Stability : experimental -- Portability : non-portable -- -- Secret-key authenticated encryption with additional data (AEAD): -- "Crypto.Saltine.Core.AEAD" -- -- The 'aead' function encrypts and authenticates a message -- 'ByteString' and additional authenticated data 'ByteString' -- using a secret key and a nonce. The 'aeadOpen' -- function verifies and decrypts a ciphertext 'ByteString' using a -- secret key and a nonce. If the ciphertext fails validation, -- 'aeadOpen' returns 'Nothing'. -- -- The "Crypto.Saltine.Core.AEAD" module is designed to meet -- the standard notions of privacy and authenticity for a secret-key -- authenticated-encryption scheme using nonces. For formal -- definitions see, e.g., Bellare and Namprempre, "Authenticated -- encryption: relations among notions and analysis of the generic -- composition paradigm," Lecture Notes in Computer Science 1976 -- (2000), 531–545, . -- -- Note that the length is not hidden. Note also that it is the -- caller's responsibility to ensure the uniqueness of nonces—for -- example, by using nonce 1 for the first message, nonce 2 for the -- second message, etc. Nonces are long enough that randomly generated -- nonces have negligible risk of collision. module Crypto.Saltine.Core.AEAD ( Key, Nonce, aead, aeadOpen, aeadDetached, aeadOpenDetached, newKey, newNonce ) where import Crypto.Saltine.Class import Crypto.Saltine.Internal.Util import qualified Crypto.Saltine.Internal.ByteSizes as Bytes import Control.Applicative import Foreign.C import Foreign.Ptr import qualified Data.ByteString as S import Data.ByteString (ByteString) -- $types -- | An opaque 'secretbox' cryptographic key. newtype Key = Key ByteString deriving (Eq, Ord) instance IsEncoding Key where decode v = if S.length v == Bytes.secretBoxKey then Just (Key v) else Nothing {-# INLINE decode #-} encode (Key v) = v {-# INLINE encode #-} -- | An opaque 'secretbox' nonce. newtype Nonce = Nonce ByteString deriving (Eq, Ord) instance IsEncoding Nonce where decode v = if S.length v == Bytes.secretBoxNonce then Just (Nonce v) else Nothing {-# INLINE decode #-} encode (Nonce v) = v {-# INLINE encode #-} instance IsNonce Nonce where zero = Nonce (S.replicate Bytes.secretBoxNonce 0) nudge (Nonce n) = Nonce (nudgeBS n) -- | Creates a random key of the correct size for 'secretbox'. newKey :: IO Key newKey = Key <$> randomByteString Bytes.secretBoxKey -- | Creates a random nonce of the correct size for 'secretbox'. newNonce :: IO Nonce newNonce = Nonce <$> randomByteString Bytes.secretBoxNonce -- | Encrypts a message. It is infeasible for an attacker to decrypt -- the message so long as the 'Nonce' is never repeated. aead :: Key -> Nonce -> ByteString -- ^ Message -> ByteString -- ^ AAD -> ByteString -- ^ Ciphertext aead (Key key) (Nonce nonce) msg aad = snd . buildUnsafeByteString clen $ \pc -> constByteStrings [key, msg, aad, nonce] $ \ [(pk, _), (pm, _), (pa, _), (pn, _)] -> c_aead pc nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk where mlen = S.length msg alen = S.length aad clen = mlen + Bytes.aead_xchacha20poly1305_ietf_ABYTES -- | Decrypts a message. Returns 'Nothing' if the keys and message do -- not match. aeadOpen :: Key -> Nonce -> ByteString -- ^ Ciphertext -> ByteString -- ^ AAD -> Maybe ByteString -- ^ Message aeadOpen (Key key) (Nonce nonce) cipher aad = let (err, vec) = buildUnsafeByteString mlen $ \pm -> constByteStrings [key, cipher, aad, nonce] $ \ [(pk, _), (pc, _), (pa, _), (pn, _)] -> c_aead_open pm nullPtr nullPtr pc (fromIntegral clen) pa (fromIntegral alen) pn pk in hush . handleErrno err $ vec where clen = S.length cipher alen = S.length aad mlen = clen - Bytes.aead_xchacha20poly1305_ietf_ABYTES -- | Encrypts a message. It is infeasible for an attacker to decrypt -- the message so long as the 'Nonce' is never repeated. aeadDetached :: Key -> Nonce -> ByteString -- ^ Message -> ByteString -- ^ AAD -> (ByteString,ByteString) -- ^ Tag, Ciphertext aeadDetached (Key key) (Nonce nonce) msg aad = buildUnsafeByteString clen $ \pc -> fmap snd . buildUnsafeByteString' tlen $ \pt -> constByteStrings [key, msg, aad, nonce] $ \ [(pk, _), (pm, _), (pa, _), (pn, _)] -> c_aead_detached pc pt nullPtr pm (fromIntegral mlen) pa (fromIntegral alen) nullPtr pn pk where mlen = S.length msg alen = S.length aad clen = mlen tlen = Bytes.aead_xchacha20poly1305_ietf_ABYTES -- | Decrypts a message. Returns 'Nothing' if the keys and message do -- not match. aeadOpenDetached :: Key -> Nonce -> ByteString -- ^ Tag -> ByteString -- ^ Ciphertext -> ByteString -- ^ AAD -> Maybe ByteString -- ^ Message aeadOpenDetached (Key key) (Nonce nonce) tag cipher aad | S.length tag /= tlen = Nothing | otherwise = let (err, vec) = buildUnsafeByteString len $ \pm -> constByteStrings [key, tag, cipher, aad, nonce] $ \ [(pk, _), (pt, _), (pc, _), (pa, _), (pn, _)] -> c_aead_open_detached pm nullPtr pc (fromIntegral len) pt pa (fromIntegral alen) pn pk in hush . handleErrno err $ vec where len = S.length cipher alen = S.length aad tlen = Bytes.aead_xchacha20poly1305_ietf_ABYTES -- | The aead C API uses C strings. Always returns 0. foreign import ccall "crypto_aead_xchacha20poly1305_ietf_encrypt" c_aead :: Ptr CChar -- ^ Cipher output buffer -> Ptr CULLong -- ^ Cipher output bytes used -> Ptr CChar -- ^ Constant message input buffer -> CULLong -- ^ Length of message input buffer -> Ptr CChar -- ^ Constant aad input buffer -> CULLong -- ^ Length of aad input buffer -> Ptr CChar -- ^ Unused 'nsec' value (must be NULL) -> Ptr CChar -- ^ Constant nonce buffer -> Ptr CChar -- ^ Constant key buffer -> IO CInt -- | The aead open C API uses C strings. Returns 0 if successful. foreign import ccall "crypto_aead_xchacha20poly1305_ietf_decrypt" c_aead_open :: Ptr CChar -- ^ Message output buffer -> Ptr CULLong -- ^ Message output bytes used -> Ptr CChar -- ^ Unused 'nsec' value (must be NULL) -> Ptr CChar -- ^ Constant ciphertext input buffer -> CULLong -- ^ Length of ciphertext input buffer -> Ptr CChar -- ^ Constant aad input buffer -> CULLong -- ^ Length of aad input buffer -> Ptr CChar -- ^ Constant nonce buffer -> Ptr CChar -- ^ Constant key buffer -> IO CInt -- | The aead C API uses C strings. Always returns 0. foreign import ccall "crypto_aead_xchacha20poly1305_ietf_encrypt_detached" c_aead_detached :: Ptr CChar -- ^ Cipher output buffer -> Ptr CChar -- ^ Tag output buffer -> Ptr CULLong -- ^ Tag bytes used -> Ptr CChar -- ^ Constant message input buffer -> CULLong -- ^ Length of message input buffer -> Ptr CChar -- ^ Constant aad input buffer -> CULLong -- ^ Length of aad input buffer -> Ptr CChar -- ^ Unused 'nsec' value (must be NULL) -> Ptr CChar -- ^ Constant nonce buffer -> Ptr CChar -- ^ Constant key buffer -> IO CInt -- | The aead open C API uses C strings. Returns 0 if successful. foreign import ccall "crypto_aead_xchacha20poly1305_ietf_decrypt_detached" c_aead_open_detached :: Ptr CChar -- ^ Message output buffer -> Ptr CChar -- ^ Unused 'nsec' value (must be NULL) -> Ptr CChar -- ^ Constant ciphertext input buffer -> CULLong -- ^ Length of ciphertext input buffer -> Ptr CChar -- ^ Constant tag input buffer -> Ptr CChar -- ^ Constant aad input buffer -> CULLong -- ^ Length of aad input buffer -> Ptr CChar -- ^ Constant nonce buffer -> Ptr CChar -- ^ Constant key buffer -> IO CInt