module Network.S3.Sign ( sign ) where
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Base64 as Base64
import Data.ByteString.UTF8 (ByteString)
import Blaze.ByteString.Builder (toByteString, fromByteString)
import Data.Monoid ((<>))
import Data.Time.Clock (UTCTime)
import Data.Time.Format (formatTime, defaultTimeLocale)
import Network.S3.Types (S3Request(..), S3SignedRequest(..))
import Network.S3.URL (canonicalRequest)
import Data.Byteable (toBytes)
import Crypto.Hash
reqString :: S3Request -> Digest SHA256
reqString = hash . toByteString . canonicalRequest
hmacSHA256 :: ByteString -> ByteString -> HMAC SHA256
hmacSHA256 key = hmac key
hmac_ :: ByteString -> ByteString -> ByteString
hmac_ key = toBytes . hmacGetDigest . hmacSHA256 key
sign :: ByteString -> S3Request -> UTCTime -> S3SignedRequest
sign key req utc =
let
seconds = BS.pack (formatTime defaultTimeLocale "T%M%H%SZ" utc)
date = BS.pack (formatTime defaultTimeLocale "%Y%m%d" utc)
timestamp = fromByteString (date <> seconds)
reqHash = fromByteString (digestToHexByteString (reqString req))
region = regionName req
service = "s3"
dateKey = hmac_ ("AWS4" <> key) date
dateRegionKey = hmac_ dateKey region
dateRegionServiceKey = hmac_ dateRegionKey service
signingKey = hmac_ dateRegionServiceKey "aws4_request"
scope =
fromByteString date <> fromByteString "/" <>
fromByteString region <> fromByteString "/" <>
fromByteString service <> fromByteString "/aws4_request"
algorithm = "AWS4-HMAC-SHA256"
signingStringBuilder =
algorithm <> "\n" <>
timestamp <> "\n" <>
scope <> "\n" <>
reqHash
stringToSign = toByteString signingStringBuilder
signature = hmacSHA256 signingKey stringToSign
hexSignature = digestToHexByteString (hmacGetDigest signature)
in
S3SignedRequest {
sigHeaders = s3headers req
, sigDate = toByteString timestamp
, sigCredential = toByteString scope
, sigPolicy = Base64.encode stringToSign
, sigSignature = hexSignature
, sigAlgorithm = toByteString algorithm
}