HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Modulehackage-security-0.6.3.2Haskell2010

Hackage.Security.Trusted

  • 6 types
  • 1 class
  • 10 values
newtypenewtype Trusted a
#

Trusted values

Trusted values originate in only two ways:

  • Anything that is statically known is trusted (trustStatic)

  • If we have "dynamic" data we can trust it once we have verified the the signatures (trustSigned).

NOTE: Trusted is NOT a functor. If it was we could define

trustAnything :: a -> Trusted a
trustAnything a = fmap (const a) (trustStatic (static ()))

Consequently, it is neither a monad nor a comonad. However, we _can_ apply trusted functions to trusted arguments (trustApply).

The DeclareTrusted constructor is exported, but any use of it should be verified.

Instances2Eq, Show
  • Eq a => Eq (Trusted a)Defined in hackage-security-0.6.3.2 · Hackage.Security.Trusted.TCB
  • Show a => Show (Trusted a)Defined in hackage-security-0.6.3.2 · Hackage.Security.Trusted.TCB
datadata VerificationError
#

Errors thrown during role validation

Constructors

Instances3Show, Exception, Pretty
datadata RootUpdated
#

Root metadata updated (as part of the normal update process)

Instances3Show, Exception, Pretty
valuetrustApply :: Trusted (a -> b) -> Trusted a -> Trusted b
#

Equivalent of <*>

Trusted isn't quite applicative (no pure, not a functor), but it is somehow Applicative-like: we have the equivalent of <*>

valuetrustElems :: Traversable f => Trusted (f a) -> f (Trusted a)
#

Trust all elements of some trusted (traversable) container

If we have, say, a trusted list of values, we should be able to get a list of trusted values out of it.

trustElems :: Trusted [a] -> [Trusted a]

NOTE. It might appear that the more natural primitive to offer is a sequenceA-like operator such as

trustSeq :: Applicative f => Trusted (f a) -> f (Trusted a)

However, this is unsound. To see this, consider that ((->) a) is Applicative (it's the reader monad); hence, we can instantiate trustSeq at

trustSeq :: Trusted (a -> a) -> a -> Trusted a

and by passing trustStatic (static id) make Trusted a functor, which we certainly don't want to do (see comments for Trusted).

So why is it okay when we insist on Traversable rather than Applicative? To see this, it's instructive to consider how we might make a ((->) a) an instance of Traversable. If we define the domain of enumerable types as

class Eq a => Enumerable a where
  enumerate :: [a]

then we can make ((->) r) traversable by

instance Enumerable r => Traversable ((->) r) where
  sequenceA f = rebuild <$> sequenceA ((\r -> (r,) <$> f r) <$> enumerate)
    where
      rebuild :: [(r, a)] -> r -> a
      rebuild fun arg = fromJust (lookup arg fun)

The idea is that if the domain of a function is enumerable, we can apply the function to each possible input, collect the outputs, and construct a new function by pairing the inputs with the outputs. I.e., if we had something of type

a -> IO b

and a is enumerable, we just run the IO action on each possible a and collect all bs to get a pure function a -> b. Of course, you probably don't want to be doing that, but the point is that as far as the type system is concerned you could.

In the context of Trusted, this means that we can derive

enumPure :: Enumerable a => a -> Trusted a

but in a way this this makes sense anyway. If a domain is enumerable, it would not be unreasonable to change Enumerable to

class Eq a => Enumerable a where
  enumerate :: [StaticPtr a]

so we could define enumPure as

enumPure :: Enumerable a => a -> Trusted a
enumPure x = trustStatic
           $ fromJust (find ((== x) . deRefStaticPtr) enumerate)

In other words, we just enumerate the entire domain as trusted values (because we defined them locally) and then return the one that matched the untrusted value.

The conclusion from all of this is that the types of untrusted input (like the types of the TUF files we download from the server) should probably not be considered enumerable.

valueverifyRole'
  1. :: HasHeader a
  2. => Trusted (RoleSpec a)

    For signature validation

  3. -> TargetPath

    File source (for error messages)

  4. -> Maybe FileVersion

    Previous version (if available)

  5. -> Maybe UTCTime

    Time now (if checking expiry)

  6. -> Signed a
  7. -> Either VerificationError (SignaturesVerified a)
#

Role verification

NOTE: We throw an error when the version number _decreases_, but allow it to be the same. This is sufficient: the file number is there so that attackers cannot replay old files. It cannot protect against freeze attacks (that's what the expiry date is for), so "replaying" the same file is not a problem. If an attacker changes the contents of the file but not the version number we have an inconsistent situation, but this is not something we need to worry about: in this case the attacker will need to resign the file or otherwise the signature won't match, and if the attacker has compromised the key then he might just as well increase the version number and resign.

NOTE 2: We are not actually verifying the signatures _themselves_ here (we did that when we parsed the JSON). We are merely verifying the provenance of the keys.

datadata StaticPtr a
#

A reference to a value of type a.

Instances1IsStatic

Derived functions

1 declaration

Role verification

classclass VerifyRole a where
#
Instances4VerifyRole

File info verification