{-# 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.Except
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 { Trusted a -> a
trusted :: a }
deriving (Trusted a -> Trusted a -> Bool
(Trusted a -> Trusted a -> Bool)
-> (Trusted a -> Trusted a -> Bool) -> Eq (Trusted a)
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
[Trusted a] -> ShowS
Trusted a -> String
(Int -> Trusted a -> ShowS)
-> (Trusted a -> String)
-> ([Trusted a] -> ShowS)
-> Show (Trusted a)
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 :: StaticPtr a -> Trusted a
trustStatic = a -> Trusted a
forall a. a -> Trusted a
DeclareTrusted (a -> Trusted a) -> (StaticPtr a -> a) -> StaticPtr a -> Trusted a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StaticPtr a -> a
forall a. StaticPtr a -> a
deRefStaticPtr
trustVerified :: SignaturesVerified a -> Trusted a
trustVerified :: SignaturesVerified a -> Trusted a
trustVerified = a -> Trusted a
forall a. a -> Trusted a
DeclareTrusted (a -> Trusted a)
-> (SignaturesVerified a -> a) -> SignaturesVerified a -> Trusted a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SignaturesVerified a -> a
forall a. SignaturesVerified a -> a
signaturesVerified
trustApply :: Trusted (a -> b) -> Trusted a -> Trusted b
trustApply :: Trusted (a -> b) -> Trusted a -> Trusted b
trustApply (DeclareTrusted a -> b
f) (DeclareTrusted a
x) = b -> Trusted b
forall a. a -> Trusted a
DeclareTrusted (a -> b
f a
x)
trustElems :: Traversable f => Trusted (f a) -> f (Trusted a)
trustElems :: Trusted (f a) -> f (Trusted a)
trustElems (DeclareTrusted f a
fa) = a -> Trusted a
forall a. a -> Trusted a
DeclareTrusted (a -> Trusted a) -> f a -> f (Trusted a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` f a
fa
newtype SignaturesVerified a = SignaturesVerified { 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 = VerificationError -> String
forall a. Pretty a => a -> String
pretty
instance Exception RootUpdated where displayException :: RootUpdated -> String
displayException = RootUpdated -> String
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) =
TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" does not have enough signatures signed with the appropriate keys"
pretty (VerificationErrorExpired TargetPath
file) =
TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" is expired"
pretty (VerificationErrorVersion TargetPath
file) =
String
"Version of " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" is less than the previous version"
pretty (VerificationErrorFileInfo TargetPath
file) =
String
"Invalid hash for " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file
pretty (VerificationErrorUnknownTarget TargetPath
file) =
TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" not found in corresponding target metadata"
pretty (VerificationErrorMissingSHA256 TargetPath
file) =
String
"Missing SHA256 hash for " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file
pretty (VerificationErrorDeserialization TargetPath
file DeserializationError
err) =
String
"Could not deserialize " String -> ShowS
forall a. [a] -> [a] -> [a]
++ TargetPath -> String
forall a. Pretty a => a -> String
pretty TargetPath
file String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
": " String -> ShowS
forall a. [a] -> [a] -> [a]
++ DeserializationError -> String
forall a. Pretty a => a -> String
pretty DeserializationError
err
pretty (VerificationErrorLoop VerificationHistory
es) =
String
"Verification loop. Errors in order:\n"
String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
unlines ((Either RootUpdated VerificationError -> String)
-> VerificationHistory -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ((String
" " String -> ShowS
forall a. [a] -> [a] -> [a]
++) ShowS
-> (Either RootUpdated VerificationError -> String)
-> Either RootUpdated VerificationError
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (RootUpdated -> String)
-> (VerificationError -> String)
-> Either RootUpdated VerificationError
-> String
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either RootUpdated -> String
forall a. Pretty a => a -> String
pretty VerificationError -> String
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' :: Trusted (RoleSpec a)
-> TargetPath
-> Maybe FileVersion
-> Maybe UTCTime
-> Signed a
-> Either VerificationError (SignaturesVerified a)
verifyRole' (Trusted (RoleSpec a) -> RoleSpec a
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
..} =
Except VerificationError (SignaturesVerified a)
-> Either VerificationError (SignaturesVerified 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 ->
Bool
-> ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (UTCTime -> FileExpires -> Bool
isExpired UTCTime
now (LensLike' (Const FileExpires) a FileExpires -> a -> FileExpires
forall a s. LensLike' (Const a) s a -> s -> a
Lens.get LensLike' (Const FileExpires) a FileExpires
forall a. HasHeader a => Lens' a FileExpires
fileExpires a
signed)) (ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ())
-> ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ()
forall a b. (a -> b) -> a -> b
$
VerificationError -> ExceptT VerificationError Identity ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (VerificationError -> ExceptT VerificationError Identity ())
-> VerificationError -> ExceptT VerificationError Identity ()
forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorExpired TargetPath
targetPath
Maybe UTCTime
_otherwise ->
() -> ExceptT VerificationError Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
case Maybe FileVersion
mPrev of
Maybe FileVersion
Nothing -> () -> ExceptT VerificationError Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just FileVersion
prev ->
Bool
-> ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (LensLike' (Const FileVersion) a FileVersion -> a -> FileVersion
forall a s. LensLike' (Const a) s a -> s -> a
Lens.get LensLike' (Const FileVersion) a FileVersion
forall a. HasHeader a => Lens' a FileVersion
fileVersion a
signed FileVersion -> FileVersion -> Bool
forall a. Ord a => a -> a -> Bool
< FileVersion
prev) (ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ())
-> ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ()
forall a b. (a -> b) -> a -> b
$
VerificationError -> ExceptT VerificationError Identity ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (VerificationError -> ExceptT VerificationError Identity ())
-> VerificationError -> ExceptT VerificationError Identity ()
forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorVersion TargetPath
targetPath
Bool
-> ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([Signature] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Signature -> Bool) -> [Signature] -> [Signature]
forall a. (a -> Bool) -> [a] -> [a]
filter Signature -> Bool
isRoleSpecKey [Signature]
sigs) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int54 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int54
threshold) (ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ())
-> ExceptT VerificationError Identity ()
-> ExceptT VerificationError Identity ()
forall a b. (a -> b) -> a -> b
$
VerificationError -> ExceptT VerificationError Identity ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (VerificationError -> ExceptT VerificationError Identity ())
-> VerificationError -> ExceptT VerificationError Identity ()
forall a b. (a -> b) -> a -> b
$ TargetPath -> VerificationError
VerificationErrorSignatures TargetPath
targetPath
SignaturesVerified a
-> Except VerificationError (SignaturesVerified a)
forall (m :: * -> *) a. Monad m => a -> m a
return (SignaturesVerified a
-> Except VerificationError (SignaturesVerified a))
-> SignaturesVerified a
-> Except VerificationError (SignaturesVerified a)
forall a b. (a -> b) -> a -> b
$ a -> SignaturesVerified a
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 Some PublicKey -> [Some PublicKey] -> Bool
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 [Signature] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Signature -> Bool) -> [Signature] -> [Signature]
forall a. (a -> Bool) -> [a] -> [a]
filter Signature -> Bool
isTrustedKey [Signature]
sigs) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int54 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int54
threshold
then SignaturesVerified Root
-> Either VerificationError (SignaturesVerified Root)
forall a b. b -> Either a b
Right (SignaturesVerified Root
-> Either VerificationError (SignaturesVerified Root))
-> SignaturesVerified Root
-> Either VerificationError (SignaturesVerified Root)
forall a b. (a -> b) -> a -> b
$ Root -> SignaturesVerified Root
forall a. a -> SignaturesVerified a
SignaturesVerified Root
signed
else VerificationError
-> Either VerificationError (SignaturesVerified Root)
forall a b. a -> Either a b
Left (VerificationError
-> Either VerificationError (SignaturesVerified Root))
-> VerificationError
-> Either VerificationError (SignaturesVerified Root)
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
..} = Some PublicKey -> KeyId
forall (key :: * -> *). HasKeyId key => Some key -> KeyId
someKeyId Some PublicKey
signatureKey KeyId -> [KeyId] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [KeyId]
fingerprints