{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} module Crypto.Verification (Signed(..) , getSignable , sign , verify , serialize , deserialize , deserializeSignable) where import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS import Crypto.Hash.Algorithms (SHA256) import Crypto.MAC.HMAC (HMAC(..), hmac) import Crypto.Hash (Digest, digestFromByteString) import Data.ByteArray.Encoding (Base(..) , convertToBase , convertFromBase) import Protolude (rightToMaybe) import Data.Tuple (swap) import Crypto.Encryption (Encrypted(..), getIV, getSecret) import qualified Extension.ByteString as EBS data Signed signable = Signed signable (Digest SHA256) deriving (Show, Eq) class Eq signable => Signable signable where sign :: String -> signable -> Signed signable verify :: String -> Signed signable -> Bool verify key signed@(Signed message _) = sign key message == signed getSignable :: Signed signable -> signable getSignable (Signed signable _) = signable serializeSignable :: signable -> ByteString deserializeSignable :: ByteString -> Maybe signable serialize :: Signed signable -> ByteString serialize (Signed signable digest) = convertToBase Base64URLUnpadded (serializeSignable signable) `BS.append` "|signature." `BS.append` BS.pack (show digest) deserialize :: ByteString -> Maybe (Signed signable) deserialize bs = Signed <$> (rightToMaybe message >>= deserializeSignable) <*> (rightToMaybe signature >>= digestFromByteString) where (encodedMessage, base16Signature) = EBS.stripPrefix "|signature." <$> BS.span (/= '|') bs signature :: Either String ByteString signature = convertFromBase Base16 base16Signature message = convertFromBase Base64URLUnpadded encodedMessage instance {-# OVERLAPPING #-} Signable String where sign key message = Signed message digest where digest = hmacGetDigest hmac'ed hmac'ed = hmac (BS.pack key) (BS.pack message) :: HMAC SHA256 serializeSignable = convertToBase Base64URLUnpadded . BS.pack deserializeSignable bs = BS.unpack <$> rightToMaybe (convertFromBase Base64URLUnpadded bs) instance Signable Encrypted where sign key message = Signed message digest where digest = hmacGetDigest hmac'ed hmac'ed = hmac (BS.pack key) (serializeSignable message) :: HMAC SHA256 serializeSignable encrypted = "iv," `BS.append` convertToBase Base64URLUnpadded (BS.pack . getIV $ encrypted) `BS.append` "|" `BS.append` convertToBase Base64URLUnpadded (getSecret encrypted) deserializeSignable bs = Encrypted <$> (BS.unpack <$> rightToMaybe iv) <*> (rightToMaybe secret) where (base64Secret, base64IV) = EBS.stripPrefix "iv," . EBS.stripSuffix "|" <$> swap (BS.spanEnd (/= '|') bs) iv = convertFromBase Base64URLUnpadded base64IV secret = convertFromBase Base64URLUnpadded base64Secret