packages feed

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

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

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

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

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

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

This module exports the OCB authenticated-encryption with associated-data
(AEAD) construction (Rogaway, "Efficient Authenticated Encryption with
Associated Data") based on AES-128, as supported by nettle:
  <http://www.lysator.liu.se/~nisse/nettle/>

The one-shot @ocb_aes128_decrypt_message@ function in Nettle 4.0 is broken
(it passes the address of the decryption context instead of the context
itself, breaking messages of 16 bytes or larger); the streaming primitives
are used instead, which work on all supported Nettle versions.
-}
module Crypto.Nettle.OCB
    ( -- * OCB

    --
    -- 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.

      ocbAES128Encrypt
    , ocbAES128Decrypt
    ) 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.  @nonce@ must not be longer than 15 bytes.
The returned tag is 16 bytes long, but may be shortened for verification (losing security).
-}
ocbAES128Encrypt
    :: B.ByteString
    -- ^ @key@ (must be 16 bytes)
    -> B.ByteString
    -- ^ @nonce@ (must not be longer than 15 bytes)
    -> 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
ocbAES128Encrypt key nonce aad plain = unsafeDupablePerformIO $ do
    let k = copyAndConvertToScrubbedBytes key
    cipher <- B.create (B.length plain) (\_ -> return ())
    tag <- B.create 16 (\_ -> return ())
    _ <- withByteStringPtr plain $ \psize pptr ->
        withByteStringPtr aad $ \aadsize aadptr ->
            withByteStringPtr nonce $ \noncesize nonceptr ->
                withByteStringPtr cipher $ \_ cipherptr ->
                    withByteStringPtr tag $ \_ tagptr ->
                        withAlignedContext c_ocb_aes128_key_ctx_size $ \keyctxptr ->
                            withAlignedContext c_ocb_aes128_ctx_size $ \ctxptr ->
                                BA.withByteArray k $ \kptr ->
                                    if BA.length k /= 16
                                        then error "Invalid key length"
                                        else
                                            if noncesize > 15
                                                then error "Invalid nonce length"
                                                else do
                                                    c_ocb_aes128_set_encrypt_key keyctxptr kptr
                                                    c_ocb_aes128_set_nonce ctxptr keyctxptr 16 noncesize nonceptr
                                                    c_ocb_aes128_update ctxptr keyctxptr aadsize aadptr
                                                    c_ocb_aes128_encrypt ctxptr keyctxptr psize cipherptr pptr
                                                    callNettleOcbDigest
                                                        c_ocb_aes128_digest
                                                        16
                                                        ctxptr
                                                        keyctxptr
                                                        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.
-}
ocbAES128Decrypt
    :: B.ByteString
    -> B.ByteString
    -> B.ByteString
    -> B.ByteString
    -> B.ByteString
    -> Maybe B.ByteString
ocbAES128Decrypt key nonce aad cipher verifytag = unsafeDupablePerformIO $ do
    let k = copyAndConvertToScrubbedBytes key
    plain <- B.create (B.length cipher) (\_ -> return ())
    tag <- B.create 16 (\_ -> return ())
    _ <- withByteStringPtr cipher $ \psize pptr ->
        withByteStringPtr aad $ \aadsize aadptr ->
            withByteStringPtr nonce $ \noncesize nonceptr ->
                withByteStringPtr plain $ \_ plainptr ->
                    withByteStringPtr tag $ \_ tagptr ->
                        withAlignedContext c_ocb_aes128_key_ctx_size $ \keyctxptr ->
                            withAlignedContext c_aes128_ctx_size $ \decryptptr ->
                                withAlignedContext c_ocb_aes128_ctx_size $ \ctxptr ->
                                    BA.withByteArray k $ \kptr ->
                                        if BA.length k /= 16
                                            then error "Invalid key length"
                                            else
                                                if noncesize > 15
                                                    then error "Invalid nonce length"
                                                    else do
                                                        c_ocb_aes128_set_decrypt_key keyctxptr decryptptr kptr
                                                        c_ocb_aes128_set_nonce ctxptr keyctxptr 16 noncesize nonceptr
                                                        c_ocb_aes128_update ctxptr keyctxptr aadsize aadptr
                                                        c_ocb_aes128_decrypt
                                                            ctxptr
                                                            keyctxptr
                                                            decryptptr
                                                            psize
                                                            plainptr
                                                            pptr
                                                        callNettleOcbDigest
                                                            c_ocb_aes128_digest
                                                            16
                                                            ctxptr
                                                            keyctxptr
                                                            tagptr
    if B.take (B.length verifytag) tag == verifytag
        then return $ Just plain
        else return Nothing