module Hackage.Security.TUF.FileInfo (
FileInfo(..)
, HashFn(..)
, Hash(..)
, fileInfo
, computeFileInfo
, compareTrustedFileInfo
, knownFileInfoEqual
, fileInfoSHA256
, Int54
) where
import Prelude hiding (lookup)
import Data.Map (Map)
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.Map as Map
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Lazy as BS.L
import qualified Data.ByteString.Char8 as BS.C8
import Hackage.Security.JSON
import Hackage.Security.TUF.Common
import Hackage.Security.Util.Path
data HashFn = HashFnSHA256
| HashFnMD5
deriving (Show, Eq, Ord)
data FileInfo = FileInfo {
fileInfoLength :: FileLength
, fileInfoHashes :: Map HashFn Hash
}
deriving (Show)
fileInfo :: BS.L.ByteString -> FileInfo
fileInfo bs = FileInfo {
fileInfoLength = FileLength . fromIntegral $ BS.L.length bs
, fileInfoHashes = Map.fromList [
(HashFnSHA256, Hash $ BS.C8.unpack $ Base16.encode $ SHA256.hashlazy bs)
]
}
computeFileInfo :: FsRoot root => Path root -> IO FileInfo
computeFileInfo fp = fileInfo <$> readLazyByteString fp
compareTrustedFileInfo :: FileInfo
-> FileInfo
-> Bool
compareTrustedFileInfo expectedInfo actualInfo =
sameLength expectedInfo actualInfo
&& sameSHA256 expectedInfo actualInfo
where
sameLength a b = fileInfoLength a
== fileInfoLength b
sameSHA256 a b = case (fileInfoSHA256 a,
fileInfoSHA256 b) of
(Just ha, Just hb) -> ha == hb
_ -> False
knownFileInfoEqual :: FileInfo -> FileInfo -> Bool
knownFileInfoEqual a b = (==) (fileInfoLength a, fileInfoHashes a)
(fileInfoLength b, fileInfoHashes b)
fileInfoSHA256 :: FileInfo -> Maybe Hash
fileInfoSHA256 FileInfo{..} = Map.lookup HashFnSHA256 fileInfoHashes
instance Monad m => ToObjectKey m HashFn where
toObjectKey HashFnSHA256 = return "sha256"
toObjectKey HashFnMD5 = return "md5"
instance ReportSchemaErrors m => FromObjectKey m HashFn where
fromObjectKey "sha256" = return (Just HashFnSHA256)
fromObjectKey "md5" = return (Just HashFnMD5)
fromObjectKey _ = return Nothing
instance Monad m => ToJSON m FileInfo where
toJSON FileInfo{..} = mkObject [
("length", toJSON fileInfoLength)
, ("hashes", toJSON fileInfoHashes)
]
instance ReportSchemaErrors m => FromJSON m FileInfo where
fromJSON enc = do
fileInfoLength <- fromJSField enc "length"
fileInfoHashes <- fromJSField enc "hashes"
return FileInfo{..}