packages feed

nettle-0.4: src/Crypto/Nettle/EAX.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

-----------------------------------------------------------------------------

-----------------------------------------------------------------------------

{- |
Module      :  Crypto.Nettle.EAX
Copyright   :  (c) 2026 Clint Adams
License     :  MIT-style (see the file COPYING)

Maintainer  :  clint@debian.org
Stability   :  experimental
Portability :  portable

This module exports the EAX authenticated-encryption with associated-data
(AEAD) construction (Bellare, Rogaway, Wagner; NIST) based on AES-128,
as supported by nettle:
  <http://www.lysator.liu.se/~nisse/nettle/>
-}
module Crypto.Nettle.EAX
    ( -- * EAX

    --
    -- No streaming interface is provided, as this basically violates the
    -- spirit of the "AEAD-should-be-simple-to-use" concept - you only can
    -- use the decrypted data after it got successfully verified.

      eaxAES128Encrypt
    , eaxAES128Decrypt
    ) where

import qualified Data.ByteArray as BA
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B

import Crypto.Nettle.Ciphers.ForeignImports
import Nettle.Utils

{- |
Encrypt plain text and create a verification tag for the encrypted text and some additional data.
@key@ and @nonce@ must not be reused together.
The returned tag is 16 bytes long, but may be shortened for verification (losing security).
-}
eaxAES128Encrypt
    :: B.ByteString
    -- ^ @key@ (must be 16 bytes)
    -> B.ByteString
    -- ^ @nonce@
    -> B.ByteString
    -- ^ @aad@ additional data to be verified
    -> B.ByteString
    -- ^ @plain@ data to encrypt
    -> (B.ByteString, B.ByteString)
    -- ^ returns (@cipher@, @tag@) ciphertext and verification tag
eaxAES128Encrypt key nonce aad plain = unsafeDupablePerformIO $ do
    let k = copyAndConvertToScrubbedBytes key
    tag <- B.create 16 (\_ -> return ())
    cipher <- B.create (B.length plain) (\_ -> return ())
    _ <- withByteStringPtr plain $ \psize pptr ->
        withByteStringPtr aad $ \aadsize aadptr ->
            withByteStringPtr cipher $ \_ cipherptr ->
                withByteStringPtr tag $ \_ tagptr ->
                    alignedCtxCreate c_eax_aes128_ctx_size $ \ctxptr ->
                        BA.withByteArray k $ \kptr ->
                            if BA.length k /= 16
                                then error "Invalid key length"
                                else withByteStringPtr nonce $ \noncesize nonceptr -> do
                                    c_eax_aes128_set_key ctxptr kptr
                                    c_eax_aes128_set_nonce ctxptr noncesize nonceptr
                                    c_eax_aes128_update ctxptr aadsize aadptr
                                    c_eax_aes128_encrypt ctxptr psize cipherptr pptr
                                    callNettleHashDigest c_eax_aes128_digest 16 ctxptr tagptr
    return (cipher, tag)

{- |
Decrypt cipher text and verify a (possible shortened) tag for the encrypted text and some additional data.
@key@ and @nonce@ must not be reused together.
-}
eaxAES128Decrypt
    :: B.ByteString
    -> B.ByteString
    -> B.ByteString
    -> B.ByteString
    -> B.ByteString
    -> Maybe B.ByteString
eaxAES128Decrypt key nonce aad cipher verifytag = unsafeDupablePerformIO $ do
    let k = copyAndConvertToScrubbedBytes key
    tag <- B.create 16 (\_ -> return ())
    plain <- B.create (B.length cipher) (\_ -> return ())
    _ <- withByteStringPtr cipher $ \psize pptr ->
        withByteStringPtr aad $ \aadsize aadptr ->
            withByteStringPtr plain $ \_ plainptr ->
                withByteStringPtr tag $ \_ tagptr ->
                    alignedCtxCreate c_eax_aes128_ctx_size $ \ctxptr ->
                        BA.withByteArray k $ \kptr ->
                            if BA.length k /= 16
                                then error "Invalid key length"
                                else withByteStringPtr nonce $ \noncesize nonceptr -> do
                                    c_eax_aes128_set_key ctxptr kptr
                                    c_eax_aes128_set_nonce ctxptr noncesize nonceptr
                                    c_eax_aes128_update ctxptr aadsize aadptr
                                    c_eax_aes128_decrypt ctxptr psize plainptr pptr
                                    callNettleHashDigest c_eax_aes128_digest 16 ctxptr tagptr
    if B.take (B.length verifytag) tag == verifytag
        then return $ Just plain
        else return Nothing