packages feed

saltine-0.2.2.0: src/Crypto/Saltine/Core/AEAD/AES256GCM.hs

-- |
-- Module      : Crypto.Saltine.Core.AEAD.AES256GCM
-- Copyright   : (c) Thomas DuBuisson 2017
--               (c) Max Amanshauser 2021
-- License     : MIT
--
-- Maintainer  : max@lambdalifting.org
-- Stability   : experimental
-- Portability : non-portable
--
-- Secret-key authenticated encryption with additional data (AEAD):
-- "Crypto.Saltine.Core.AEAD.AES256GCM"
--
-- Using this module is not recommended. Don't use unless you have to.
-- Keep in mind its limitations: https://doc.libsodium.org/secret-key_cryptography/aead
--
-- Unless you know for certain the CPU your program will run on supports
-- Intel SSSE3, AES-NI and CLMUL, you should run @aead_aes256gcm_available@
-- first and only proceed if the result is True.
--
-- Generating nonces for the functions in this module randomly
-- is not recommended, due to the risk of generating collisions.

module Crypto.Saltine.Core.AEAD.AES256GCM (
  Key, Nonce,
  aead_aes256gcm_available,
  aead, aeadOpen,
  aeadDetached, aeadOpenDetached,
  newKey, newNonce
  ) where

import Crypto.Saltine.Internal.AEAD.AES256GCM
            ( c_aead_aes256gcm_is_available
            , c_aead
            , c_aead_open
            , c_aead_detached
            , c_aead_open_detached
            , Key(..)
            , Nonce(..)
            )
import Crypto.Saltine.Internal.Util as U
import Data.ByteString              (ByteString)
import Foreign.Ptr
import System.IO.Unsafe             (unsafePerformIO)

import qualified Crypto.Saltine.Internal.AEAD.AES256GCM as Bytes
import qualified Data.ByteString                        as S

-- | Creates a random 'AES256GCM' key
newKey :: IO Key
newKey = Key <$> randomByteString Bytes.aead_aes256gcm_keybytes

-- | Creates a random 'AES256GCM' nonce
newNonce :: IO Nonce
newNonce = Nonce <$> randomByteString Bytes.aead_aes256gcm_npubbytes


{-# NOINLINE aead_aes256gcm_available #-}
aead_aes256gcm_available :: Bool
aead_aes256gcm_available =
    unsafePerformIO c_aead_aes256gcm_is_available == 1

-- | 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_aes256gcm_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 = do
  let clen   = S.length cipher
      alen   = S.length aad
  mlen <- clen `safeSubtract` Bytes.aead_aes256gcm_abytes
  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
  hush . handleErrno err $ vec

-- | 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_aes256gcm_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_aes256gcm_abytes