module Ecluse.Core.Worker.Integrity (
IntegrityResult (..),
verifyIntegrity,
) where
import Data.ByteArray.Encoding (Base (Base16, Base64), convertToBase)
import Data.Foldable (maximumBy)
import Data.Text qualified as T
import Ecluse.Core.Package (Hash (hashAlg, hashValue), HashAlg (SHA256, SRI), computeDigest)
import Ecluse.Core.Package.Integrity (Strength, assertedAlg, integrityStrength, sriBody, sriPrefix)
data IntegrityResult
=
IntegrityVerified
|
IntegrityMismatch Text
deriving stock (IntegrityResult -> IntegrityResult -> Bool
(IntegrityResult -> IntegrityResult -> Bool)
-> (IntegrityResult -> IntegrityResult -> Bool)
-> Eq IntegrityResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IntegrityResult -> IntegrityResult -> Bool
== :: IntegrityResult -> IntegrityResult -> Bool
$c/= :: IntegrityResult -> IntegrityResult -> Bool
/= :: IntegrityResult -> IntegrityResult -> Bool
Eq, Int -> IntegrityResult -> ShowS
[IntegrityResult] -> ShowS
IntegrityResult -> String
(Int -> IntegrityResult -> ShowS)
-> (IntegrityResult -> String)
-> ([IntegrityResult] -> ShowS)
-> Show IntegrityResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> IntegrityResult -> ShowS
showsPrec :: Int -> IntegrityResult -> ShowS
$cshow :: IntegrityResult -> String
show :: IntegrityResult -> String
$cshowList :: [IntegrityResult] -> ShowS
showList :: [IntegrityResult] -> ShowS
Show)
verifyIntegrity :: NonEmpty Hash -> ByteString -> IntegrityResult
verifyIntegrity :: NonEmpty Hash -> ByteString -> IntegrityResult
verifyIntegrity NonEmpty Hash
hashes ByteString
bytes =
let strongest :: Hash
strongest = (Hash -> Hash -> Ordering) -> NonEmpty Hash -> Hash
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> Ordering) -> t a -> a
maximumBy ((Hash -> Strength) -> Hash -> Hash -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing Hash -> Strength
authority) NonEmpty Hash
hashes
in case LByteString -> Hash -> Maybe Bool
matchesDigest (ByteString -> LByteString
forall l s. LazyStrict l s => s -> l
toLazy ByteString
bytes) Hash
strongest of
Maybe Bool
Nothing ->
Text -> IntegrityResult
IntegrityMismatch
( Text
"the strongest admitted digest ("
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Hash -> Text
describeDigest Hash
strongest
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
") is in an algorithm the worker cannot verify"
)
Just Bool
True -> IntegrityResult
IntegrityVerified
Just Bool
False ->
Text -> IntegrityResult
IntegrityMismatch (Text
"the " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Hash -> Text
describeDigest Hash
strongest Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" digest did not match the fetched bytes")
authority :: Hash -> Strength
authority :: Hash -> Strength
authority = Strength -> (HashAlg -> Strength) -> Maybe HashAlg -> Strength
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (HashAlg -> Strength
integrityStrength HashAlg
SHA256) HashAlg -> Strength
integrityStrength (Maybe HashAlg -> Strength)
-> (Hash -> Maybe HashAlg) -> Hash -> Strength
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Hash -> Maybe HashAlg
assertedAlg
matchesDigest :: LByteString -> Hash -> Maybe Bool
matchesDigest :: LByteString -> Hash -> Maybe Bool
matchesDigest LByteString
lazyBytes Hash
h = do
alg <- Hash -> Maybe HashAlg
assertedAlg Hash
h
digestOf <- computeDigest alg
let digest = LByteString -> ByteString
digestOf LByteString
lazyBytes
pure $ case hashAlg h of
HashAlg
SRI -> ByteString -> Text
base64 ByteString
digest Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> Text
sriBody (Hash -> Text
hashValue Hash
h)
HashAlg
_ -> ByteString -> Text
hexLower ByteString
digest Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> Text
T.toLower (Hash -> Text
hashValue Hash
h)
describeDigest :: Hash -> Text
describeDigest :: Hash -> Text
describeDigest Hash
h = case Hash -> HashAlg
hashAlg Hash
h of
HashAlg
SRI -> Text
"SRI " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
sriPrefix (Hash -> Text
hashValue Hash
h)
HashAlg
alg -> HashAlg -> Text
forall b a. (Show a, IsString b) => a -> b
show HashAlg
alg
hexLower :: ByteString -> Text
hexLower :: ByteString -> Text
hexLower ByteString
d = Text -> Text
T.toLower (ByteString -> Text
forall a b. ConvertUtf8 a b => b -> a
decodeUtf8 (Base -> ByteString -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
Base -> bin -> bout
convertToBase Base
Base16 ByteString
d :: ByteString))
base64 :: ByteString -> Text
base64 :: ByteString -> Text
base64 ByteString
d = ByteString -> Text
forall a b. ConvertUtf8 a b => b -> a
decodeUtf8 (Base -> ByteString -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
Base -> bin -> bout
convertToBase Base
Base64 ByteString
d :: ByteString)