packages feed

nettle-0.4: src/Crypto/Nettle/XOF.hsc

{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

#include <nettle/version.h>

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

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

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

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

This module exports extendable-output functions (XOF) supported by nettle:
  <http://www.lysator.liu.se/~nisse/nettle/>
-}
module Crypto.Nettle.XOF
    ( -- * XOF class
      XOF (..)
    , shake
    , shake'

      -- * XOF algorithms
#if (NETTLE_VERSION_MAJOR > 3 || (NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 10))
    , SHAKE128
#endif
    , SHAKE256
    ) where

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

import Crypto.Nettle.Hash.ForeignImports
import Nettle.Utils

-- internal functions are not camelCase on purpose
{-# ANN module "HLint: ignore Use camelCase" #-}

{- |
'XOF' is a class for extendable-output functions: like a hash, they absorb a
message, but they can produce an arbitrary number of output bytes.
-}
class XOF a where
    -- | Name of the XOF algorithm
    xofName :: Tagged a String

    -- | Initialize a new context for this XOF algorithm
    xofInit :: a

    -- | Update the context with a bytestring, and return a new context with the updates.
    xofUpdate :: a -> B.ByteString -> a

    -- | Finalize a context and return @outlen@ bytes of output.
    xofFinalize :: a -> Int -> B.ByteString

nettleXOFName :: NettleXOF a => Tagged a String
nettleXOFName = xof_name
nettleXOFInit :: NettleXOF a => a
nettleXOFInit = untagSelf $ do
    size <- xof_ctx_size
    initfun <- xof_init
    return $ xof_Ctx $ BA.unsafeCreate size $ \ctxptr ->
        initfun ctxptr
nettleXOFUpdate :: NettleXOF a => a -> B.ByteString -> a
nettleXOFUpdate c msg = untagSelf $ do
    updatefun <- xof_update
    return $ xof_Ctx $ BA.copyAndFreeze (xof_ctx c) $ \ctxptr ->
        withByteStringPtr msg $ \msglen msgptr ->
            updatefun ctxptr msglen msgptr
nettleXOFFinalize :: NettleXOF a => a -> Int -> B.ByteString
nettleXOFFinalize c outlen = flip witness c $ do
    let ctx = copyScrubbedBytes (xof_ctx c)
    shakefun <- xof_shake
    return $
        unsafeDupablePerformIO $
            B.create outlen $ \outptr ->
                BA.withByteArray ctx $ \ctxptr ->
                    shakefun ctxptr (fromIntegral outlen) outptr

class NettleXOF a where
    xof_ctx_size :: Tagged a Int
    xof_name :: Tagged a String
    xof_init :: Tagged a NettleHashInit
    xof_update :: Tagged a NettleHashUpdate
    xof_shake
        :: Tagged a (Ptr Word8 -> Word -> Ptr Word8 -> IO ())
    xof_ctx :: a -> BA.ScrubbedBytes
    xof_Ctx :: BA.ScrubbedBytes -> a

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

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

{- |
Helper to run an XOF over a single (strict) 'B.ByteString', producing @outlen@ bytes.

Example:

> untag (shake (fromString "abc") 64 :: Tagged SHAKE256 B.ByteString)
-}
shake
    :: XOF a
    => B.ByteString
    -- ^ @msg@ argument
    -> Int
    -- ^ @outlen@ argument
    -> Tagged a B.ByteString
shake msg outlen =
    (flip xofFinalize outlen)
        <$> (flip xofUpdate msg <$> tagSelf xofInit)

{- |
Untagged variant of 'shake'; takes a (possible 'undefined') typed 'XOF' context as parameter.

Example:

> shake' (undefined :: SHAKE256) (fromString "abc") 64
-}
shake' :: XOF a => a -> B.ByteString -> Int -> B.ByteString
shake' a msg outlen = shake msg outlen `witness` a

#if (NETTLE_VERSION_MAJOR > 3 || (NETTLE_VERSION_MAJOR == 3 && NETTLE_VERSION_MINOR >= 10))
{- | 'SHAKE128' is an extendable-output function based on the Keccak permutation.
  It produces arbitrary-length output with a security strength of 128 bits.
  It is only available with Nettle >= 3.10.
-}
data SHAKE128 = SHAKE128 {shake128_ctx :: BA.ScrubbedBytes}

instance NettleXOF SHAKE128 where
    xof_ctx_size = Tagged c_sha3_128_ctx_size
    xof_name = Tagged "SHAKE128"
    xof_init = Tagged c_sha3_128_init
    xof_update = Tagged c_sha3_128_update
    xof_shake = Tagged c_sha3_128_shake
    xof_ctx = shake128_ctx
    xof_Ctx = SHAKE128

instance XOF SHAKE128 where
    xofName     = nettleXOFName
    xofInit     = nettleXOFInit
    xofUpdate   = nettleXOFUpdate
    xofFinalize = nettleXOFFinalize
#endif

{- | 'SHAKE256' is an extendable-output function based on the Keccak permutation.
  It produces arbitrary-length output with a security strength of 256 bits.
-}
data SHAKE256 = SHAKE256 {shake256_ctx :: BA.ScrubbedBytes}

instance NettleXOF SHAKE256 where
    xof_ctx_size = Tagged c_sha3_256_ctx_size
    xof_name = Tagged "SHAKE256"
    xof_init = Tagged c_sha3_256_init
    xof_update = Tagged c_sha3_256_update
    xof_shake = Tagged c_sha3_256_shake
    xof_ctx = shake256_ctx
    xof_Ctx = SHAKE256

instance XOF SHAKE256 where
    xofName     = nettleXOFName
    xofInit     = nettleXOFInit
    xofUpdate   = nettleXOFUpdate
    xofFinalize = nettleXOFFinalize