-- SPDX-FileCopyrightText: 2020 Serokell -- -- SPDX-License-Identifier: MPL-2.0 -- | Internals of @crypto_box@. module Crypto.Box.Internal ( SecretKey , toSecretKey , PublicKey , toPublicKey , keypair , Nonce , toNonce , create , open ) where import Prelude hiding (length) import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, allocRet, length, withByteArray) import Data.ByteArray.Sized (SizedByteArray, sizedByteArray) import Data.ByteString (ByteString) import Data.Functor (void) import Data.Proxy (Proxy (Proxy)) import qualified Data.ByteArray.Sized as Sized (alloc, allocRet) import qualified Libsodium as Na -- | Secret key that can be used for Box. type SecretKey = SizedByteArray Na.CRYPTO_BOX_SECRETKEYBYTES ScrubbedBytes -- | Convert bytes to a secret key. toSecretKey :: ScrubbedBytes -> Maybe SecretKey toSecretKey = sizedByteArray -- | Public key that can be used for Box. type PublicKey = SizedByteArray Na.CRYPTO_BOX_PUBLICKEYBYTES ByteString toPublicKey :: ByteString -> Maybe PublicKey toPublicKey = sizedByteArray -- | Generate a new 'SecretKey' together with its 'PublicKey'. -- -- Note: this function is not thread-safe (since the underlying -- C function is not thread-safe both in Sodium and in NaCl)! -- Either make sure there are no concurrent calls or see -- @Crypto.Init@ in -- -- to learn how to make this function thread-safe. keypair :: IO (PublicKey, SecretKey) keypair = do (pk, sk) <- Sized.allocRet Proxy $ \skPtr -> Sized.alloc $ \pkPtr -> -- always returns 0, so we don’t check it void $ Na.crypto_box_keypair pkPtr skPtr pure (pk, sk) -- | Nonce that can be used for Box. -- -- This type is parametrised by the actual data type that contains -- bytes. This can be, for example, a @ByteString@. type Nonce a = SizedByteArray Na.CRYPTO_BOX_NONCEBYTES a -- | Make a 'Nonce' from an arbitrary byte array. -- -- This function returns @Just@ if and only if the byte array has -- the right length to be used as a nonce with a Box. toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba) toNonce = sizedByteArray -- | Encrypt a message. create :: ( ByteArrayAccess nonce , ByteArrayAccess pt, ByteArray ct ) => PublicKey -- ^ Receiver’s public key -> SecretKey -- ^ Sender’s secret key -> Nonce nonce -- ^ Nonce -> pt -- ^ Plaintext message -> IO ct create pk sk nonce msg = do (_ret, ct) <- allocRet clen $ \ctPtr -> withByteArray pk $ \pkPtr -> withByteArray sk $ \skPtr -> withByteArray nonce $ \noncePtr -> withByteArray msg $ \msgPtr -> do -- TODO: Maybe, reimplement this without _easy, to stay closer -- to the original NaCl. Na.crypto_box_easy ctPtr msgPtr (fromIntegral $ length msg) noncePtr pkPtr skPtr -- _ret can be only 0, so we don’t check it -- TODO: Actually, it looks like this function can fail and return -- a -1, even though this is not documented :/. pure ct where clen :: Int clen = fromIntegral Na.crypto_box_macbytes + length msg -- | Decrypt a message. open :: ( ByteArrayAccess nonce , ByteArray pt, ByteArrayAccess ct ) => SecretKey -- ^ Receiver’s secret key -> PublicKey -- ^ Sender’s public key -> Nonce nonce -- ^ Nonce -> ct -- ^ Cyphertext -> IO (Maybe pt) open sk pk nonce ct = do (ret, msg) <- allocRet mlen $ \msgPtr -> withByteArray sk $ \skPtr -> withByteArray pk $ \pkPtr -> withByteArray nonce $ \noncePtr -> withByteArray ct $ \ctPtr -> do -- TODO: Maybe, reimplement this without _easy, to stay closer -- to the original NaCl. Na.crypto_box_open_easy msgPtr ctPtr (fromIntegral $ length ct) noncePtr pkPtr skPtr if ret == 0 then pure $ Just msg else pure Nothing where mlen :: Int mlen = length ct - fromIntegral Na.crypto_box_macbytes