packages feed

Dust-crypto-0.1: Dust/Crypto/Ed25519.hs

{-# CFILES lib/ed25519-donna/ed25519.c #-}
{-# LANGUAGE ForeignFunctionInterface #-}

module Dust.Crypto.Ed25519
(
    ed25519_publickey,
    ed25519_sign_open,
    ed25519_sign
) where

import qualified Data.ByteString as B
import Data.ByteString (ByteString)
import Data.ByteString.Unsafe (unsafeUseAsCString)

import Foreign.C
import Foreign.Marshal.Unsafe (unsafeLocalState)

ed25519_publickey :: ByteString -> ByteString
ed25519_publickey privkey = pubkey
    where
    Just pubkey = fmap unsafeLocalState (unsafe_ed25519_publickey privkey)

ed25519_sign_open :: ByteString -> ByteString -> ByteString -> Bool
ed25519_sign_open msg pubkey signature = result
	where
	Just result = fmap unsafeLocalState $ unsafe_ed25519_sign_open msg pubkey signature

ed25519_sign :: ByteString -> ByteString -> ByteString -> ByteString
ed25519_sign msg secret pubkey = signature
	where
	Just signature = fmap unsafeLocalState $ unsafe_ed25519_sign msg secret pubkey

unsafe_ed25519_publickey :: ByteString -> Maybe (IO ByteString)
unsafe_ed25519_publickey input
    | B.length input <= 32 = Just $
        -- This ByteString will be overwritten by the C call, but so long as
        -- the C side does not keep a reference to it afterward, it does what we
        -- expect and saves us a copy
        unsafeUseAsCString outBS $ \output ->
            B.useAsCString input $ \cinput -> do
                c_ed25519_publickey cinput output
                return outBS
    | otherwise = Nothing
    where
    outBS = B.replicate 32 0xAB
    {-# NOINLINE outBS #-}

unsafe_ed25519_sign_open :: ByteString -> ByteString -> ByteString -> Maybe (IO Bool)
unsafe_ed25519_sign_open msg pubkey signature
    | B.length pubkey <= 32 && B.length signature <= 64 = Just $
        B.useAsCStringLen msg $ \(cmsg, cmsglen) ->
            B.useAsCString pubkey $ \cpubkey ->
                B.useAsCString signature $ \csignature ->
                    let result = c_ed25519_sign_open cmsg (fromIntegral cmsglen) cpubkey csignature in
                        return (result == 0)
    | otherwise = Nothing

unsafe_ed25519_sign :: ByteString -> ByteString -> ByteString -> Maybe (IO ByteString)
unsafe_ed25519_sign msg secret pubkey
    | B.length secret <= 32 && B.length pubkey <= 32 = Just $
        -- This ByteString will be overwritten by the C call, but so long as
        -- the C side does not keep a reference to it afterward, it does what we
        -- expect and saves us a copy
        unsafeUseAsCString outBS $ \signature ->
            B.useAsCStringLen msg $ \(cmsg, cmsglen) ->
                B.useAsCString secret $ \csecret ->
                    B.useAsCString pubkey $ \cpubkey -> do
                        c_ed25519_sign cmsg (fromIntegral cmsglen) csecret cpubkey signature
                        return outBS
    | otherwise = Nothing
    where
    outBS = B.replicate 64 0xAB
    {-# NOINLINE outBS #-}

-- Should be Ptr CUChar (not CString), but this should be safe if we just
-- use them as bytes

foreign import ccall unsafe "ed25519_publickey" c_ed25519_publickey ::
   CString -> CString -> IO ()

foreign import ccall unsafe "ed25519_sign_open" c_ed25519_sign_open ::
    CString -> CSize -> CString -> CString -> CInt

foreign import ccall unsafe "ed25519_sign" c_ed25519_sign ::
    CString -> CSize -> CString -> CString -> CString -> IO ()