sntrup761 (#865)

* add sntrup761 source

* it compiles

* Wrap bindings in non-FFI types

Test passes with a dummy RNG.

* pass ChaChaDRG via FunPtr

* Add iOS smoke test at createAgentStore

* style

* add "ssl" library dep

Attempt to fix missing _SHA512 symbol on macos.

* remove sha512 wrapper and use openssl directly

* restore names, remove dummy RNG

* Revert "remove sha512 wrapper and use openssl directly"

This reverts commit f9f7781f09.

* restore code from RFC

* shorter names

* enable all tests

* remove run test

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
This commit is contained in:
Evgeny Poberezkin
2023-10-31 22:40:15 +00:00
parent db1b2f77cd
commit c22c15a2e8
12 changed files with 1239 additions and 0 deletions
@@ -0,0 +1,38 @@
{-# LANGUAGE NamedFieldPuns #-}
module Simplex.Messaging.Crypto.SNTRUP761.Bindings where
import Data.ByteArray (ScrubbedBytes)
import qualified Data.ByteArray as BA
import Data.ByteString (ByteString)
import Simplex.Messaging.Crypto.SNTRUP761.Bindings.Defines
import Simplex.Messaging.Crypto.SNTRUP761.Bindings.FFI
import Simplex.Messaging.Crypto.SNTRUP761.Bindings.RNG (RNG (..))
type PublicKey = ByteString
type SecretKey = ScrubbedBytes
type Ciphertext = ByteString
type Key = ScrubbedBytes
sntrup761Keypair :: RNG -> IO (PublicKey, SecretKey)
sntrup761Keypair RNG {rngContext, rngFunc} = do
BA.allocRet c_SNTRUP761_SECRETKEY_SIZE $ \skPtr ->
BA.alloc c_SNTRUP761_PUBLICKEY_SIZE $ \pkPtr ->
c_sntrup761_keypair pkPtr skPtr rngContext rngFunc
sntrup761Enc :: RNG -> PublicKey -> IO (Ciphertext, Key)
sntrup761Enc RNG {rngContext, rngFunc} pk =
BA.withByteArray pk $ \pkPtr ->
BA.allocRet c_SNTRUP761_SIZE $ \kPtr ->
BA.alloc c_SNTRUP761_CIPHERTEXT_SIZE $ \cPtr ->
c_sntrup761_enc cPtr kPtr pkPtr rngContext rngFunc
sntrup761Dec :: Ciphertext -> SecretKey -> IO Key
sntrup761Dec c sk =
BA.withByteArray sk $ \skPtr ->
BA.withByteArray c $ \cPtr ->
BA.alloc c_SNTRUP761_SIZE $ \kPtr ->
c_sntrup761_dec kPtr cPtr skPtr
@@ -0,0 +1,17 @@
module Simplex.Messaging.Crypto.SNTRUP761.Bindings.Defines where
import Foreign.C
#include "sntrup761.h"
c_SNTRUP761_SECRETKEY_SIZE :: Int
c_SNTRUP761_SECRETKEY_SIZE = #{const SNTRUP761_SECRETKEY_SIZE}
c_SNTRUP761_PUBLICKEY_SIZE :: Int
c_SNTRUP761_PUBLICKEY_SIZE = #{const SNTRUP761_PUBLICKEY_SIZE}
c_SNTRUP761_CIPHERTEXT_SIZE :: Int
c_SNTRUP761_CIPHERTEXT_SIZE = #{const SNTRUP761_CIPHERTEXT_SIZE}
c_SNTRUP761_SIZE :: Int
c_SNTRUP761_SIZE = #{const SNTRUP761_SIZE}
@@ -0,0 +1,23 @@
{-# LANGUAGE ForeignFunctionInterface #-}
module Simplex.Messaging.Crypto.SNTRUP761.Bindings.FFI
( c_sntrup761_keypair,
c_sntrup761_enc,
c_sntrup761_dec,
) where
import Foreign
import Foreign.C
import Simplex.Messaging.Crypto.SNTRUP761.Bindings.RNG (RNGContext, RNGFunc)
-- void sntrup761_keypair (uint8_t *pk, uint8_t *sk, void *random_ctx, sntrup761_random_func *random);
foreign import ccall "sntrup761_keypair"
c_sntrup761_keypair :: Ptr Word8 -> Ptr Word8 -> RNGContext -> FunPtr RNGFunc -> IO ()
-- void sntrup761_enc (uint8_t *c, uint8_t *k, const uint8_t *pk, void *random_ctx, sntrup761_random_func *random);
foreign import ccall "sntrup761_enc"
c_sntrup761_enc :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> RNGContext -> FunPtr RNGFunc -> IO ()
-- void sntrup761_dec (uint8_t *k, const uint8_t *c, const uint8_t *sk);
foreign import ccall "sntrup761_dec"
c_sntrup761_dec :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> IO ()
@@ -0,0 +1,48 @@
{-# LANGUAGE NamedFieldPuns #-}
module Simplex.Messaging.Crypto.SNTRUP761.Bindings.RNG
( RNG (..),
withRNG,
createRNG,
freeRNG,
RNGContext,
RNGFunc,
mkRNGFunc,
) where
import Foreign
import Foreign.C
import Crypto.Random (drgNew, randomBytesGenerate)
import Data.ByteArray (ByteArrayAccess (copyByteArrayToPtr), Bytes)
import Data.IORef (atomicModifyIORef', newIORef)
import UnliftIO (bracket)
data RNG = RNG
{ rngContext :: RNGContext,
rngFunc :: FunPtr RNGFunc
}
withRNG :: (RNG -> IO c) -> IO c
withRNG = bracket createRNG freeRNG
createRNG :: IO RNG
createRNG = do
chachaState <- drgNew >>= newIORef -- XXX: ctxPtr could be used to store drg state, but cryptonite doesn't provide ByteAccess for ChaChaDRG
rngFunc <- mkRNGFunc $ \_ctxPtr sz buf -> do
bs <- atomicModifyIORef' chachaState $ swap . randomBytesGenerate (fromIntegral sz) :: IO Bytes
copyByteArrayToPtr bs buf
pure RNG {rngContext = nullPtr, rngFunc}
where
swap (a, b) = (b, a)
freeRNG :: RNG -> IO ()
freeRNG RNG {rngFunc} = freeHaskellFunPtr rngFunc
type RNGContext = Ptr RNG
-- typedef void random_func (void *ctx, size_t length, uint8_t *dst);
type RNGFunc = Ptr RNGContext -> CSize -> Ptr Word8 -> IO ()
foreign import ccall "wrapper"
mkRNGFunc :: RNGFunc -> IO (FunPtr RNGFunc)