packages feed

mlkem-0.1.0.0: src/BlockN.hs

-- |
-- Module      : BlockN
-- License     : BSD-3-Clause
-- Copyright   : (c) 2025 Olivier Chéron
--
-- A secure block with length at type level
--
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
module BlockN
    ( BlockN, MutableBlockN, create, index, iterModify, BlockN.map
    , BlockN.new, BlockN.read, BlockN.thaw, BlockN.unsafeCast
    , BlockN.unsafeFreeze, BlockN.write, BlockN.zipWith
#ifdef ML_KEM_TESTING
    , BlockN.fromList, BlockN.replicate, BlockN.toList
#endif
    ) where

import Basement.Monad
import Basement.Nat
import Basement.NormalForm
import Basement.PrimType
import Basement.Types.OffsetSize

import Data.Proxy

import Block (MutableBlock, blockRead, blockWrite)
import Marking (Classified, SecurityMarking)
import SecureBlock (SecureBlock)
import qualified SecureBlock
import Math

newtype BlockN marking (n :: Nat) a = BlockN { unBlockN :: SecureBlock marking a }

#ifdef ML_KEM_TESTING
instance (Classified marking, PrimType a) => Eq (BlockN marking n a) where
    BlockN a == BlockN b = SecureBlock.eq a b

instance (Classified marking, PrimType a, Show a) => Show (BlockN marking n a) where
    showsPrec d = SecureBlock.showsPrec d . unBlockN
#endif

instance Classified marking => NormalForm (BlockN marking n a) where
    toNormalForm = SecureBlock.toNormalForm . unBlockN

instance (Classified marking, KnownNat n, PrimType a, Add a) => Add (BlockN marking n a) where
    zero = create (const zero)
    {-# INLINE zero #-}
    (.+) = BlockN.zipWith (.+)
    {-# INLINE (.+) #-}
    (.-) = BlockN.zipWith (.-)
    {-# INLINE (.-) #-}
    neg = BlockN.map neg
    {-# INLINE neg #-}

newtype MutableBlockN (marking :: SecurityMarking) (n :: Nat) a m = MutableBlockN { unMutableBlockN :: MutableBlock a m }

index :: (Classified marking, PrimType a) => BlockN marking n a -> Offset a -> a
index = SecureBlock.index . unBlockN

#ifdef ML_KEM_TESTING
replicate :: forall marking n a. (Classified marking, KnownNat n, PrimType a) => a -> BlockN marking n a
replicate = BlockN . SecureBlock.replicate sz
  where !sz = fromIntegral $ natVal (Proxy :: Proxy n)

fromList :: forall marking n a. (Classified marking, KnownNat n, PrimType a) => [a] -> Maybe (BlockN marking n a)
fromList elems
    | SecureBlock.length a == CountOf sz = Just (BlockN a)
    | otherwise = Nothing
  where
    a = SecureBlock.fromList elems
    !sz = fromIntegral $ natVal (Proxy :: Proxy n)

toList :: (Classified marking, PrimType a) => BlockN marking n a -> [a]
toList = SecureBlock.toList . unBlockN
#endif

create :: forall marking n ty. (Classified marking, KnownNat n, PrimType ty)
       => (Offset ty -> ty)
       -> BlockN marking n ty
create initializer = BlockN $ SecureBlock.create (CountOf sz) initializer
  where !sz = fromIntegral $ natVal (Proxy :: Proxy n)
{-# INLINE create #-}

map :: (Classified marking, PrimType a, PrimType b) => (a -> b) -> BlockN marking n a -> BlockN marking n b
map f = BlockN . SecureBlock.map f . unBlockN
{-# INLINE map #-}

iterModify :: (PrimType ty, PrimMonad prim)
           => (ty -> ty)
           -> MutableBlockN marking n ty (PrimState prim)
           -> prim ()
iterModify f = SecureBlock.iterModify f . unMutableBlockN
{-# INLINE iterModify #-}

zipWith :: (Classified ma, Classified mb, Classified mc, KnownNat n, PrimType a, PrimType b, PrimType c)
        => (a -> b -> c) -> BlockN ma n a -> BlockN mb n b -> BlockN mc n c
zipWith f (BlockN !a) (BlockN !b) =
    create $ \(Offset i) ->
        f (SecureBlock.index a (Offset i)) (SecureBlock.index b (Offset i))
{-# INLINE zipWith #-}

unsafeCast :: (Classified marking, PrimType b) => BlockN marking n a -> SecureBlock marking b
unsafeCast = SecureBlock.unsafeCast . unBlockN

read :: (PrimMonad prim, PrimType a) => MutableBlockN marking n a (PrimState prim) -> Offset a -> prim a
read = blockRead . unMutableBlockN

write :: (PrimMonad prim, PrimType a) => MutableBlockN marking n a (PrimState prim) -> Offset a -> a -> prim ()
write = blockWrite . unMutableBlockN

new :: forall proxy marking n a prim. (Classified marking, KnownNat n, PrimMonad prim, PrimType a) => proxy marking -> prim (MutableBlockN marking n a (PrimState prim))
new prx = MutableBlockN <$> SecureBlock.new prx (CountOf sz)
  where !sz = fromIntegral $ natVal (Proxy :: Proxy n)

thaw :: (Classified marking, PrimMonad prim, PrimType a) => BlockN marking n a -> prim (MutableBlockN marking n a (PrimState prim))
thaw = fmap MutableBlockN . SecureBlock.thaw . unBlockN

unsafeFreeze :: (Classified marking, PrimMonad prim) => MutableBlockN marking n a (PrimState prim) -> prim (BlockN marking n a)
unsafeFreeze = fmap BlockN . SecureBlock.unsafeFreeze . unMutableBlockN