hackage-security-0.6.2.6: Hackage security library

Safe Haskell | None
Language | Haskell2010

Hackage.Security.Trusted

Contents

- Derived functions
  - Role verification
  - File info verification

Synopsis

- data Trusted a
- data StaticPtr a
- data VerificationError
  - = VerificationErrorSignatures TargetPath Integer [KeyId] [KeyId]
  - | VerificationErrorExpired TargetPath
  - | VerificationErrorVersion TargetPath
  - | VerificationErrorFileInfo TargetPath
  - | VerificationErrorUnknownTarget TargetPath
  - | VerificationErrorMissingSHA256 TargetPath
  - | VerificationErrorDeserialization TargetPath DeserializationError
  - | VerificationErrorLoop VerificationHistory
- type VerificationHistory = [Either RootUpdated VerificationError]
- data RootUpdated = RootUpdated
- trustStatic :: StaticPtr a -> Trusted a
- trustVerified :: SignaturesVerified a -> Trusted a
- trustApply :: Trusted (a -> b) -> Trusted a -> Trusted b
- trustElems :: Traversable f => Trusted (f a) -> f (Trusted a)
- data SignaturesVerified a
- verifyRole' :: HasHeader a => Trusted (RoleSpec a) -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed a -> Either VerificationError (SignaturesVerified a)
- verifyFingerprints :: [KeyId] -> KeyThreshold -> TargetPath -> Signed Root -> Either VerificationError (SignaturesVerified Root)
- (<$$>) :: StaticPtr (a -> b) -> Trusted a -> Trusted b
- class VerifyRole a where
  - verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed a -> Either VerificationError (SignaturesVerified a)
- trustedFileInfoEqual :: Trusted FileInfo -> Trusted FileInfo -> Bool

# Documentation

data 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.

#### Instances

Instances details

Show a => Show (Trusted a) # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods showsPrec :: Int -> Trusted a -> ShowS # show :: Trusted a -> String # showList :: [Trusted a] -> ShowS #
Eq a => Eq (Trusted a) # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods (==) :: Trusted a -> Trusted a -> Bool # (/=) :: Trusted a -> Trusted a -> Bool #

data StaticPtr a #

A reference to a value of type `a`.

#### Instances

Instances details

IsStatic StaticPtr | @since base-4.9.0.0
Instance details Defined in GHC.Internal.StaticPtr Methods fromStaticPtr :: Typeable a => StaticPtr a -> StaticPtr a #

data VerificationError #

Errors thrown during role validation

Constructors

VerificationErrorSignatures TargetPath Integer [KeyId] [KeyId] | Not enough signatures signed with the appropriate keys
VerificationErrorExpired TargetPath | The file is expired
VerificationErrorVersion TargetPath | The file version is less than the previous version
VerificationErrorFileInfo TargetPath | File information mismatch
VerificationErrorUnknownTarget TargetPath | We tried to lookup file information about a particular target file, but the information wasn't in the corresponding `targets.json` file.
VerificationErrorMissingSHA256 TargetPath | The metadata for the specified target is missing a SHA256
VerificationErrorDeserialization TargetPath DeserializationError | Some verification errors materialize as deserialization errors For example: if we try to deserialize a timestamp file but the timestamp key has been rolled over, deserialization of the file will fail with `DeserializationErrorUnknownKey`.
VerificationErrorLoop VerificationHistory | The spec stipulates that if a verification error occurs during the check for updates, we must download new root information and start over. However, we limit how often we attempt this. We record all verification errors that occurred before we gave up.

#### Instances

Instances details

Exception VerificationError # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods toException :: VerificationError -> SomeException # fromException :: SomeException -> Maybe VerificationError # displayException :: VerificationError -> String # backtraceDesired :: VerificationError -> Bool #
Show VerificationError # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods showsPrec :: Int -> VerificationError -> ShowS # show :: VerificationError -> String # showList :: [VerificationError] -> ShowS #
Pretty VerificationError # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods pretty :: VerificationError -> String #

type VerificationHistory = [Either RootUpdated VerificationError] #

data RootUpdated #

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

Constructors

RootUpdated |

#### Instances

Instances details

Exception RootUpdated # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods toException :: RootUpdated -> SomeException # fromException :: SomeException -> Maybe RootUpdated # displayException :: RootUpdated -> String # backtraceDesired :: RootUpdated -> Bool #
Show RootUpdated # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods showsPrec :: Int -> RootUpdated -> ShowS # show :: RootUpdated -> String # showList :: [RootUpdated] -> ShowS #
Pretty RootUpdated # |
Instance details Defined in Hackage.Security.Trusted.TCB Methods pretty :: RootUpdated -> String #

trustStatic :: StaticPtr a -> Trusted a #

trustVerified :: SignaturesVerified a -> Trusted a #

trustApply :: 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 `<*>`

trustElems :: 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 `b`s 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.

data SignaturesVerified a #

verifyRole' #

Arguments

:: HasHeader a |
=> Trusted (RoleSpec a) | For signature validation
-> TargetPath | File source (for error messages)
-> Maybe FileVersion | Previous version (if available)
-> Maybe UTCTime | Time now (if checking expiry)
-> Signed a |
-> 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.

verifyFingerprints #

Arguments

:: [KeyId] |
-> KeyThreshold |
-> TargetPath | For error messages
-> Signed Root |
-> Either VerificationError (SignaturesVerified Root) |

Variation on `verifyRole` that uses key IDs rather than keys

This is used during the bootstrap process.

See http://en.wikipedia.org/wiki/Public_key_fingerprint.

# Derived functions

(<$$>) :: StaticPtr (a -> b) -> Trusted a -> Trusted b #

Apply a static function to a trusted argument

## Role verification

class VerifyRole a where #

Methods

verifyRole #

Arguments

:: Trusted Root | Root data
-> TargetPath | Source (for error messages)
-> Maybe FileVersion | Previous version (if available)
-> Maybe UTCTime | Time now (if checking expiry)
-> Signed a | Mirrors to verify
-> Either VerificationError (SignaturesVerified a) |

#### Instances

Instances details

VerifyRole Mirrors # |
Instance details Defined in Hackage.Security.Trusted Methods verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Mirrors -> Either VerificationError (SignaturesVerified Mirrors) #
VerifyRole Root # |
Instance details Defined in Hackage.Security.Trusted Methods verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Root -> Either VerificationError (SignaturesVerified Root) #
VerifyRole Snapshot # |
Instance details Defined in Hackage.Security.Trusted Methods verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Snapshot -> Either VerificationError (SignaturesVerified Snapshot) #
VerifyRole Timestamp # |
Instance details Defined in Hackage.Security.Trusted Methods verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Timestamp -> Either VerificationError (SignaturesVerified Timestamp) #

## File info verification

trustedFileInfoEqual :: Trusted FileInfo -> Trusted FileInfo -> Bool #

Variation on `knownFileInfoEqual` for `Trusted` `FileInfo`