{-# 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