module Botan.OneTimeAuth.Class
( OneTimeAuth(..)
, OneTimeAuthKey(..)
, OneTimeAuthNonce(..)
, OneTimeAuthCode(..)
, oneTimeAuthProxy
, oneTimeAuthFile
, IncrementalOneTimeAuth(..)
, oneTimeAuthFileLazy
) where

import Botan.Prelude

import Data.Proxy (Proxy(..))

import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Lazy as Lazy

-- TODO: Maybe OneTimeAuth -> OTA?

data family OneTimeAuthKey ota
data family OneTimeAuthNonce ota
data family OneTimeAuthCode ota

-- Invariant: The key and nonce must never be re-used together.
type OneTimeKey ota = (OneTimeAuthKey ota, OneTimeAuthNonce ota)
-- TODO: Decide whether to `SeparateKey ota -> SeparateNonce ota -> ...`
--  or `CombinedOneTimeKey ota -> ...`
-- Note that this is a different meaning of `Combined` than as used in `saltine`.
-- We mean `Combined` as the opposite of `Detached` in saltine.
-- TODO: Prefer `Attached` terminology?

class OneTimeAuth ota where
    oneTimeAuth :: OneTimeAuthKey ota -> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
    default oneTimeAuth :: (IncrementalOneTimeAuth ota) => OneTimeAuthKey ota -> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
    oneTimeAuth OneTimeAuthKey ota
k OneTimeAuthNonce ota
n = OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
forall ota.
IncrementalOneTimeAuth ota =>
OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
oneTimeAuthLazy OneTimeAuthKey ota
k OneTimeAuthNonce ota
n (ByteString -> OneTimeAuthCode ota)
-> (ByteString -> ByteString) -> ByteString -> OneTimeAuthCode ota
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
ByteString.fromStrict

    -- verifyOneTimeAuth :: OneTimeAuthKey ota -> ByteString -> OneTimeAuthCode ota -> Bool
    -- verifyOneTimeAuth k bs d = oneTimeAuth k bs == d

oneTimeAuthProxy :: (OneTimeAuth ota) => Proxy ota -> OneTimeAuthKey ota -> OneTimeAuthNonce ota ->ByteString -> OneTimeAuthCode ota
oneTimeAuthProxy :: forall ota.
OneTimeAuth ota =>
Proxy ota
-> OneTimeAuthKey ota
-> OneTimeAuthNonce ota
-> ByteString
-> OneTimeAuthCode ota
oneTimeAuthProxy Proxy ota
_ = OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
forall ota.
OneTimeAuth ota =>
OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
oneTimeAuth

oneTimeAuthFile :: (OneTimeAuth ota, MonadIO m) => OneTimeAuthKey ota -> OneTimeAuthNonce ota -> FilePath -> m (OneTimeAuthCode ota)
oneTimeAuthFile :: forall ota (m :: * -> *).
(OneTimeAuth ota, MonadIO m) =>
OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> FilePath -> m (OneTimeAuthCode ota)
oneTimeAuthFile OneTimeAuthKey ota
k OneTimeAuthNonce ota
n FilePath
fp = OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
forall ota.
OneTimeAuth ota =>
OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
oneTimeAuth OneTimeAuthKey ota
k OneTimeAuthNonce ota
n (ByteString -> OneTimeAuthCode ota)
-> m ByteString -> m (OneTimeAuthCode ota)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ByteString -> m ByteString
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO ByteString
ByteString.readFile FilePath
fp)

class (OneTimeAuth ota) => IncrementalOneTimeAuth ota where
    oneTimeAuthLazy :: OneTimeAuthKey ota -> OneTimeAuthNonce ota -> Lazy.ByteString -> OneTimeAuthCode ota

oneTimeAuthFileLazy :: (IncrementalOneTimeAuth ota, MonadIO m) => OneTimeAuthKey ota -> OneTimeAuthNonce ota -> FilePath -> m (OneTimeAuthCode ota)
oneTimeAuthFileLazy :: forall ota (m :: * -> *).
(IncrementalOneTimeAuth ota, MonadIO m) =>
OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> FilePath -> m (OneTimeAuthCode ota)
oneTimeAuthFileLazy OneTimeAuthKey ota
k OneTimeAuthNonce ota
n FilePath
fp = do
    ByteString
bs <- IO ByteString -> m ByteString
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ByteString -> m ByteString) -> IO ByteString -> m ByteString
forall a b. (a -> b) -> a -> b
$ FilePath -> IO ByteString
Lazy.readFile FilePath
fp
    -- Seq is probably unnecessary
    let d :: OneTimeAuthCode ota
d = OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
forall ota.
IncrementalOneTimeAuth ota =>
OneTimeAuthKey ota
-> OneTimeAuthNonce ota -> ByteString -> OneTimeAuthCode ota
oneTimeAuthLazy OneTimeAuthKey ota
k OneTimeAuthNonce ota
n ByteString
bs
        in OneTimeAuthCode ota
d OneTimeAuthCode ota
-> m (OneTimeAuthCode ota) -> m (OneTimeAuthCode ota)
forall a b. a -> b -> b
`seq` OneTimeAuthCode ota -> m (OneTimeAuthCode ota)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return OneTimeAuthCode ota
d