{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Crypto.PubKey.Curve25519
( SecretKey
, PublicKey
, DhSecret
, dhSecret
, publicKey
, secretKey
, dh
, toPublic
, generateSecretKey
) where
import Data.Bits
import Data.Word
import Foreign.Ptr
import Foreign.Storable
import GHC.Ptr
import Crypto.Error
import Crypto.Internal.Compat
import Crypto.Internal.Imports
import Crypto.Internal.ByteArray (ByteArrayAccess, ScrubbedBytes, Bytes, withByteArray)
import qualified Crypto.Internal.ByteArray as B
import Crypto.Random
newtype SecretKey = SecretKey ScrubbedBytes
deriving (Show,Eq,ByteArrayAccess,NFData)
newtype PublicKey = PublicKey Bytes
deriving (Show,Eq,ByteArrayAccess,NFData)
newtype DhSecret = DhSecret ScrubbedBytes
deriving (Show,Eq,ByteArrayAccess,NFData)
publicKey :: ByteArrayAccess bs => bs -> CryptoFailable PublicKey
publicKey bs
| B.length bs == 32 = CryptoPassed $ PublicKey $ B.copyAndFreeze bs (\_ -> return ())
| otherwise = CryptoFailed CryptoError_PublicKeySizeInvalid
secretKey :: ByteArrayAccess bs => bs -> CryptoFailable SecretKey
secretKey bs
| B.length bs == 32 = unsafeDoIO $ do
withByteArray bs $ \inp -> do
valid <- isValidPtr inp
if valid
then (CryptoPassed . SecretKey) <$> B.copy bs (\_ -> return ())
else return $ CryptoFailed CryptoError_SecretKeyStructureInvalid
| otherwise = CryptoFailed CryptoError_SecretKeySizeInvalid
where
isValidPtr :: Ptr Word8 -> IO Bool
isValidPtr _ = do
return True
{-# NOINLINE secretKey #-}
dhSecret :: ByteArrayAccess b => b -> CryptoFailable DhSecret
dhSecret bs
| B.length bs == 32 = CryptoPassed $ DhSecret $ B.copyAndFreeze bs (\_ -> return ())
| otherwise = CryptoFailed CryptoError_SharedSecretSizeInvalid
dh :: PublicKey -> SecretKey -> DhSecret
dh (PublicKey pub) (SecretKey sec) = DhSecret <$>
B.allocAndFreeze 32 $ \result ->
withByteArray sec $ \psec ->
withByteArray pub $ \ppub ->
ccryptonite_curve25519 result psec ppub
{-# NOINLINE dh #-}
toPublic :: SecretKey -> PublicKey
toPublic (SecretKey sec) = PublicKey <$>
B.allocAndFreeze 32 $ \result ->
withByteArray sec $ \psec ->
ccryptonite_curve25519 result psec basePoint
where
basePoint = Ptr "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
{-# NOINLINE toPublic #-}
generateSecretKey :: MonadRandom m => m SecretKey
generateSecretKey = tweakToSecretKey <$> getRandomBytes 32
where
tweakToSecretKey :: ScrubbedBytes -> SecretKey
tweakToSecretKey bin = SecretKey $ B.copyAndFreeze bin $ \inp -> do
modifyByte inp 0 (\e0 -> e0 .&. 0xf8)
modifyByte inp 31 (\e31 -> (e31 .&. 0x7f) .|. 0x40)
modifyByte :: Ptr Word8 -> Int -> (Word8 -> Word8) -> IO ()
modifyByte p n f = peekByteOff p n >>= pokeByteOff p n . f
foreign import ccall "cryptonite_curve25519_donna"
ccryptonite_curve25519 :: Ptr Word8
-> Ptr Word8
-> Ptr Word8
-> IO ()