{-# 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