{-# LANGUAGE CPP #-}
module Hackage.Security.Trusted.TCB (
Trusted(DeclareTrusted)
, trusted
, trustStatic
, trustVerified
, trustApply
, trustElems
, VerificationError(..)
, RootUpdated(..)
, VerificationHistory
, SignaturesVerified
, signaturesVerified
, verifyRole'
, verifyFingerprints
#if __GLASGOW_HASKELL__ >= 710
, StaticPtr
#else
, StaticPtr
, static
#endif
) where
import MyPrelude
import Control.Exception
import Control.Monad (when, unless)
import Control.Monad.Except (Except, runExcept, throwError)
import Data.Typeable
import Data.Time
import Hackage.Security.TUF
import Hackage.Security.JSON
import Hackage.Security.Key
import Hackage.Security.Util.Pretty
import qualified Hackage.Security.Util.Lens as Lens
#if __GLASGOW_HASKELL__ >= 710
import GHC.StaticPtr
#else
newtype StaticPtr a = StaticPtr { deRefStaticPtr :: a }
static :: a -> StaticPtr a
static = StaticPtr
#endif
newtype Trusted a = DeclareTrusted { forall a. Trusted a -> a
trusted :: a }
deriving (Trusted a -> Trusted a -> Bool
forall a. Eq a => Trusted a -> Trusted a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Trusted a -> Trusted a -> Bool
$c/= :: forall a. Eq a => Trusted a -> Trusted a -> Bool
== :: Trusted a -> Trusted a -> Bool
$c== :: forall a. Eq a => Trusted a -> Trusted a -> Bool
Eq, Int -> Trusted a -> ShowS
forall a. Show a => Int -> Trusted a -> ShowS
forall a. Show a => [Trusted a] -> ShowS
forall a. Show a => Trusted a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Trusted a] -> ShowS
$cshowList :: forall a. Show a => [Trusted a] -> ShowS
show :: Trusted a -> String
$cshow :: forall a. Show a => Trusted a -> String
showsPrec :: Int -> Trusted a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Trusted a -> ShowS
Show)
trustStatic :: StaticPtr a -> Trusted a
trustStatic :: forall a. StaticPtr a -> Trusted a
trustStatic = forall a. a -> Trusted a
DeclareTrusted forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. StaticPtr a -> a
deRefStaticPtr
trustVerified :: SignaturesVerified a -> Trusted a
trustVerified :: forall a. SignaturesVerified a -> Trusted a
trustVerified = forall a. a -> Trusted a
DeclareTrusted forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. SignaturesVerified a -> a
signaturesVerified
trustApply :: Trusted (a -> b) -> Trusted a -> Trusted b
trustApply :: forall a b. Trusted (a -> b) -> Trusted a -> Trusted b
trustApply (DeclareTrusted a -> b
f) (DeclareTrusted a
x) = forall a. a -> Trusted a
DeclareTrusted (a -> b
f a
x)
trustElems :: Traversable f => Trusted (f a) -> f (Trusted a)
trustElems :: forall (f :: * -> *) a.
Traversable f =>
Trusted (f a) -> f (Trusted a)
trustElems (DeclareTrusted f a
fa) = forall a. a -> Trusted a
DeclareTrusted forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` f a
fa
newtype SignaturesVerified a = SignaturesVerified { forall a. SignaturesVerified a -> a
signaturesVerified :: a }
data VerificationError =
VerificationErrorSignatures TargetPath
| VerificationErrorExpired TargetPath
| VerificationErrorVersion TargetPath
| VerificationErrorFileInfo TargetPath
| VerificationErrorUnknownTarget TargetPath
| VerificationErrorMissingSHA256 TargetPath
| VerificationErrorDeserialization TargetPath DeserializationError
| VerificationErrorLoop VerificationHistory
deriving (Typeable)
data RootUpdated = RootUpdated
deriving (Typeable)
type VerificationHistory = [Either RootUpdated VerificationError]
#if MIN_VERSION_base(4,8,0)
deriving instance Show VerificationError
deriving instance Show RootUpdated
instance Exception VerificationError where displayException :: VerificationError -> String
displayException = forall a. Pretty a => a -> String
pretty
instance Exception RootUpdated where displayException :: RootUpdated -> String
displayException = forall a. Pretty a => a -> String
pretty
#else
instance Exception VerificationError
instance Show VerificationError where show = pretty
instance Show RootUpdated where show = pretty
instance Exception RootUpdated
#endif
instance Pretty VerificationError where
pretty :: VerificationError -> String
pretty (VerificationErrorSignatures TargetPath
file) =
forall a. Pretty a => a -> String
pretty TargetPath
file forall a. [a] -> [a] -> [a]
++ String
" does not have enough signatures signed with the appropriate keys"
pretty (VerificationErrorExpired TargetPath
file) =
forall a. Pretty a => a -> String
pretty TargetPath
file forall a. [a] -> [a] -> [a]
++ String
" is expired"
pretty (VerificationErrorVersion TargetPath
file) =
String
"Version of " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty TargetPath
file forall a. [a] -> [a] -> [a]
++ String
" is less than the previous version"
pretty (VerificationErrorFileInfo TargetPath
file) =
String
"Invalid hash for " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty TargetPath
file
pretty (VerificationErrorUnknownTarget TargetPath
file) =
forall a. Pretty a => a -> String
pretty TargetPath
file forall a. [a] -> [a] -> [a]
++ String
" not found in corresponding target metadata"
pretty (VerificationErrorMissingSHA256 TargetPath
file) =
String
"Missing SHA256 hash for " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty TargetPath
file
pretty (VerificationErrorDeserialization TargetPath
file DeserializationError
err) =
String
"Could not deserialize " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty TargetPath
file forall a. [a] -> [a] -> [a]
++ String
": " forall a. [a] -> [a] -> [a]
++ forall a. Pretty a => a -> String
pretty DeserializationError
err
pretty (VerificationErrorLoop VerificationHistory
es) =
String
"Verification loop. Errors in order:\n"
forall a. [a] -> [a] -> [a]
++ [String] -> String
unlines (forall a b. (a -> b) -> [a] -> [b]
map ((String
" " forall a. [a] -> [a] -> [a]
++) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either forall a. Pretty a => a -> String
pretty forall a. Pretty a => a -> String
pretty) VerificationHistory
es)
instance Pretty RootUpdated where
pretty :: RootUpdated -> String
pretty RootUpdated
RootUpdated = String
"Root information updated"
verifyRole' :: forall a. HasHeader a
=> Trusted (RoleSpec a)
-> TargetPath
-> Maybe FileVersion
-> Maybe UTCTime
-> Signed a -> Either VerificationError (SignaturesVerified a)
verifyRole' :: forall a.
HasHeader a =>
Trusted (RoleSpec a)
-> TargetPath
-> Maybe FileVersion
-> Maybe UTCTime
-> Signed a
-> Either VerificationError (SignaturesVerified a)
verifyRole' (forall a. Trusted a -> a
trusted -> RoleSpec{roleSpecThreshold :: forall a. RoleSpec a -> KeyThreshold
roleSpecThreshold = KeyThreshold Int54
threshold, [Some PublicKey]
roleSpecKeys :: forall a. RoleSpec a -> [Some PublicKey]
roleSpecKeys :: [Some PublicKey]
..})
TargetPath
targetPath
Maybe FileVersion
mPrev
Maybe UTCTime
mNow
Signed{signatures :: forall a. Signed a -> Signatures
signatures = Signatures [Signature]
sigs, a
signed :: forall a. Signed a -> a
signed :: a
..} =
forall e a. Except e a -> Either e a
runExcept Except VerificationError (SignaturesVerified a)
go
where
go :: Except VerificationError (SignaturesVerified a)
go :: Except VerificationError (SignaturesVerified a)
go = do
case Maybe UTCTime
mNow of
Just UTCTime
now ->
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (UTCTime -> FileExpires -> Bool
isExpired UTCTime
now (forall a s. LensLike' (Const a) s a -> s -> a
Lens.get forall a. HasHeader a => Lens' a FileExpires
fileExpires a
signed)) forall a b. (a -> b) -> a -> b
$
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorExpired TargetPath
targetPath
Maybe UTCTime
_otherwise ->
forall (m :: * -> *) a. Monad m => a -> m a
return ()
case Maybe FileVersion
mPrev of
Maybe FileVersion
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just FileVersion
prev ->
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (forall a s. LensLike' (Const a) s a -> s -> a
Lens.get forall a. HasHeader a => Lens' a FileVersion
fileVersion a
signed forall a. Ord a => a -> a -> Bool
< FileVersion
prev) forall a b. (a -> b) -> a -> b
$
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorVersion TargetPath
targetPath
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (forall (t :: * -> *) a. Foldable t => t a -> Int
length (forall a. (a -> Bool) -> [a] -> [a]
filter Signature -> Bool
isRoleSpecKey [Signature]
sigs) forall a. Ord a => a -> a -> Bool
>= forall a b. (Integral a, Num b) => a -> b
fromIntegral Int54
threshold) forall a b. (a -> b) -> a -> b
$
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorSignatures TargetPath
targetPath
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. a -> SignaturesVerified a
SignaturesVerified a
signed
isRoleSpecKey :: Signature -> Bool
isRoleSpecKey :: Signature -> Bool
isRoleSpecKey Signature{ByteString
Some PublicKey
signatureKey :: Signature -> Some PublicKey
signature :: Signature -> ByteString
signatureKey :: Some PublicKey
signature :: ByteString
..} = Some PublicKey
signatureKey forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Some PublicKey]
roleSpecKeys
verifyFingerprints :: [KeyId]
-> KeyThreshold
-> TargetPath
-> Signed Root
-> Either VerificationError (SignaturesVerified Root)
verifyFingerprints :: [KeyId]
-> KeyThreshold
-> TargetPath
-> Signed Root
-> Either VerificationError (SignaturesVerified Root)
verifyFingerprints [KeyId]
fingerprints
(KeyThreshold Int54
threshold)
TargetPath
targetPath
Signed{signatures :: forall a. Signed a -> Signatures
signatures = Signatures [Signature]
sigs, Root
signed :: Root
signed :: forall a. Signed a -> a
..} =
if forall (t :: * -> *) a. Foldable t => t a -> Int
length (forall a. (a -> Bool) -> [a] -> [a]
filter Signature -> Bool
isTrustedKey [Signature]
sigs) forall a. Ord a => a -> a -> Bool
>= forall a b. (Integral a, Num b) => a -> b
fromIntegral Int54
threshold
then forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ forall a. a -> SignaturesVerified a
SignaturesVerified Root
signed
else forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorSignatures TargetPath
targetPath
where
isTrustedKey :: Signature -> Bool
isTrustedKey :: Signature -> Bool
isTrustedKey Signature{ByteString
Some PublicKey
signatureKey :: Some PublicKey
signature :: ByteString
signatureKey :: Signature -> Some PublicKey
signature :: Signature -> ByteString
..} = forall (key :: * -> *). HasKeyId key => Some key -> KeyId
someKeyId Some PublicKey
signatureKey forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [KeyId]
fingerprints