crypton-2.1.8: Crypto/PubKey/MLDSA.hs
-- |
-- Module : Crypto.PubKey.MLDSA
-- License : BSD-style
-- Maintainer : Kazu Yamamoto <kazu@iij.ad.jp>
-- Stability : experimental
-- Portability : unknown
--
-- ML-DSA, the Module-Lattice-Based Digital Signature Algorithm of
-- <https://csrc.nist.gov/pubs/fips/204/final FIPS 204>, in all three
-- parameter sets.
--
-- > (vk, sk) <- generateKeyPair MLDSA65
-- > sig <- sign sk emptyContext message
-- > verify vk emptyContext message sig
--
-- What 'generateKeyPair' and 'sign' draw their randomness from is the
-- 'Crypto.Random.MonadRandom' instance in use. Its documentation says what
-- an instance of your own has to be.
--
-- The parameter set is a type, so an ML-DSA-65 key cannot be passed where
-- an ML-DSA-87 one is expected. The three are fixed by FIPS 204 and the
-- class has no other instances.
--
-- This is pure ML-DSA: the message goes in whole. The pre-hash variant
-- (HashML-DSA) is a different algorithm with a different domain separator
-- and is not offered here.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Crypto.PubKey.MLDSA (
-- * Parameter sets
MLDSA44 (..),
MLDSA65 (..),
MLDSA87 (..),
MLDSA (verificationKeySize, signingKeySize, signatureSize),
-- * Keys and signatures
VerificationKey,
SigningKey,
Signature,
-- * Smart constructors
verificationKey,
signingKey,
signature,
-- * Generating a key pair
generateKeyPair,
generateKeyPairAndSeed,
keyPairFromSeed,
toPublic,
-- * The context string
Context,
context,
emptyContext,
-- * The message representative
Mu,
mu,
messageRepresentative,
-- ** A message that does not arrive in one piece
MuContext,
muInit,
muUpdate,
muUpdates,
muFinalize,
-- * Signing and verifying
sign,
signWith,
signDeterministic,
verify,
-- * Signing and verifying a message representative
signExternalMu,
signExternalMuWith,
signExternalMuDeterministic,
verifyExternalMu,
-- * Sizes
seedSize,
signingRandomnessSize,
maxContextLength,
muSize,
) where
import Data.Proxy (Proxy (..))
import Foreign.C.Types (CInt (..), CSize (..))
import Foreign.Ptr (Ptr, nullPtr)
import Crypto.Debug (DebugShow (..), debugShowBytes)
import Crypto.Hash (Digest, hash, hashFinalize, hashInit, hashUpdate, hashUpdates)
import qualified Crypto.Hash as Hash (Context)
import Crypto.Hash.Algorithms (SHAKE256 (..))
import Crypto.Error
import Crypto.Internal.ByteArray (
ByteArrayAccess,
Bytes,
ScrubbedBytes,
withByteArray,
)
import qualified Crypto.Internal.ByteArray as B
import Crypto.Internal.Compat (unsafeDoIO)
import Crypto.Internal.Imports
import Crypto.Random (MonadRandom, getRandomBytes)
-- | ML-DSA-44.
data MLDSA44 = MLDSA44 deriving (Show, Eq)
-- | ML-DSA-65.
data MLDSA65 = MLDSA65 deriving (Show, Eq)
-- | ML-DSA-87.
data MLDSA87 = MLDSA87 deriving (Show, Eq)
-- | The three parameter sets of FIPS 204.
--
-- Named for the algorithm rather than \"DSA\", which is a different one that
-- crypton also has, in "Crypto.PubKey.DSA". It is the three sets FIPS 204
-- defines, closed, carrying their sizes and the calls into the
-- implementation; only the sizes are exported.
class MLDSA p where
-- | Size in bytes of a 'VerificationKey' of this parameter set.
verificationKeySize :: proxy p -> Int
-- | Size in bytes of a 'SigningKey' of this parameter set.
signingKeySize :: proxy p -> Int
-- | Size in bytes of a 'Signature' of this parameter set.
signatureSize :: proxy p -> Int
c_keypair :: proxy p -> Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO CInt
c_sign
:: proxy p
-> Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> Ptr Word8 -> CInt -> IO CInt
c_verify
:: proxy p
-> Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> CInt -> IO CInt
c_pkFromSk :: proxy p -> Ptr Word8 -> Ptr Word8 -> IO CInt
-- | A public verification key.
newtype VerificationKey p = VerificationKey Bytes
deriving (Show, Eq, ByteArrayAccess, NFData)
-- | A private signing key.
newtype SigningKey p = SigningKey ScrubbedBytes
deriving (Eq, ByteArrayAccess, NFData)
instance Show (SigningKey p) where
show _ = "SigningKey <redacted>"
instance DebugShow (SigningKey p) where
debugShow = debugShowBytes "SigningKey"
-- | A signature.
newtype Signature p = Signature Bytes
deriving (Show, Eq, ByteArrayAccess, NFData)
-- | The context string a signature is bound to, at most
-- 'maxContextLength' bytes.
--
-- FIPS 204 mixes it into what is signed, so a signature made under one
-- context does not verify under another. Two uses of one key that don't
-- agree on a context string cannot be made to accept each other's
-- signatures. Use 'emptyContext' where there is nothing to separate -- TLS,
-- for one, signs with an empty context.
newtype Context = Context Bytes
deriving (Show, Eq, ByteArrayAccess, NFData)
-- | The context string of length zero, which is what to sign under when
-- there is nothing to separate.
--
-- It is a context and not the absence of one: 'sign' always takes one, and
-- what this separates from is every non-empty context there is.
emptyContext :: Context
emptyContext = Context B.empty
-- | Try to build a context string.
context :: ByteArrayAccess ba => ba -> CryptoFailable Context
context bs
| B.length bs <= maxContextLength =
CryptoPassed $ Context $ B.copyAndFreeze bs (\_ -> return ())
| otherwise = CryptoFailed CryptoError_ParameterInvalid
-- | The longest context string FIPS 204 allows, which is 255 bytes because
-- its length is encoded in one byte.
maxContextLength :: Int
maxContextLength = 255
-- | The message representative, @mu@ in FIPS 204: a 64-byte commitment to
-- the verification key, the context string and the message, and the only
-- part of them that signing and verification actually read.
--
-- Signing it directly is the "external mu" interface. It is for a caller
-- that has the representative without having the message in one piece: a
-- message arriving as a stream, or hashed on another machine, or by a
-- device that holds the key and is handed only this. TLS does not need it.
newtype Mu = Mu Bytes
deriving (Show, Eq, ByteArrayAccess, NFData)
-- | Size in bytes of a 'Mu'.
muSize :: Int
muSize = 64
-- | Try to read a message representative.
mu :: ByteArrayAccess ba => ba -> CryptoFailable Mu
mu bs
| B.length bs == muSize = CryptoPassed $ Mu $ B.copyAndFreeze bs (\_ -> return ())
| otherwise = CryptoFailed CryptoError_ParameterInvalid
-- | Compute the message representative, for a caller that wants to make it
-- here and sign it later, or sign it elsewhere.
--
-- @'signExternalMuDeterministic' sk ('messageRepresentative' ('toPublic' sk) ctx msg)@
-- and @'signDeterministic' sk ctx msg@ are the same signature.
messageRepresentative
:: (MLDSA p, ByteArrayAccess msg)
=> VerificationKey p -> Context -> msg -> Mu
messageRepresentative vk ctx msg = muFinalize (muUpdate (muInit vk ctx) msg)
-- | A 'Mu' being computed, with the message going in a piece at a time.
--
-- The name is not 'Context': that is ML-DSA's context string, which this
-- is built from and is not.
newtype MuContext = MuContext (Hash.Context (SHAKE256 512))
-- | Begin a message representative. The key and the context string are
-- what it is bound to, and they are all that is needed before the message.
--
-- > muFinalize (muUpdates (muInit vk ctx) chunks)
--
-- is 'messageRepresentative' of the chunks joined, so a message too large
-- to hold at once never has to be.
muInit :: MLDSA p => VerificationKey p -> Context -> MuContext
muInit vk ctx =
-- FIPS 204: tr <- H(pk, 64) at key generation, and mu <- H(tr || M', 64)
-- when signing, with M' the domain-separated message. Everything up to
-- the message itself is absorbed here.
MuContext $ hashUpdates hashInit [tr, domainPrefix ctx]
where
tr = B.convert (shake64 (B.convert vk :: Bytes)) :: Bytes
-- | Absorb a piece of the message.
muUpdate :: ByteArrayAccess msg => MuContext -> msg -> MuContext
muUpdate (MuContext c) msg = MuContext (hashUpdate c msg)
-- | Absorb several pieces, which is 'muUpdate' one after the other.
muUpdates :: ByteArrayAccess msg => MuContext -> [msg] -> MuContext
muUpdates (MuContext c) msgs = MuContext (hashUpdates c msgs)
-- | The message representative of everything absorbed so far.
muFinalize :: MuContext -> Mu
muFinalize (MuContext c) = Mu (B.convert (hashFinalize c :: Digest (SHAKE256 512)))
shake64 :: ByteArrayAccess ba => ba -> Digest (SHAKE256 512)
shake64 = hash
-- | Size in bytes of the seed 'keyPairFromSeed' takes, @xi@ in FIPS 204.
seedSize :: Int
seedSize = 32
-- | Size in bytes of the randomness 'signWith' takes.
signingRandomnessSize :: Int
signingRandomnessSize = 32
-- | Try to read a verification key. Only the length is checked: a
-- verification key is a packed encoding with no redundancy to test, and one
-- that is not a real key simply verifies nothing.
verificationKey
:: forall p ba
. (MLDSA p, ByteArrayAccess ba)
=> ba -> CryptoFailable (VerificationKey p)
verificationKey bs
| B.length bs == verificationKeySize (Proxy :: Proxy p) =
CryptoPassed $ VerificationKey $ B.copyAndFreeze bs (\_ -> return ())
| otherwise = CryptoFailed CryptoError_PublicKeySizeInvalid
-- | Try to read a signing key.
--
-- Beyond the length this runs the validity checks of the implementation:
-- the secret polynomials must have coefficients in range, and the
-- commitment and the public-key hash the key carries must match what is
-- recomputed from the rest of it. A key that fails has been damaged or was
-- never a key, and signing with it would produce signatures nothing
-- verifies.
signingKey
:: forall p ba
. (MLDSA p, ByteArrayAccess ba)
=> ba -> CryptoFailable (SigningKey p)
signingKey bs
| B.length bs /= signingKeySize p = CryptoFailed CryptoError_SecretKeySizeInvalid
| otherwise = unsafeDoIO $ do
(r, _ :: Bytes) <- B.allocRet (verificationKeySize p) $ \ppk ->
withByteArray bs $ \psk -> c_pkFromSk p ppk psk
return $
if r == 0
then CryptoPassed $ SigningKey $ B.copyAndFreeze bs (\_ -> return ())
else CryptoFailed CryptoError_SecretKeyStructureInvalid
where
p = Proxy :: Proxy p
{-# NOINLINE signingKey #-}
-- | Try to read a signature. Only the length is checked; whether it is a
-- signature of anything is what 'verify' answers.
signature
:: forall p ba
. (MLDSA p, ByteArrayAccess ba)
=> ba -> CryptoFailable (Signature p)
signature bs
| B.length bs == signatureSize (Proxy :: Proxy p) =
CryptoPassed $ Signature $ B.copyAndFreeze bs (\_ -> return ())
| otherwise = CryptoFailed CryptoError_ParameterInvalid
-- | Recover the verification key a signing key was made with.
toPublic :: forall p. MLDSA p => SigningKey p -> VerificationKey p
toPublic sk = VerificationKey $ unsafeDoIO $ do
(_ :: CInt, pk) <- B.allocRet (verificationKeySize p) $ \ppk ->
withByteArray sk $ \psk -> c_pkFromSk p ppk psk
return pk
where
p = Proxy :: Proxy p
{-# NOINLINE toPublic #-}
-- | Generate a key pair.
--
-- The seed it is derived from is drawn here and thrown away. Use
-- 'generateKeyPairAndSeed' where it has to be kept.
generateKeyPair
:: forall p proxy m
. (MLDSA p, MonadRandom m)
=> proxy p -> m (VerificationKey p, SigningKey p)
generateKeyPair p = do
(vk, sk, _) <- generateKeyPairAndSeed p
return (vk, sk)
-- | Generate a key pair and hand back the seed it was derived from, @xi@
-- in FIPS 204.
--
-- A 'SigningKey' is the expanded key and nothing else, so the seed cannot
-- be recovered from a pair afterwards. An application that has to write
-- the key out in a form that keeps the seed -- RFC 9881 lets an ML-DSA
-- private key be the seed, the expanded key, or both -- has to generate it
-- here:
--
-- > (vk, sk, seed) <- generateKeyPairAndSeed MLDSA65
--
-- The seed is as secret as the signing key: 'keyPairFromSeed' turns it
-- back into the same pair.
generateKeyPairAndSeed
:: forall p proxy m
. (MLDSA p, MonadRandom m)
=> proxy p -> m (VerificationKey p, SigningKey p, ScrubbedBytes)
generateKeyPairAndSeed p = do
seed <- getRandomBytes seedSize :: m ScrubbedBytes
case keyPairFromSeed p seed of
CryptoPassed (vk, sk) -> return (vk, sk, seed)
CryptoFailed e ->
error ("Crypto.PubKey.MLDSA.generateKeyPairAndSeed: " ++ show e)
-- | Derive a key pair from a seed, @xi@ in FIPS 204, which must be
-- 'seedSize' bytes.
keyPairFromSeed
:: forall p proxy ba
. (MLDSA p, ByteArrayAccess ba)
=> proxy p -> ba -> CryptoFailable (VerificationKey p, SigningKey p)
keyPairFromSeed p seed
| B.length seed /= seedSize = CryptoFailed CryptoError_SeedSizeInvalid
| otherwise = unsafeDoIO $ do
-- Not zeroed, and does not need to be: the C writes the whole
-- buffer, and on a non-zero return the result is discarded without
-- being read. Anything that is *read* before being written has to
-- use B.zero instead -- see signInternal in Crypto.PubKey.MLDSA.
sk <- B.alloc (signingKeySize p) (\_ -> return ()) :: IO ScrubbedBytes
(r, vk) <- B.allocRet (verificationKeySize p) $ \pvk ->
withByteArray sk $ \psk ->
withByteArray seed $ \pseed ->
c_keypair p pvk psk pseed
return $
if r == 0
then CryptoPassed (VerificationKey vk, SigningKey sk)
else CryptoFailed CryptoError_ParameterInvalid
{-# NOINLINE keyPairFromSeed #-}
-- | Sign a message.
--
-- This is the hedged signing FIPS 204 recommends: fresh randomness goes in
-- alongside the key and the message, so two signatures of one message
-- differ and a fault in one reveals less. Verification does not care which
-- of the three entry points made the signature.
sign
:: forall p m msg
. (MLDSA p, MonadRandom m, ByteArrayAccess msg)
=> SigningKey p -> Context -> msg -> m (Signature p)
sign sk ctx msg = do
rnd <- getRandomBytes signingRandomnessSize :: m ScrubbedBytes
case signWith sk ctx msg rnd of
CryptoPassed s -> return s
CryptoFailed e -> error ("Crypto.PubKey.MLDSA.sign: " ++ show e)
-- | Sign with the randomness supplied, which must be
-- 'signingRandomnessSize' bytes.
--
-- For test vectors, and for callers who draw their own randomness. Ordinary
-- use wants 'sign'.
signWith
:: forall p msg rnd
. (MLDSA p, ByteArrayAccess msg, ByteArrayAccess rnd)
=> SigningKey p -> Context -> msg -> rnd -> CryptoFailable (Signature p)
signWith sk ctx msg rnd
| B.length rnd /= signingRandomnessSize = CryptoFailed CryptoError_SeedSizeInvalid
| otherwise = signInternal sk ctx msg (Just rnd)
-- | Sign deterministically, as FIPS 204 section 3.4 allows: the randomness
-- is replaced by zeroes, so one key and one message always give one
-- signature.
--
-- This is what test vectors are written against, and what to use where the
-- signature must be reproducible. It gives up what hedging buys, so where
-- there is a usable random source 'sign' is the better default.
signDeterministic
:: forall p msg
. (MLDSA p, ByteArrayAccess msg)
=> SigningKey p -> Context -> msg -> Signature p
signDeterministic sk ctx msg =
case signInternal sk ctx msg (Nothing :: Maybe Bytes) of
CryptoPassed s -> s
CryptoFailed e -> error ("Crypto.PubKey.MLDSA.signDeterministic: " ++ show e)
signInternal
:: forall p msg rnd
. (MLDSA p, ByteArrayAccess msg, ByteArrayAccess rnd)
=> SigningKey p -> Context -> msg -> Maybe rnd -> CryptoFailable (Signature p)
signInternal sk ctx msg mrnd = unsafeDoIO $ do
-- B.zero, not B.alloc with an empty action: alloc hands back whatever
-- was in the memory. That made signDeterministic sign with the last
-- caller's bytes and produce a different signature every time, which the
-- ACVP vectors caught only once the whole suite ran and the allocator
-- stopped handing out fresh zeroed pages.
let zeroes = B.zero signingRandomnessSize :: ScrubbedBytes
withRnd f = case mrnd of
Just r -> withByteArray r f
Nothing -> withByteArray zeroes f
(r, sig) <- B.allocRet (signatureSize p) $ \psig ->
withByteArray msg $ \pmsg ->
withByteArray pre $ \ppre ->
withRnd $ \prnd ->
withByteArray sk $ \psk ->
c_sign
p
psig
pmsg
(fromIntegral (B.length msg))
ppre
(fromIntegral (B.length pre))
prnd
psk
0
return $
if r == 0
then CryptoPassed (Signature sig)
else CryptoFailed CryptoError_ParameterInvalid
where
p = Proxy :: Proxy p
pre = domainPrefix ctx
{-# NOINLINE signInternal #-}
-- | Sign a message representative, drawing the randomness.
--
-- The context string is already inside the representative, which is why
-- this does not take one.
signExternalMu
:: forall p m
. (MLDSA p, MonadRandom m)
=> SigningKey p -> Mu -> m (Signature p)
signExternalMu sk m = do
rnd <- getRandomBytes signingRandomnessSize :: m ScrubbedBytes
case signExternalMuWith sk m rnd of
CryptoPassed s -> return s
CryptoFailed e -> error ("Crypto.PubKey.MLDSA.signExternalMu: " ++ show e)
-- | Sign a message representative with the randomness supplied.
signExternalMuWith
:: (MLDSA p, ByteArrayAccess rnd)
=> SigningKey p -> Mu -> rnd -> CryptoFailable (Signature p)
signExternalMuWith sk m rnd
| B.length rnd /= signingRandomnessSize = CryptoFailed CryptoError_SeedSizeInvalid
| otherwise = signMu sk m (Just rnd)
-- | Sign a message representative deterministically.
signExternalMuDeterministic
:: MLDSA p => SigningKey p -> Mu -> Signature p
signExternalMuDeterministic sk m =
case signMu sk m (Nothing :: Maybe Bytes) of
CryptoPassed s -> s
CryptoFailed e ->
error ("Crypto.PubKey.MLDSA.signExternalMuDeterministic: " ++ show e)
-- | Verify a signature of a message representative.
verifyExternalMu
:: forall p. MLDSA p => VerificationKey p -> Mu -> Signature p -> Bool
verifyExternalMu vk m sig
| B.length sig /= signatureSize p = False
| otherwise = unsafeDoIO $
withByteArray sig $ \psig ->
withByteArray m $ \pmu ->
withByteArray vk $ \pvk -> do
r <-
c_verify
p
psig
pmu
(fromIntegral muSize)
nullPtr
0
pvk
1
return (r == 0)
where
p = Proxy :: Proxy p
{-# NOINLINE verifyExternalMu #-}
-- The external-mu entry points are the ordinary ones with the last argument
-- set: the representative goes in where the message would, there is no
-- domain separation prefix to prepend because it is already inside, and the
-- implementation is told so.
signMu
:: forall p rnd
. (MLDSA p, ByteArrayAccess rnd)
=> SigningKey p -> Mu -> Maybe rnd -> CryptoFailable (Signature p)
signMu sk m mrnd = unsafeDoIO $ do
let zeroes = B.zero signingRandomnessSize :: ScrubbedBytes
withRnd f = case mrnd of
Just r -> withByteArray r f
Nothing -> withByteArray zeroes f
(r, sig) <- B.allocRet (signatureSize p) $ \psig ->
withByteArray m $ \pmu ->
withRnd $ \prnd ->
withByteArray sk $ \psk ->
c_sign p psig pmu (fromIntegral muSize) nullPtr 0 prnd psk 1
return $
if r == 0
then CryptoPassed (Signature sig)
else CryptoFailed CryptoError_ParameterInvalid
where
p = Proxy :: Proxy p
{-# NOINLINE signMu #-}
-- | Verify a signature.
--
-- The context must be the one it was signed under; anything else is a
-- rejection, which is what the context is for.
verify
:: forall p msg
. (MLDSA p, ByteArrayAccess msg)
=> VerificationKey p -> Context -> msg -> Signature p -> Bool
verify vk ctx msg sig
| B.length sig /= signatureSize p = False
| otherwise = unsafeDoIO $
withByteArray sig $ \psig ->
withByteArray msg $ \pmsg ->
withByteArray pre $ \ppre ->
withByteArray vk $ \pvk -> do
r <-
c_verify
p
psig
pmsg
(fromIntegral (B.length msg))
ppre
(fromIntegral (B.length pre))
pvk
0
return (r == 0)
where
p = Proxy :: Proxy p
pre = domainPrefix ctx
{-# NOINLINE verify #-}
-- | The domain separation prefix of FIPS 204 for pure ML-DSA, which is a
-- zero byte, the context's length and the context itself. It is built here
-- rather than taken from the implementation because it is three bytes of
-- concatenation and doing it here keeps one fewer foreign call.
domainPrefix :: Context -> Bytes
domainPrefix (Context ctx) =
B.concat [B.pack [0, fromIntegral (B.length ctx)] :: Bytes, B.convert ctx]
instance MLDSA MLDSA44 where
verificationKeySize _ = 1312
signingKeySize _ = 2560
signatureSize _ = 2420
c_keypair _ = c_mldsa44_keypair
c_sign _ = c_mldsa44_sign
c_verify _ = c_mldsa44_verify
c_pkFromSk _ = c_mldsa44_pk_from_sk
instance MLDSA MLDSA65 where
verificationKeySize _ = 1952
signingKeySize _ = 4032
signatureSize _ = 3309
c_keypair _ = c_mldsa65_keypair
c_sign _ = c_mldsa65_sign
c_verify _ = c_mldsa65_verify
c_pkFromSk _ = c_mldsa65_pk_from_sk
instance MLDSA MLDSA87 where
verificationKeySize _ = 2592
signingKeySize _ = 4896
signatureSize _ = 4627
c_keypair _ = c_mldsa87_keypair
c_sign _ = c_mldsa87_sign
c_verify _ = c_mldsa87_verify
c_pkFromSk _ = c_mldsa87_pk_from_sk
foreign import ccall unsafe "crypton_mldsa44_keypair_internal"
c_mldsa44_keypair :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO CInt
foreign import ccall unsafe "crypton_mldsa44_signature_internal"
c_mldsa44_sign
:: Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> Ptr Word8 -> CInt -> IO CInt
foreign import ccall unsafe "crypton_mldsa44_verify_internal"
c_mldsa44_verify
:: Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> CInt -> IO CInt
foreign import ccall unsafe "crypton_mldsa44_pk_from_sk"
c_mldsa44_pk_from_sk :: Ptr Word8 -> Ptr Word8 -> IO CInt
foreign import ccall unsafe "crypton_mldsa65_keypair_internal"
c_mldsa65_keypair :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO CInt
foreign import ccall unsafe "crypton_mldsa65_signature_internal"
c_mldsa65_sign
:: Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> Ptr Word8 -> CInt -> IO CInt
foreign import ccall unsafe "crypton_mldsa65_verify_internal"
c_mldsa65_verify
:: Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> CInt -> IO CInt
foreign import ccall unsafe "crypton_mldsa65_pk_from_sk"
c_mldsa65_pk_from_sk :: Ptr Word8 -> Ptr Word8 -> IO CInt
foreign import ccall unsafe "crypton_mldsa87_keypair_internal"
c_mldsa87_keypair :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO CInt
foreign import ccall unsafe "crypton_mldsa87_signature_internal"
c_mldsa87_sign
:: Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> Ptr Word8 -> CInt -> IO CInt
foreign import ccall unsafe "crypton_mldsa87_verify_internal"
c_mldsa87_verify
:: Ptr Word8 -> Ptr Word8 -> CSize -> Ptr Word8 -> CSize
-> Ptr Word8 -> CInt -> IO CInt
foreign import ccall unsafe "crypton_mldsa87_pk_from_sk"
c_mldsa87_pk_from_sk :: Ptr Word8 -> Ptr Word8 -> IO CInt