packages feed

NaCl-0.0.2.0: lib/Crypto/Secretbox/Internal.hs

-- SPDX-FileCopyrightText: 2020 Serokell
--
-- SPDX-License-Identifier: MPL-2.0

-- | Internals of @crypto_secretbox@.
module Crypto.Secretbox.Internal
  ( Key
  , toKey

  , Nonce
  , toNonce

  , create
  , open
  ) where

import Prelude hiding (length)

import Data.ByteArray (ByteArray, ByteArrayAccess, allocRet, length, withByteArray)

import qualified Libsodium as Na

import Data.ByteArray.Sized (SizedByteArray, sizedByteArray)


-- | Encryption key that can be used for Secretbox.
--
-- This type is parametrised by the actual data type that contains
-- bytes. This can be, for example, a @ByteString@, but, since this
-- is a secret key, it is better to use @ScrubbedBytes@.
type Key a = SizedByteArray Na.CRYPTO_SECRETBOX_KEYBYTES a

-- | Make a 'Key' 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 key with a Secretbox.
toKey :: ByteArrayAccess ba => ba -> Maybe (Key ba)
toKey = sizedByteArray


-- | Nonce that can be used for Secretbox.
--
-- 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_SECRETBOX_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 Secretbox.
toNonce :: ByteArrayAccess ba => ba -> Maybe (Nonce ba)
toNonce = sizedByteArray


-- | Encrypt a message.
create
  ::  ( ByteArrayAccess key, ByteArrayAccess nonce
      , ByteArrayAccess pt, ByteArray ct
      )
  => Key key  -- ^ Secret key
  -> Nonce nonce  -- ^ Nonce
  -> pt -- ^ Plaintext message
  -> IO ct
create key nonce msg = do
    (_ret, ct) <-
      allocRet clen $ \ctPtr ->
      withByteArray key $ \keyPtr ->
      withByteArray nonce $ \noncePtr ->
      withByteArray msg $ \msgPtr -> do
        -- TODO: Maybe, reimplement this without _easy, to stay closer
        -- to the original NaCl.
        Na.crypto_secretbox_easy ctPtr
          msgPtr (fromIntegral $ length msg)
          noncePtr
          keyPtr
    -- _ret can be only 0, so we don’t check it
    pure ct
  where
    clen :: Int
    clen = fromIntegral Na.crypto_secretbox_macbytes + length msg


-- | Decrypt a message.
open
  ::  ( ByteArrayAccess key, ByteArrayAccess nonce
      , ByteArray pt, ByteArrayAccess ct
      )
  => Key key  -- ^ Secret key
  -> Nonce nonce  -- ^ Nonce
  -> ct -- ^ Cyphertext
  -> IO (Maybe pt)
open key nonce ct = do
    (ret, msg) <-
      allocRet mlen $ \msgPtr ->
      withByteArray key $ \keyPtr ->
      withByteArray nonce $ \noncePtr ->
      withByteArray ct $ \ctPtr -> do
        -- TODO: Maybe, reimplement this without _easy, to stay closer
        -- to the original NaCl.
        Na.crypto_secretbox_open_easy msgPtr
          ctPtr (fromIntegral $ length ct)
          noncePtr
          keyPtr
    if ret == 0 then
      pure $ Just msg
    else
      pure Nothing
  where
    mlen :: Int
    mlen = length ct - fromIntegral Na.crypto_secretbox_macbytes