packages feed

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

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

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

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

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

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

This module exports CMAC (Cipher-based Message Authentication Code,
NIST SP 800-38B) algorithms supported by nettle:
  <http://www.lysator.liu.se/~nisse/nettle/>
-}
module Crypto.Nettle.CMAC
    ( -- * CMAC algorithms
      CMAC_AES128
    , CMAC_AES256
    , CMAC_DES3
    , cmacInit
    , cmacInit'
    , cmac
    , cmac'
    ) 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 Crypto.Nettle.Hash.Types
import Nettle.Utils

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

class NettleCMAC a where
    cmac_ctx_size :: Tagged a Int
    cmac_digest_size :: Tagged a Int
    cmac_name :: Tagged a String
    cmac_set_key :: Tagged a (Ptr Word8 -> Ptr Word8 -> IO ())
    cmac_update
        :: Tagged a (Ptr Word8 -> Word -> Ptr Word8 -> IO ())
    cmac_digest :: Tagged a NettleHashDigest
    cmac_ctx :: a -> AlignedContext
    cmac_Ctx :: AlignedContext -> a

nettleCMACInit :: NettleCMAC a => B.ByteString -> a
nettleCMACInit key = untagSelf $ do
    size <- cmac_ctx_size
    setkey <- cmac_set_key
    return $
        cmac_Ctx $
            unsafeDupablePerformIO $
                alignedCtxCreate size $ \ctxptr ->
                    BA.withByteArray key $ \keyptr ->
                        setkey ctxptr keyptr
nettleCMACUpdate :: NettleCMAC a => a -> B.ByteString -> a
nettleCMACUpdate c msg = untagSelf $ do
    size <- cmac_ctx_size
    updatefun <- cmac_update
    return $
        cmac_Ctx $
            unsafeDupablePerformIO $
                alignedCtxCopy (cmac_ctx c) size $ \ctxptr ->
                    withByteStringPtr msg $ \msglen msgptr ->
                        updatefun ctxptr msglen msgptr
nettleCMACFinalize :: NettleCMAC a => a -> B.ByteString
nettleCMACFinalize c = flip witness c $ do
    let ctx = cmac_ctx c
    digestSize <- cmac_digest_size
    digestfun <- cmac_digest
    return $
        unsafeDupablePerformIO $
            B.create digestSize $ \digestptr ->
                BA.withByteArray (alignedCtxBuffer ctx) $ \ctxptr ->
                    callNettleHashDigest
                        digestfun
                        digestSize
                        (ctxptr `plusPtr` alignedCtxOffset ctx)
                        digestptr

#define INSTANCE_CMAC(Typ) \
instance NettleCMAC Typ => KeyedHashAlgorithm Typ where \
	{ implKeyedHashDigestSize = cmac_digest_size \
	; implKeyedHashName       = cmac_name \
	; implKeyedHashInit       = nettleCMACInit \
	; implKeyedHashUpdate     = nettleCMACUpdate \
	; implKeyedHashFinalize   = nettleCMACFinalize \
	}

-- | 'CMAC_AES128' is the CMAC algorithm based on AES-128 (NIST SP 800-38B), with a 16 byte (128 bit) key.
data CMAC_AES128 = CMAC_AES128 {cmac_aes128_ctx :: AlignedContext}

instance NettleCMAC CMAC_AES128 where
    cmac_ctx_size = Tagged c_cmac_aes128_ctx_size
    cmac_digest_size = Tagged 16
    cmac_name = Tagged "CMAC-AES128"
    cmac_set_key = Tagged c_cmac_aes128_set_key
    cmac_update = Tagged c_cmac_aes128_update
    cmac_digest = Tagged c_cmac_aes128_digest
    cmac_ctx = cmac_aes128_ctx
    cmac_Ctx = CMAC_AES128
INSTANCE_CMAC (CMAC_AES128)

-- | 'CMAC_AES256' is the CMAC algorithm based on AES-256 (NIST SP 800-38B), with a 32 byte (256 bit) key.
data CMAC_AES256 = CMAC_AES256 {cmac_aes256_ctx :: AlignedContext}

instance NettleCMAC CMAC_AES256 where
    cmac_ctx_size = Tagged c_cmac_aes256_ctx_size
    cmac_digest_size = Tagged 16
    cmac_name = Tagged "CMAC-AES256"
    cmac_set_key = Tagged c_cmac_aes256_set_key
    cmac_update = Tagged c_cmac_aes256_update
    cmac_digest = Tagged c_cmac_aes256_digest
    cmac_ctx = cmac_aes256_ctx
    cmac_Ctx = CMAC_AES256
INSTANCE_CMAC (CMAC_AES256)

-- | 'CMAC_DES3' is the CMAC algorithm based on 3DES (NIST SP 800-38B), with a 24 byte (192 bit) key.
data CMAC_DES3 = CMAC_DES3 {cmac_des3_ctx :: AlignedContext}

instance NettleCMAC CMAC_DES3 where
    cmac_ctx_size = Tagged c_cmac_des3_ctx_size
    cmac_digest_size = Tagged 8
    cmac_name = Tagged "CMAC-DES3"
    cmac_set_key = Tagged c_cmac_des3_set_key
    cmac_update = Tagged c_cmac_des3_update
    cmac_digest = Tagged c_cmac_des3_digest
    cmac_ctx = cmac_des3_ctx
    cmac_Ctx = CMAC_DES3
INSTANCE_CMAC (CMAC_DES3)

{- |
'cmacInit' initializes a 'KeyedHash' to calculate the CMAC for a message with the given @key@.
-}
cmacInit
    :: KeyedHashAlgorithm a
    => B.ByteString
    -- ^ @key@ argument
    -> Tagged a KeyedHash
cmacInit = keyedHashInit

{- |
Untagged variant of 'cmacInit'; takes a (possible 'undefined') typed 'CMAC' context as parameter.
-}
cmacInit'
    :: KeyedHashAlgorithm a => a -> B.ByteString -> KeyedHash
cmacInit' a key = cmacInit key `witness` a

{- |
Calculate the CMAC for a @key@ and @message@.
-}
cmac
    :: KeyedHashAlgorithm a
    => B.ByteString
    -- ^ @key@ argument
    -> B.ByteString
    -- ^ @message@ argument
    -> Tagged a B.ByteString
cmac = keyedHash

{- |
Untagged variant of 'cmac'; takes a (possible 'undefined') typed 'CMAC' context as parameter.
-}
cmac'
    :: KeyedHashAlgorithm a
    => a -> B.ByteString -> B.ByteString -> B.ByteString
cmac' a key msg = cmac key msg `witness` a