module Data.UUID.Cryptographic
( CryptoID(..)
, CryptoUUID
, encrypt
, decrypt
, CryptoIDError(..)
) where
import Data.CryptoID
import Data.CryptoID.Poly hiding (encrypt, decrypt)
import qualified Data.CryptoID.Poly as Poly (encrypt, decrypt)
import Data.UUID (UUID, toByteString, fromByteString)
import Data.Binary
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Lazy as Lazy.ByteString
import Data.ByteArray (ByteArrayAccess)
import qualified Data.ByteArray as ByteArray
import Control.Monad.Except
import GHC.TypeLits
type CryptoUUID (namespace :: Symbol) = CryptoID namespace UUID
_ciphertext :: Functor m => (a -> m b) -> CryptoID n a -> m (CryptoID n b)
_ciphertext f (CryptoID x) = CryptoID <$> f x
pad :: (MonadError CryptoIDError m, ByteArrayAccess a) => Int -> a -> m ByteString
pad n (ByteArray.unpack -> src)
| l > n = throwError CiphertextConversionFailed
| otherwise = return . ByteString.pack $ src ++ replicate (n l) 0
where
l = length src
encrypt :: forall a m namespace.
( KnownSymbol namespace
, Binary a
, MonadError CryptoIDError m
) => CryptoIDKey -> a -> m (CryptoUUID namespace)
encrypt key val = do
plaintext <- pad 16 . Lazy.ByteString.toStrict $ encode val
_ciphertext uuidConversion =<< Poly.encrypt key plaintext
where
uuidConversion = maybe (throwError CiphertextConversionFailed) return . fromByteString . Lazy.ByteString.fromStrict
decrypt :: forall a m namespace.
( KnownSymbol namespace
, Binary a
, MonadError CryptoIDError m
) => CryptoIDKey -> CryptoUUID namespace -> m a
decrypt key cId = do
cId' <- _ciphertext (return . Lazy.ByteString.toStrict . toByteString) cId
plaintext <- Lazy.ByteString.fromStrict <$> Poly.decrypt key cId'
case decodeOrFail plaintext of
Left err -> throwError $ DeserializationError err
Right (rem, _, res)
| Lazy.ByteString.all (== 0) rem -> return res
| otherwise -> throwError InvalidNamespaceDetected