Safe Haskell | None |
---|---|
Language | Haskell2010 |
- Checking for updates
- Downloading targets
- Bootstrapping
- Re-exports
- Types
- Utility
- Repository layout
- Index tarball layout
- Cache layout
- TUF types
- Patterns and replacements
- Datatypes
- TUF types
- Construction and verification
- JSON aids
- Avoid interpreting signatures
- TUF types
- Key types
- Types abstracting over key types
- Key types in isolation
- Hiding key types
- Operations on keys
- Key IDs
- Signing
- Exceptions
Main entry point into the Hackage Security framework for clients
- checkForUpdates :: (Throws VerificationError, Throws SomeRemoteError) => Repository -> CheckExpiry -> IO HasUpdates
- data CheckExpiry
- data HasUpdates
- downloadPackage :: (Throws SomeRemoteError, Throws VerificationError, Throws InvalidPackageException) => Repository -> PackageIdentifier -> (TempPath -> IO a) -> IO a
- getCabalFile :: Throws InvalidPackageException => Repository -> PackageIdentifier -> IO ByteString
- requiresBootstrap :: Repository -> IO Bool
- bootstrap :: (Throws SomeRemoteError, Throws VerificationError) => Repository -> [KeyId] -> KeyThreshold -> IO ()
- newtype FileLength = FileLength {
- fileLength :: Int
- newtype Hash = Hash String
- newtype KeyThreshold = KeyThreshold Int
- data FileInfo = FileInfo {}
- data HashFn = HashFnSHA256
- newtype Hash = Hash String
- fileInfo :: ByteString -> FileInfo
- computeFileInfo :: IsFileSystemRoot root => Path (Rooted root) -> IO FileInfo
- knownFileInfoEqual :: FileInfo -> FileInfo -> Bool
- module Hackage.Security.TUF.FileMap
- class HasHeader a where
- fileExpires :: Lens' a FileExpires
- fileVersion :: Lens' a FileVersion
- newtype FileVersion = FileVersion Int
- newtype FileExpires = FileExpires (Maybe UTCTime)
- data Header = Header {}
- expiresInDays :: UTCTime -> Integer -> FileExpires
- expiresNever :: FileExpires
- isExpired :: UTCTime -> FileExpires -> Bool
- versionInitial :: FileVersion
- versionIncrement :: FileVersion -> FileVersion
- data RepoRoot
- type RepoPath = Path (Rooted RepoRoot)
- data RepoLayout = RepoLayout {}
- hackageRepoLayout :: RepoLayout
- cabalLocalRepoLayout :: RepoLayout
- anchorRepoPathLocally :: IsFileSystemRoot root => Path (Rooted root) -> RepoPath -> Path (Rooted root)
- anchorRepoPathRemotely :: URIPath -> RepoPath -> URIPath
- data IndexRoot
- type IndexPath = Path (Rooted RepoRoot)
- data IndexLayout = IndexLayout {}
- hackageIndexLayout :: IndexLayout
- data CacheRoot
- type CachePath = Path (Rooted CacheRoot)
- data CacheLayout = CacheLayout {}
- cabalCacheLayout :: CacheLayout
- anchorCachePath :: IsFileSystemRoot root => Path (Rooted root) -> CachePath -> Path (Rooted root)
- data Mirrors = Mirrors {}
- data Mirror = Mirror {}
- data MirrorContent = MirrorFull
- type MirrorDescription = String
- describeMirror :: Mirror -> MirrorDescription
- type FileName = String
- type Directory = String
- type Extension = String
- type BaseName = String
- data Pattern a where
- PatFileConst :: FileName -> Pattern ()
- PatFileExt :: Extension -> Pattern (BaseName :- ())
- PatFileAny :: Pattern (FileName :- ())
- PatDirConst :: Directory -> Pattern a -> Pattern a
- PatDirAny :: Pattern a -> Pattern (Directory :- a)
- data Replacement a where
- RepFileConst :: FileName -> Replacement a
- RepFileExt :: Extension -> Replacement (BaseName :- a)
- RepFileAny :: Replacement (FileName :- a)
- RepDirConst :: Directory -> Replacement a -> Replacement a
- RepDirAny :: Replacement a -> Replacement (Directory :- a)
- data Delegation = forall a . Delegation (Pattern a) (Replacement a)
- identityReplacement :: Pattern typ -> Replacement typ
- matchDelegation :: Delegation -> String -> Maybe String
- parseDelegation :: String -> String -> Either String Delegation
- qqd :: String -> String -> Q Exp
- data Root = Root {}
- data RootRoles = RootRoles {}
- data RoleSpec a = RoleSpec {}
- data Signed a = Signed {
- signed :: a
- signatures :: Signatures
- newtype Signatures = Signatures [Signature]
- data Signature = Signature {}
- unsigned :: a -> Signed a
- withSignatures :: ToJSON WriteJSON a => RepoLayout -> [Some Key] -> a -> Signed a
- withSignatures' :: ToJSON Identity a => [Some Key] -> a -> Signed a
- signRendered :: [Some Key] -> ByteString -> Signatures
- verifySignature :: ByteString -> Signature -> Bool
- signedFromJSON :: (MonadKeys m, FromJSON m a) => JSValue -> m (Signed a)
- verifySignatures :: JSValue -> Signatures -> Bool
- data UninterpretedSignatures a = UninterpretedSignatures {}
- data PreSignature = PreSignature {}
- fromPreSignature :: MonadKeys m => PreSignature -> m Signature
- fromPreSignatures :: MonadKeys m => [PreSignature] -> m Signatures
- toPreSignature :: Signature -> PreSignature
- toPreSignatures :: Signatures -> [PreSignature]
- data Snapshot = Snapshot {}
- data Targets = Targets {}
- data Delegations = Delegations {}
- data DelegationSpec = DelegationSpec {}
- data Delegation = forall a . Delegation (Pattern a) (Replacement a)
- targetsLookup :: TargetPath -> Targets -> Maybe FileInfo
- data Timestamp = Timestamp {}
- data Ed25519
- data Key a where
- KeyEd25519 :: PublicKey -> SecretKey -> Key Ed25519
- data PublicKey a where
- data PrivateKey a where
- data KeyType typ where
- somePublicKey :: Some Key -> Some PublicKey
- somePublicKeyType :: Some PublicKey -> Some KeyType
- someKeyId :: HasKeyId key => Some key -> KeyId
- publicKey :: Key a -> PublicKey a
- privateKey :: Key a -> PrivateKey a
- createKey :: KeyType key -> IO (Key key)
- createKey' :: KeyType key -> IO (Some Key)
- newtype KeyId = KeyId {}
- class HasKeyId key where
- sign :: PrivateKey typ -> ByteString -> ByteString
- verify :: PublicKey typ -> ByteString -> ByteString -> Bool
- data Repository
- data SomeRemoteError :: * where
- SomeRemoteError :: Exception e => e -> SomeRemoteError
- data LogMessage
- uncheckClientErrors :: ((Throws VerificationError, Throws SomeRemoteError, Throws InvalidPackageException) => IO a) -> IO a
- data VerificationError
- data InvalidPackageException = InvalidPackageException PackageIdentifier
- data InvalidFileInIndex = InvalidFileInIndex IndexFile DeserializationError
- data LocalFileCorrupted = LocalFileCorrupted DeserializationError
Checking for updates
checkForUpdates :: (Throws VerificationError, Throws SomeRemoteError) => Repository -> CheckExpiry -> IO HasUpdates Source
Generic logic for checking if there are updates
This implements the logic described in Section 5.1, "The client application", of the TUF spec. It checks which of the server metadata has changed, and downloads all changed metadata to the local cache. (Metadata here refers both to the TUF security metadata as well as the Hackage packge index.)
data CheckExpiry Source
Should we check expiry dates?
CheckExpiry | Yes, check expiry dates |
DontCheckExpiry | No, don't check expiry dates. This should ONLY be used in exceptional circumstances (such as when the main server is down for longer than the expiry dates used in the timestamp files on mirrors). |
Downloading targets
downloadPackage :: (Throws SomeRemoteError, Throws VerificationError, Throws InvalidPackageException) => Repository -> PackageIdentifier -> (TempPath -> IO a) -> IO a Source
Download a package
It is the responsibility of the callback to move the package from its temporary location to a permanent location (if desired). The callback will only be invoked once the chain of trust has been verified.
NOTE: Unlike the check for updates, downloading a package never triggers an update of the root information (even if verification of the package fails).
getCabalFile :: Throws InvalidPackageException => Repository -> PackageIdentifier -> IO ByteString Source
Get a cabal file from the index
This does currently not do any verification (bcause the cabal file comes from the index, and the index itself is verified). Once we introduce author signing this needs to be adapted.
Should be called only once a local index is available
(i.e., after checkForUpdates
).
Throws an InvalidPackageException
if there is no cabal file for the
specified package in the index.
Bootstrapping
requiresBootstrap :: Repository -> IO Bool Source
Check if we need to bootstrap (i.e., if we have root info)
bootstrap :: (Throws SomeRemoteError, Throws VerificationError) => Repository -> [KeyId] -> KeyThreshold -> IO () Source
Bootstrap the chain of trust
New clients might need to obtain a copy of the root metadata. This however represents a chicken-and-egg problem: how can we verify the root metadata we downloaded? The only possibility is to be provided with a set of an out-of-band set of root keys and an appropriate threshold.
Clients who provide a threshold of 0 can do an initial "unsafe" update of the root information, if they wish.
The downloaded root information will _only_ be verified against the
provided keys, and _not_ against previously downloaded root info (if any).
It is the responsibility of the client to call bootstrap
only when this
is the desired behaviour.
Re-exports
Types
newtype FileLength Source
File length
Having verified file length information means we can protect against endless data attacks and similar.
Eq FileLength | |
Ord FileLength | |
Show FileLength | |
ReportSchemaErrors m => FromJSON m FileLength | |
Monad m => ToJSON m FileLength |
File hash
newtype KeyThreshold Source
Key threshold
The key threshold is the minimum number of keys a document must be signed
with. Key thresholds are specified in RoleSpec
or DelegationsSpec
.
File information
This intentionally does not have an Eq
instance; see knownFileInfoEqual
and verifyFileInfo
instead.
NOTE: Throughout we compute file information always over the raw bytes.
For example, when timestamp.json
lists the hash of snapshot.json
, this
hash is computed over the actual snapshot.json
file (as opposed to the
canonical form of the embedded JSON). This brings it in line with the hash
computed over target files, where that is the only choice available.
Eq HashFn | |
Ord HashFn | |
Show HashFn | |
ReportSchemaErrors m => FromObjectKey m HashFn | |
Monad m => ToObjectKey m HashFn |
File hash
Utility
fileInfo :: ByteString -> FileInfo Source
Compute FileInfo
TODO: Currently this will load the entire input bytestring into memory. We need to make this incremental, by computing the length and all hashes in a single traversal over the input. However, the precise way to do that will depend on the hashing package we will use, and we have yet to pick that package.
computeFileInfo :: IsFileSystemRoot root => Path (Rooted root) -> IO FileInfo Source
Compute FileInfo
knownFileInfoEqual :: FileInfo -> FileInfo -> Bool Source
Compare known file info
This should be used only when the FileInfo is already known. If we want to
compare known FileInfo against a file on disk we should delay until we know
have confirmed that the file lengths don't match (see verifyFileInfo
).
module Hackage.Security.TUF.FileMap
class HasHeader a where Source
fileExpires :: Lens' a FileExpires Source
File expiry date
fileVersion :: Lens' a FileVersion Source
File version (monotonically increasing counter)
newtype FileVersion Source
newtype FileExpires Source
File expiry date
A Nothing
value here means no expiry. That makes it possible to set some
files to never expire. (Note that not having the Maybe in the type here still
allows that, because you could set an expiry date 2000 years into the future.
By having the Maybe here we avoid the _need_ for such encoding issues.)
Utility
expiresInDays :: UTCTime -> Integer -> FileExpires Source
isExpired :: UTCTime -> FileExpires -> Bool Source
Repository layout
The root of the repository
Repository roots can be anchored at a remote URL or a local directory.
Note that even for remote repos RepoRoot
is (potentially) different from
WebRoot
-- for a repository located at, say, http://hackage.haskell.org
they happen to coincide, but for one location at
http://example.com/some/subdirectory
they do not.
data RepoLayout Source
Layout of a repository
RepoLayout | |
|
hackageRepoLayout :: RepoLayout Source
The layout used on Hackage
cabalLocalRepoLayout :: RepoLayout Source
Layout used by cabal for ("legacy") local repos
Obviously, such repos do not normally contain any of the TUF files, so their location is more or less arbitrary here.
anchorRepoPathLocally :: IsFileSystemRoot root => Path (Rooted root) -> RepoPath -> Path (Rooted root) Source
anchorRepoPathRemotely :: URIPath -> RepoPath -> URIPath Source
Index tarball layout
data IndexLayout Source
Layout of the files within the index tarball
IndexLayout | |
|
hackageIndexLayout :: IndexLayout Source
The layout of the index as maintained on Hackage
Cache layout
data CacheLayout Source
Location of the various files we cache
Although the generic TUF algorithms do not care how we organize the cache,
we nonetheless specity this here because as long as there are tools which
access files in the cache directly we need to define the cache layout.
See also comments for defaultCacheLayout
.
CacheLayout | |
|
cabalCacheLayout :: CacheLayout Source
The cache layout cabal-install uses
We cache the index as cache/00-index.tar
; this is important because
`cabal-install` expects to find it there (and does not currently go through
the hackage-security library to get files from the index).
anchorCachePath :: IsFileSystemRoot root => Path (Rooted root) -> CachePath -> Path (Rooted root) Source
Anchor a cache path to the location of the cache
TUF types
HasHeader Mirrors | |
(MonadError DeserializationError m, ReportSchemaErrors m) => FromJSON m Mirrors | |
Monad m => ToJSON m Mirrors | |
MonadKeys m => FromJSON m (Signed Mirrors) |
Definition of a mirror
NOTE: Unlike the TUF specification, we require that all mirrors must have
the same format. That is, we omit metapath
and targetspath
.
data MirrorContent Source
Full versus partial mirrors
The TUF spec explicitly allows for partial mirrors, with the mirrors file specifying (through patterns) what is available from partial mirrors.
For now we only support full mirrors; if we wanted to add partial mirrors,
we would add a second MirrorPartial
constructor here with arguments
corresponding to TUF's metacontent
and targetscontent
fields.
Utility
type MirrorDescription = String Source
describeMirror :: Mirror -> MirrorDescription Source
Give a human-readable description of a particular mirror
(for use in error messages)
Patterns and replacements
Structured patterns over paths
The type argument indicates what kind of function we expect when the
pattern matches. For example, we have the pattern "*/*.txt"
:
PathPatternDirAny (PathPatternFileExt ".txt") :: PathPattern (Directory :- BaseName :- ())
TODOs (see README.md):
- Update this to work with
Path
rather than 'FilePath'/'String' - Add different kinds of wildcards
- Add path roots
Currently this is a proof of concept more than anything else; the right structure is here, but it needs updating. However, until we add author signing (or out-of-tarball targets) we don't actually use this yet.
NOTE: Haddock lacks GADT support so constructors have only regular comments.
PatFileConst :: FileName -> Pattern () | |
PatFileExt :: Extension -> Pattern (BaseName :- ()) | |
PatFileAny :: Pattern (FileName :- ()) | |
PatDirConst :: Directory -> Pattern a -> Pattern a | |
PatDirAny :: Pattern a -> Pattern (Directory :- a) |
data Replacement a where Source
Replacement patterns
These constructors match the ones in Pattern
: wildcards must be used
in the same order as they appear in the pattern, but they don't all have to
be used (that's why the base constructors are polymorphic in the stack tail).
RepFileConst :: FileName -> Replacement a | |
RepFileExt :: Extension -> Replacement (BaseName :- a) | |
RepFileAny :: Replacement (FileName :- a) | |
RepDirConst :: Directory -> Replacement a -> Replacement a | |
RepDirAny :: Replacement a -> Replacement (Directory :- a) |
ReportSchemaErrors m => FromJSON m (Some Replacement) | |
Monad m => ToJSON m (Some Replacement) | |
Monad m => ToJSON m (Replacement typ) | |
Eq (Replacement typ) | |
Show (Replacement typ) | |
Lift (Replacement a) |
data Delegation Source
forall a . Delegation (Pattern a) (Replacement a) |
Utility
identityReplacement :: Pattern typ -> Replacement typ Source
The identity replacement replaces a matched pattern with itself
matchDelegation :: Delegation -> String -> Maybe String Source
Parsing and quasi-quoting
parseDelegation :: String -> String -> Either String Delegation Source
qqd :: String -> String -> Q Exp Source
Quasi-quoter for delegations to make them easier to write in code
This allows to write delegations as
$(qqd "targets/*/*/*.cabal" "targets/*/*/revisions.json")
(The alternative syntax which actually uses a quasi-quoter doesn't work very
well because the /*
bits confuse CPP: "unterminated comment")
Datatypes
The root metadata
NOTE: We must have the invariant that ALL keys (apart from delegation keys)
must be listed in rootKeys
. (Delegation keys satisfy a similar invariant,
see Targets.)
Root | |
|
Role specification
The phantom type indicates what kind of type this role is meant to verify.
TUF types
Signed | |
|
MonadKeys m => FromJSON m (Signed Mirrors) | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Snapshot) | |
MonadKeys m => FromJSON m (Signed Targets) | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Timestamp) | |
MonadKeys m => FromJSON m (Signed Root) | We give an instance for Signed Root rather than Root because the key environment from the root data is necessary to resolve the explicit sharing in the signatures. |
(Monad m, ToJSON m a) => ToJSON m (Signed a) |
newtype Signatures Source
A list of signatures
Invariant: each signature must be made with a different key.
We enforce this invariant for incoming untrusted data (fromPreSignatures
)
but not for lists of signatures that we create in code.
MonadKeys m => FromJSON m Signatures | |
Monad m => ToJSON m Signatures |
Construction and verification
withSignatures :: ToJSON WriteJSON a => RepoLayout -> [Some Key] -> a -> Signed a Source
Sign a document
withSignatures' :: ToJSON Identity a => [Some Key] -> a -> Signed a Source
Variation on withSignatures
that doesn't need the repo layout
signRendered :: [Some Key] -> ByteString -> Signatures Source
Construct signatures for already rendered value
verifySignature :: ByteString -> Signature -> Bool Source
JSON aids
signedFromJSON :: (MonadKeys m, FromJSON m a) => JSValue -> m (Signed a) Source
General FromJSON instance for signed datatypes
We don't give a general FromJSON instance for Signed because for some datatypes we need to do something special (datatypes where we need to read key environments); for instance, see the "Signed Root" instance.
verifySignatures :: JSValue -> Signatures -> Bool Source
Signature verification
NOTES: 1. By definition, the signature must be verified against the canonical JSON format. This means we _must_ parse and then pretty print (as we do here) because the document as stored may or may not be in canonical format. 2. However, it is important that we NOT translate from the JSValue to whatever internal datatype we are using and then back to JSValue, because that may not roundtrip: we must allow for additional fields in the JSValue that we ignore (and would therefore lose when we attempt to roundtrip). 3. We verify that all signatures are valid, but we cannot verify (here) that these signatures are signed with the right key, or that we have a sufficient number of signatures. This will be the responsibility of the calling code.
Avoid interpreting signatures
data UninterpretedSignatures a Source
File with uninterpreted signatures
Sometimes we want to be able to read a file without interpreting the signatures (that is, resolving the key IDs) or doing any kind of checks on them. One advantage of this is that this allows us to read many file types without any key environment at all, which is sometimes useful.
(ReportSchemaErrors m, FromJSON m a) => FromJSON m (UninterpretedSignatures a) | |
(Monad m, ToJSON m a) => ToJSON m (UninterpretedSignatures a) | |
Show a => Show (UninterpretedSignatures a) |
data PreSignature Source
A signature with a key ID (rather than an actual key)
This corresponds precisely to the TUF representation of a signature.
Show PreSignature | |
ReportSchemaErrors m => FromJSON m PreSignature | |
Monad m => ToJSON m PreSignature |
Utility
fromPreSignature :: MonadKeys m => PreSignature -> m Signature Source
Convert a pre-signature to a signature
Verifies that the key type matches the advertised method.
fromPreSignatures :: MonadKeys m => [PreSignature] -> m Signatures Source
Convert a list of PreSignature
s to a list of Signature
s
This verifies the invariant that all signatures are made with different keys. We do this on the presignatures rather than the signatures so that we can do the check on key IDs, rather than keys (the latter don't have an Ord instance).
toPreSignature :: Signature -> PreSignature Source
Convert signature to pre-signature
toPreSignatures :: Signatures -> [PreSignature] Source
Convert list of pre-signatures to a list of signatures
Snapshot | |
|
HasHeader Snapshot | |
(MonadReader RepoLayout m, MonadError DeserializationError m, ReportSchemaErrors m) => FromJSON m Snapshot | |
MonadReader RepoLayout m => ToJSON m Snapshot | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Snapshot) |
TUF types
Target metadata
Most target files do not need expiry dates because they are not subject to change (and hence attacks like freeze attacks are not a concern).
data Delegations Source
Delegations
Much like the Root datatype, this must have an invariant that ALL used keys
(apart from the global keys, which are in the root key environment) must
be listed in delegationsKeys
.
Show Delegations | |
MonadKeys m => FromJSON m Delegations | |
Monad m => ToJSON m Delegations |
data DelegationSpec Source
Delegation specification
NOTE: This is a close analogue of RoleSpec
.
Show DelegationSpec | |
MonadKeys m => FromJSON m DelegationSpec | |
Monad m => ToJSON m DelegationSpec |
data Delegation Source
forall a . Delegation (Pattern a) (Replacement a) |
Util
targetsLookup :: TargetPath -> Targets -> Maybe FileInfo Source
HasHeader Timestamp | |
(MonadReader RepoLayout m, MonadError DeserializationError m, ReportSchemaErrors m) => FromJSON m Timestamp | |
MonadReader RepoLayout m => ToJSON m Timestamp | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Timestamp) |
Key types
Types abstracting over key types
KeyEd25519 :: PublicKey -> SecretKey -> Key Ed25519 |
data PrivateKey a where Source
SomeShow PrivateKey | |
SomeEq PrivateKey | |
Eq (PrivateKey typ) | |
Show (PrivateKey typ) | |
Typeable (* -> *) PrivateKey |
Key types in isolation
Hiding key types
Operations on keys
privateKey :: Key a -> PrivateKey a Source
Key IDs
The key ID of a key, by definition, is the hexdigest of the SHA-256 hash of the canonical JSON form of the key where the private object key is excluded.
NOTE: The FromJSON and ToJSON instances for KeyId are ntentially omitted. Use writeKeyAsId instead.
Signing
sign :: PrivateKey typ -> ByteString -> ByteString Source
Sign a bytestring and return the signature
TODO: It is unfortunate that we have to convert to a strict bytestring for ed25519
verify :: PublicKey typ -> ByteString -> ByteString -> Bool Source
We only a few bits from .Repository
data Repository Source
Repository
This is an abstract representation of a repository. It simply provides a way to download metafiles and target files, without specifying how this is done. For instance, for a local repository this could just be doing a file read, whereas for remote repositories this could be using any kind of HTTP client.
data SomeRemoteError :: * where Source
Repository-specific exceptions
For instance, for repositories using HTTP this might correspond to a 404; for local repositories this might correspond to file-not-found, etc.
SomeRemoteError :: Exception e => e -> SomeRemoteError |
data LogMessage Source
Log messages
We use a RemoteFile
rather than a RepoPath
here because we might not have
a RepoPath
for the file that we were trying to download (that is, for
example if the server does not provide an uncompressed tarball, it doesn't
make much sense to list the path to that non-existing uncompressed tarball).
LogRootUpdated | Root information was updated This message is issued when the root information is updated as part of the normal check for updates procedure. If the root information is updated because of a verification error WarningVerificationError is issued instead. |
LogVerificationError VerificationError | A verification error Verification errors can be temporary, and may be resolved later; hence these are just warnings. (Verification errors that cannot be resolved are thrown as exceptions.) |
LogDownloading (Some RemoteFile) | Download a file from a repository |
LogUpdating (Some RemoteFile) | Incrementally updating a file from a repository |
LogSelectedMirror MirrorDescription | Selected a particular mirror |
LogUpdateFailed (Some RemoteFile) UpdateFailure | Updating a file failed (we will try again by downloading it whole) |
LogMirrorFailed MirrorDescription SomeException | We got an exception with a particular mirror (we will try with a different mirror if any are available) |
Exceptions
uncheckClientErrors :: ((Throws VerificationError, Throws SomeRemoteError, Throws InvalidPackageException) => IO a) -> IO a Source
Re-throw all exceptions thrown by the client API as unchecked exceptions
data VerificationError Source
Errors thrown during role validation
VerificationErrorSignatures TargetPath | 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 |
VerificationErrorFileTooLarge TargetPath | The file we requested from the server was larger than expected (potential endless data attack) |
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. |
data InvalidFileInIndex Source