nettle-0.4: src/Crypto/Nettle/SIV.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
{- |
Module : Crypto.Nettle.SIV
Copyright : (c) 2026 Clint Adams
License : MIT-style (see the file COPYING)
Maintainer : clint@debian.org
Stability : experimental
Portability : portable
This module exports the SIV authenticated-encryption with associated-data
(AEAD) construction (RFC 5297, AES-SIV) based on AES-CMAC, as supported by
nettle:
<http://www.lysator.liu.se/~nisse/nettle/>
-}
module Crypto.Nettle.SIV
( -- * SIV
--
-- 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.
sivAES128Encrypt
, sivAES128Decrypt
, sivAES256Encrypt
, sivAES256Decrypt
) where
import qualified Data.ByteArray as BA
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import Foreign.C.Types (CInt)
import Crypto.Nettle.Ciphers.ForeignImports
import Nettle.Utils
{- |
Encrypt plain text and create a verification tag for the encrypted text and some additional data.
SIV is deterministic: the @nonce@ is used only as additional data, and is not required to be unique.
The returned tag is 16 bytes long, but may be shortened for verification (losing security).
-}
sivAES128Encrypt
:: B.ByteString
-- ^ @key@ (must be 32 bytes)
-> B.ByteString
-- ^ @nonce@ (must not be empty)
-> 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
sivAES128Encrypt =
sivEncrypt
c_siv_cmac_aes128_ctx_size
c_siv_cmac_aes128_set_key
c_siv_cmac_aes128_encrypt_message
32
{- |
Decrypt cipher text and verify a (possible shortened) tag for the encrypted text and some additional data.
-}
sivAES128Decrypt
:: B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> Maybe B.ByteString
sivAES128Decrypt =
sivDecrypt
c_siv_cmac_aes128_ctx_size
c_siv_cmac_aes128_set_key
c_siv_cmac_aes128_decrypt_message
32
{- |
Encrypt plain text and create a verification tag for the encrypted text and some additional data.
SIV is deterministic: the @nonce@ is used only as additional data, and is not required to be unique.
The returned tag is 16 bytes long, but may be shortened for verification (losing security).
-}
sivAES256Encrypt
:: B.ByteString
-- ^ @key@ (must be 64 bytes)
-> B.ByteString
-- ^ @nonce@ (must not be empty)
-> 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
sivAES256Encrypt =
sivEncrypt
c_siv_cmac_aes256_ctx_size
c_siv_cmac_aes256_set_key
c_siv_cmac_aes256_encrypt_message
64
{- |
Decrypt cipher text and verify a (possible shortened) tag for the encrypted text and some additional data.
-}
sivAES256Decrypt
:: B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> Maybe B.ByteString
sivAES256Decrypt =
sivDecrypt
c_siv_cmac_aes256_ctx_size
c_siv_cmac_aes256_set_key
c_siv_cmac_aes256_decrypt_message
64
sivEncrypt
:: Int
-> (Ptr Word8 -> Ptr Word8 -> IO ())
-> ( Ptr Word8
-> Word
-> Ptr Word8
-> Word
-> Ptr Word8
-> Word
-> Ptr Word8
-> Ptr Word8
-> IO ()
)
-> Int
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> (B.ByteString, B.ByteString)
sivEncrypt ctxsize setkey encmsg keysize key nonce aad plain = unsafeDupablePerformIO $ do
let k = copyAndConvertToScrubbedBytes key
let clength = B.length plain + 16
out <- B.create clength (\_ -> return ())
_ <- withByteStringPtr plain $ \_ pptr ->
withByteStringPtr aad $ \aadsize aadptr ->
withByteStringPtr nonce $ \noncesize nonceptr ->
withByteStringPtr out $ \_ outptr ->
withAlignedContext ctxsize $ \ctxptr ->
BA.withByteArray k $ \kptr ->
if BA.length k /= keysize
then error "Invalid key length"
else
if noncesize == 0
then error "Invalid nonce length"
else do
setkey ctxptr kptr
encmsg
ctxptr
noncesize
nonceptr
aadsize
aadptr
(fromIntegral clength)
outptr
pptr
return (B.drop 16 out, B.take 16 out)
sivDecrypt
:: Int
-> (Ptr Word8 -> Ptr Word8 -> IO ())
-> ( Ptr Word8
-> Word
-> Ptr Word8
-> Word
-> Ptr Word8
-> Word
-> Ptr Word8
-> Ptr Word8
-> IO CInt
)
-> Int
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> B.ByteString
-> Maybe B.ByteString
sivDecrypt ctxsize setkey decmsg keysize key nonce aad cipher verifytag = unsafeDupablePerformIO $ do
let k = copyAndConvertToScrubbedBytes key
let src = verifytag `B.append` cipher
plain <- B.create (B.length cipher) (\_ -> return ())
ok <- withByteStringPtr src $ \_ srcptr ->
withByteStringPtr aad $ \aadsize aadptr ->
withByteStringPtr nonce $ \noncesize nonceptr ->
withByteStringPtr plain $ \_ plainptr ->
withAlignedContext ctxsize $ \ctxptr ->
BA.withByteArray k $ \kptr ->
if BA.length k /= keysize
then error "Invalid key length"
else
if noncesize == 0
then error "Invalid nonce length"
else do
setkey ctxptr kptr
decmsg
ctxptr
noncesize
nonceptr
aadsize
aadptr
(fromIntegral (B.length cipher))
plainptr
srcptr
if ok /= 0 then return $ Just plain else return Nothing