{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Network.Hawk.Algo ( HawkAlgo(..) , HawkAlgoCls(..) , Key(..) , SHA1(SHA1) , SHA256(SHA256) , readHawkAlgo ) where import Crypto.Hash (Digest (..), hash) import Crypto.Hash.Algorithms (SHA1 (..), SHA256 (..)) import Crypto.MAC.HMAC (HMAC, hmac, hmacGetDigest) import Data.ByteArray (ByteArrayAccess) import qualified Data.ByteArray.Encoding as B (Base (..), convertToBase) import Data.ByteString (ByteString) import Data.Char (toLower) import Data.String (IsString) import GHC.Generics import Network.Iron.Util (b64) -- fixme: decide whether this should be bytestring or SecureMem, and -- whether it should be a typeclass. -- | A user-supplied password or generated key. newtype Key = Key ByteString deriving (Show, Eq, Generic, ByteArrayAccess, IsString) -- | The class of HMAC algorithms supported by the Hawk -- protocol. Users of the 'Network.Hawk' module probably won't -- directly need this. class HawkAlgoCls a where -- | Calculates the hash of a message. The result is encoded in -- Base64. hawkHash :: a -> ByteString -> ByteString -- | Calculates the hash-based MAC of a message. The result is -- encoded in Base64. hawkMac :: a -> Key -> ByteString -> ByteString -- | A wrapper data type representing one of the supported HMAC -- algorithms. Use @HawkAlgo SHA1@ or @HawkAlgo SHA256@. data HawkAlgo = forall alg . (HawkAlgoCls alg, Show alg) => HawkAlgo alg instance HawkAlgoCls HawkAlgo where hawkHash (HawkAlgo alg) = hawkHash alg hawkMac (HawkAlgo alg) = hawkMac alg instance Show HawkAlgo where show (HawkAlgo a) = map toLower (show a) instance HawkAlgoCls SHA1 where hawkHash _ bs = b64 (hash bs :: Digest SHA1) hawkMac _ k bs = b64 $ hmacGetDigest (hmac k bs :: HMAC SHA1) instance HawkAlgoCls SHA256 where hawkHash _ bs = b64 (hash bs :: Digest SHA256) hawkMac _ k bs = b64 $ hmacGetDigest (hmac k bs :: HMAC SHA256) instance Eq HawkAlgo where _ == _ = True -- fixme: only used for test assertions currently -- | Inverse of 'show', for parsing @"algorithm"@ fields in JSON -- structures. readHawkAlgo :: String -> Maybe HawkAlgo readHawkAlgo a = case map toLower a of "sha1" -> Just (HawkAlgo SHA1) "sha256" -> Just (HawkAlgo SHA256) _ -> Nothing