1      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                                  ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P QRSTUVWXYZ[\]^_`abcdefghi j k l m n o p q r s t u v w x y z { | } ~                                                                                                              !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                     *None!"#$%&/0269:;<=DILOQRST      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~None!"#$%&/0269:;<=DEILOQRT 1Wrap an action that may throw a checked exceptionThis is used internally in 6 to avoid impredicative instantiation of the type of 'coerce'/'unsafeCoerce'.Checked exceptionsThrow a checked exceptionCatch a checked exception with the arguments reversedLike try, but for checked exceptions+Rethrow IO exceptions as checked exceptionsThrow an unchecked exceptionThis is just an alias for throwV, but makes it evident that this is a very intentional use of an unchecked exception. Variation on  for internal errors  None!"#$%&/0269:;<=DILOQRTProduce a human-readable stringNone!"#$%&/0269:;<=DILOQRT " Abstract over a file system rootsee ;$?A file system root can be interpreted as an (absolute) FilePath)!Type-level tag for unrooted paths:Unrooted paths need a root before they can be interpreted.*PathsA * is simply a n with a type-level tag indicating where this path is rooted (relative to the current directory, absolute path, relative to a web domain, whatever). Most operations on *? are just lifted versions of the operations on the underlying n0. The tag however allows us to give a lot of operations a more meaningful type. For instance, it does not make sense to append two absolute paths together; instead, we can only append an unrooted path to another path. It also means we avoid bugs where we use one kind of path where we expect another.-Reinterpret the root of a pathAThis literally just changes the type-level tag; use with caution!3Reinterpret an unrooted pathThis is an alias for -; see comments there.4Forget a path's rootThis is an alias for -; see comments there.75A path fragment (like a single directory or filename)>Wrapper around >?Wrapper around (openBinaryTempFileWithDefaultPermissionsCNOTE: The caller is responsible for cleaning up the temporary file.L,Return the immediate children of a directory Filters out "." and "..".M(Recursive traverse a directory structure#Returns a set of paths relative to the directory specified. The list is lazily constructed, so that directories are only read when required. (This is also essential to ensure that this function does not build the entire result in memory before returning, potentially running out of heap.)C !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOldNewOPQ Path of the .tar fileBase directory&Files to add, relative to the base dirRSTUVWXYZ[\]H   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUH*+,-./01)23456789('&$%"#:;<=>?@ABCDEFGHIJKLMNO!PQ RSTU  ? !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]None!"#$%&/0269:;<=DILOQRT55None!"#$%&/0269:;<=DILOQRTCast from one type to anotherUBy default (for language with type inference) we just compare the types returned by ; however, in languages in which terms can have more than one type this may not be the correct definition (indeed, for such languages we cannot give an instance of ).&Embedded languages with type inference9Equality check that gives us a type-level equality proof.Type equality proofsThis is a direct copy of "type-equality:Data.Type.Equality"; if we don't mind the dependency we can use that package directly.  None!"#$%&/0269:;<=DILOQRTaHasFormat fs f is a proof that f is a key in fs.See q and r for typical usage.dAvailable formatsRather than having a general list here, we enumerate all possibilities. This means we are very precise about what we expect, and we avoid any runtime errors about unexpect format definitions.ZNOTE: If we add additional cases here (for dealing with additional formats) all calls to error "inaccessible" need to be reevaluated.i:Format is a singleton type (reflection type to term level)=NOTE: In the future we might add further compression formats.abcdefghijklmnopqrstabcdefghijklmnopqrmlijkdefghabcnopqr abcdefghijklmnopqrstNone!"#$%&/0269:;<=DILOQRT{Type f satisfies  SomeShow f if f a satisfies Show independent of aType f satisfies  SomeShow f if f a satisfies Show independent of aType f satisfies SomeEq f if f a satisfies Eq independent of a{|}~{|}~}~{| {|}~None!"#$%&/0269:;<=DILOQRT>Attempt to create a filesystem lock in the specified directory Given a file pathto4, we do this by attempting to create the directory /pathto/hackage-security-lock, and deleting the directory again afterwards. Creating a directory that already exists will throw an exception on most OSs (certainly Linux, OSX and Windows) and is a reasonably common way to implement a lock file.None!"#$%&/0269:;<=DILOQRTVerification monad%The verification monad is similar to  ResourceTU in intent, in that we can register handlers to be run to release resources. Unlike  ResourceT, however, we maintain _two_ handlers: a cleanup handler which is run whether or not verification succeeds, and a finalisation handler which is run only if verification succeeds.&Cleanup handlers are registered using g, and are guaranteed to run just before the computation terminates (after the finalisation handler).aThe finalisation handlers are run only when verification succeeds, and can be registered with . Finalisation can be used for instance to update the local cache (which should only happen if verification is successful).Run an action in the  monadAAcquire a resource and register the corresponding cleanup handlerNOTE: Resource acquisition happens with exceptions masked. If it is important that the resource acquistion can be timed out (or receive other kinds of asynchronous exceptions), you will need to use an interruptible operation. See  ?http://www.well-typed.com/blog/2014/08/asynchronous-exceptions/ for details.:Register an action to be run only if verification succeeds#Create a short-lived temporary fileKCreates the directory where the temp file should live if it does not exist. Temp directoryTemplate .(c) Galois, Inc. 2007-2009, Duncan Coutts 2015None!"#$%&/0269:;<=DILOQRT54-bit integer values5JavaScript can only safely represent numbers between  -(2^53 - 1) and 2^53 - 1.TODO: Although we introduce the type here, we don't actually do any bounds checking and just inherit all type class instance from Int64. We should probably define K to do bounds checking, give different instances for type classes such as  and , etc.Encode as "Canonical" JSON.UNB: Canonical JSON's string escaping rules deviate from RFC 7159 JSON which requires"All Unicode characters may be placed within the quotation marks, except for the characters that must be escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F)." Whereas the current specification of Canonical JSON explicitly requires to violate this by only escaping the quotation mark and the reverse solidus. This, however, contradicts Canonical JSON's statement that "Canonical JSON is parsable with any full JSON parser"@Consequently, Canonical JSON is not a proper subset of RFC 7159. Parse an intQTODO: Currently this allows for a maximum of 15 digits (i.e. a maximum value of 999,999,999,999,999") as a crude approximation of the  range. None!"#$%&/0269:;<=DILOQRT+Monads in which we can report schema errors Used in the  instance for  Used in the  instance for "Extract a field from a JSON object$  None!"#$%&/0269:;<=DILOQRTThe cache directory/Paths relative to the root of the index tarballThe root of the index tarball,Paths relative to the root of the repositoryThe root of the repositoryFRepository roots can be anchored at a remote URL or a local directory. Note that even for remote repos " is (potentially) different from  & -- for a repository located at, say,  http://hackage.haskell.org4 they happen to coincide, but for one location at  $http://example.com/some/subdirectory they do not.0Anchor a cache path to the location of the cache        None!"#$%&/0269:;<=DILOQRT Layout of a repositoryTUF root metadata TUF timestamp TUF snapshotTUF mirrors listCompressed index tarballUncompressed index tarballPath to the package tarballThe layout used on Hackage/Layout used by cabal for ("legacy") local reposvObviously, such repos do not normally contain any of the TUF files, so their location is more or less arbitrary here.  None!"#$%&/0269:;<=DILOQRTMonomorphic lensPolymorphic lensNone!"#$%&/0269:;<=DILOQRT ^Simple wrapper around bytestring with ToJSON and FromJSON instances that use base64 encoding.   None!"#$%&/0269:;<=DILOQRTCompute the key ID of a keyThe 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.dNOTE: The FromJSON and ToJSON instances for KeyId are ntentially omitted. Use writeKeyAsId instead.*Sign a bytestring and return the signatureSTODO: It is unfortunate that we have to convert to a strict bytestring for ed255190 !"#$%&'()*+) !"#$%&'()*+ None!"#$%&/0269:;<=DILOQRTFA key environment is a mapping from key IDs to the corresponding keys.QIt should satisfy the invariant that these key IDs actually match the keys; see ,.,7Verify that each key ID is mapped to a key with that ID -,   -, None!"#$%&/0269:;<=DILOQRT 7MonadReader-like monad, specialized to key environments aMalformed JSON has syntax errors in the JSON itself (i.e., we cannot even parse it to a JSValue)3Invalid JSON has valid syntax but invalid structure6The string gives a hint about what we expected instead1The JSON file contains a key ID of an unknown keySome verification step failedWrong file type"Records actual and expected types."Render to canonical JSON format# Variation on "- for files that don't require the repo layout3./012345     6 !"#$%&'()*+,-.;      !"#$%&;      !"#$%&$./012345     6 !"#$%&'()*+,-.None!"#$%&/0269:;<=DILOQRT@ File hashB Key thresholdqThe key threshold is the minimum number of keys a document must be signed with. Key thresholds are specified in RoleSpec or DelegationsSpec.D File lengthgHaving verified file length information means we can protect against endless data attacks and similar. @ABCDEF789:;<@ABCDEF @ABCDEF789:;<None!"#$%&/0269:;<=DILOQRTGFile information$This intentionally does not have an  instance; see O 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.MCompute GTODO: 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.NCompute GOCompare known file infoThis 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).PExtract SHA256 hash from G (if present)GHIJKLMNOP=>?@ @AGHIJKLMNOP GHIJKLMNOP=>?@None!"#$%&/0269:;<=DILOQRTR7File got added or modified; we record the new file infoSFile got deletedT Entries in W. either talk about the repository or the indexWMapping from paths to file infoxFile maps are used in target files; the paths are relative to the location of the target files containing the file map.QRSTUVWABXYZ[\]^OldNew_`abcQRSTUVWXYZ[\]^WTUVXYZ[\]QRS^QRSTUVWABXYZ[\]^_`abcNone!"#$%&/0269:;<=DILOQRTi=Occassionally it is useful to read only a header from a file. HeaderOnly intentionally only has a  instance (no ).mFile expiry dateA _& 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.)o File version[The file version is a flat integer which must monotonically increase on every file update.7 and 1 instance are defined in terms of the underlying \A (this is use for example by hackage during the backup process).rFile expiry dates/File version (monotonically increasing counter)ijklmnopqrstuvwxCDEFGHIJKijklmnopqrstuvwxijklmnopqrstuvwxCDEFGHIJKNone!"#$%&/0269:;<=DILOQRT y&Location of the various files we cacheAlthough 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.{TUF root metadata| TUF timestamp} TUF snapshot~TUF mirrors listUncompressed index tarball'Index to the uncompressed index tarballCompressed index tarballWe cache both the compressed and the uncompressed tarballs, because incremental updates happen through the compressed tarball, but reads happen through the uncompressed one (with the help of the tarball index).#The cache layout cabal-install usesWe cache the index as  cache /00-index.tar; this is important because `cabal-install`x expects to find it there (and does not currently go through the hackage-security library to get files from the index). yz{|}~ yz{|}~yz{|}~ None!"#$%&/0269:;<=DILOQRT 5A signature with a key ID (rather than an actual key)DThis corresponds precisely to the TUF representation of a signature."File with uninterpreted signaturesSometimes 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.A list of signaturesuInvariant: each signature must be made with a different key. We enforce this invariant for incoming untrusted data (:) but not for lists of signatures that we create in code.,Create a new document without any signaturesSign a document Variation on " that doesn't need the repo layout/Construct signatures for already rendered value.General FromJSON instance for signed datatypesWe 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.Signature verificationNOTES: 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.&Convert a pre-signature to a signature9Verifies that the key type matches the advertised method."Convert signature to pre-signatureConvert a list of s to a list of sThis 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).6Convert list of pre-signatures to a list of signatures%LMNOPQRLMNOPQR!None!"#$%&/0269:;<=DILOQRTFull versus partial mirrorsThe TUF spec explicitly allows for partial mirrors, with the mirrors file specifying (through patterns) what is available from partial mirrors.bFor now we only support full mirrors; if we wanted to add partial mirrors, we would add a second  MirrorPartial9 constructor here with arguments corresponding to TUF's  metacontent and targetscontent fields.Definition of a mirrormNOTE: Unlike the TUF specification, we require that all mirrors must have the same format. That is, we omit metapath and  targetspath.8Give a human-readable description of a particular mirror(for use in error messages)STUVWX  STUVWX"None!"#$%&/0269:;<=DILOQRTFile info for the root metadataWe list this explicitly in the snapshot so that we can check if we need to update the root metadata without first having to download the entire index tarball.!File info for the mirror metadataCompressed index tarballUncompressed index tarball.Repositories are not required to provide this.YZ[\]^_` YZ[\]^_`#None!"#$%&/0269:;<=DILOQRT abcdeabcde$None!"#$%&/0269:;<=DILOQRTf"Types for pattern and replacements1We intentially are not very precise here, saying String (instead of FileName, BaseName, or  Directoryi, say) so that we can, for example, use a matched filename in a pattern as a directory in a replacement. A delegation6A delegation is a pair of a pattern and a replacement.See match for an example.gReplacement patterns%These constructors match the ones in h: 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).hStructured patterns over pathsxThe 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 wildcardsAdd path rootsCurrently 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.LNOTE: Haddock lacks GADT support so constructors have only regular comments.i?The identity replacement replaces a matched pattern with itselfjParse a patternkParse a replacement\We cheat and use the parser for patterns and then translate using the identity replacement.lAQuasi-quoter for delegations to make them easier to write in code#This allows to write delegations as 9$(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").fmngopqrshtuvwxyz{|i}~jklgopqrshtuvwxyz{|il!fmngopqrshtuvwxyz{|i}~jkl%None!"#$%&/0269:;<=DILOQRTDelegation specification"NOTE: This is a close analogue of RoleSpec. DelegationsMuch 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 .Target metadataMost target files do not need expiry dates because they are not subject to change (and hence attacks like freeze attacks are not a concern). &None!"#$%&/0269:;<=DILOQRT*Files that we might request from the indexThe type index tells us the type of the decoded file, if any. For files for which the library does not support decoding this will be ():. NOTE: Clients should NOT rely on this type index being ()[, or they might break if we add support for parsing additional file formats in the future.TODO: If we wanted to support legacy Hackage, we should also have a case for the global preferred-versions file. But supporting legacy Hackage will probably require more work anyway..Package-specific metadata ( targets.json)Cabal file for a packagePreferred versions a package,Layout of the files within the index tarball Translate an  to a path Parse an n0The layout of the index as maintained on Hackage  'None!"#$%&/0269:;<=DILOQRTRole specificationJThe phantom type indicates what kind of type this role is meant to verify.The root metadata_NOTE: We must have the invariant that ALL keys (apart from delegation keys) must be listed in >. (Delegation keys satisfy a similar invariant, see Targets.)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. (None!"#$%&/0269:;<=DILOQRT@ABCDEFGHIJKLMNOPQRSTUVW^ijklmnopqrstuvwxyz{|}~)None!"#$%&/0269:;<=DILOQRT<Root metadata updated (as part of the normal update process)$Errors thrown during role validation6Not enough signatures signed with the appropriate keysThe file is expired2The file version is less than the previous versionFile information mismatchuWe tried to lookup file information about a particular target file, but the information wasn't in the corresponding  targets.json file.9The metadata for the specified target is missing a SHA256>Some verification errors materialize as deserialization errorsFor example: if we try to deserialize a timestamp file but the timestamp key has been rolled over, deserialization of the file will fail with .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.BWe record all verification errors that occurred before we gave up.Trusted values*Trusted values originate in only two ways:.Anything that is statically known is trusted ()dIf we have "dynamic" data we can trust it once we have verified the the signatures (trustSigned).9NOTE: Trusted is NOT a functor. If it was we could define ZtrustAnything :: a -> Trusted a trustAnything a = fmap (const a) (trustStatic (static ()))tConsequently, it is neither a monad nor a comonad. However, we _can_ apply trusted functions to trusted arguments ().The @ constructor is exported, but any use of it should be verified.Equivalent of =yTrusted isn't quite applicative (no pure, not a functor), but it is somehow Applicative-like: we have the equivalent of =:Trust all elements of some trusted (traversable) containerhIf 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]ENOTE. It might appear that the more natural primitive to offer is a P-like operator such as ;trustSeq :: Applicative f => Trusted (f a) -> f (Trusted a)5However, this is unsound. To see this, consider that ((->) a) is ;4 (it's the reader monad); hence, we can instantiate trustSeq at .trustSeq :: Trusted (a -> a) -> a -> Trusted aand by passing trustStatic (static id) make C a functor, which we certainly don't want to do (see comments for ).$So why is it okay when we insist on M rather than ;A? To see this, it's instructive to consider how we might make a ((->) a) an instance of M0. If we define the domain of enumerable types as 3class 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 band 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 , this means that we can derive *enumPure :: Enumerable a => a -> Trusted anbut 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.Role verificationNOTE: 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. Variation on  verifyRole# that uses key IDs rather than keys*This is used during the bootstrap process.See  3http://en.wikipedia.org/wiki/Public_key_fingerprint.For signature validation File source (for error messages)Previous version (if available)Time now (if checking expiry)For error messagesNone!"#$%&/0269:;<=DILOQRTe-Apply a static function to a trusted argument Variation on O for  GNone!"#$%&/0269:;<=DILOQRT- #Is a particular remote file cached? <This remote file should be cached, and we ask for it by name We don't cache this remote fileThis doesn't mean a Repository should not feel free to cache the file if desired, but it does mean the generic algorithms will never ask for this file from the cache. VThe index is somewhat special: it should be cached, but we never ask for it directly.2Instead, we will ask the Repository for files _from_ the index, which it can serve however it likes. For instance, some repositories might keep the index in uncompressed form, others in compressed form; some might keep an index tarball index for quick access, others may scan the tarball linearly, etc. Repository-specific exceptionsFor instance, for repositories using HTTP this might correspond to a 404; for local repositories this might correspond to file-not-found, etc.Verify a download file,Read the file we just downloaded into memory)We never read binary data, only metadata.)Copy a downloaded file to its destination>Records why we are downloading a file rather than updating it.-Server does not support incremental downloads0We don't have a local copy of the file to updateUpdate failed twiceIf we attempt an incremental update the first time, and it fails, we let it go round the loop, update local security information, and try again. But if an incremental update then fails _again_, we instead attempt a regular download.AUpdate failed (for example: perhaps the local file got corrupted) Log messages We use a 5 rather than a # here because we might not have a  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).Root information was updatedThis 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.A verification errorVerification errors can be temporary, and may be resolved later; hence these are just warnings. (Verification errors that cannot be resolved are thrown as exceptions.)!Download a file from a repository/Incrementally updating a file from a repositorySelected a particular mirror;Updating a file failed (we will instead download it whole)hWe got an exception with a particular mirror (we will try with a different mirror if any are available) JAre we requesting this information because of a previous validation error?FClients can take advantage of this to tell caches to revalidate files." Repository4This 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.$Get a file from the serverResponsibilies of $:VDownload the file from the repository and make it available at a temporary locationUse the provided file length to protect against endless data attacks. (Repositories such as local repositories that are not suspectible to endless data attacks can safely ignore this argument.)`Move the file from its temporary location to its permanent location if verification succeeds.NOTE: Calls to $% should _always_ be in the scope of +.% Get a cached file (if available)&Get the cached root\This is a separate method only because clients must ALWAYS have root information available.'Clear all cached dataIn particular, this should remove the snapshot and the timestamp. It would also be okay, but not required, to delete the index.(Open the tarball for reading%This function has this shape so that:dWe can read multiple files from the tarball without having to open and close the handle each time.We can close the handle immediately when done.)Read the index index*Lock the cache (during updates)+Mirror selectionThe purpose of +; is to scope mirror selection. The idea is that if we have )repWithMirror mirrorList $ someCallback5then the repository may pick a mirror before calling  someCallback, catch exceptions thrown by  someCallbackB, and potentially try the callback again with a different mirror.The list of mirrors may be Nothing if we haven't yet downloaded the list of mirrors from the repository, or when our cached list of mirrors is invalid. Of course, if we did download it, then the list of mirrors may still be empty. In this case the repository must fall back to its primary download mechanism.Mirrors as currently defined (in terms of a "base URL") are inherently a HTTP (or related) concept, so in repository implementations such as the local-repo repWithMirrors/ is probably just an identity operation (see  ignoreMirrors). Conversely, HTTP implementations of repositories may have other, out-of-band information (for example, coming from a cabal config file) that they may use to influence mirror selection.,Logging-Layout of this repository.Layout of the indexrSince the repository hosts the index, the layout of the index is not independent of the layout of the repository./9Description of the repository (used in the show instance)00Files that we might request from the local cache1Timestamp metadata (timestamp.json)2Root metadata ( root.json)3Snapshot metadata ( snapshot.json)4Mirrors list ( mirrors.json)56Abstract definition of files we might have to download5 is parametrized by the type of the formats that we can accept from the remote repository, as well as with information on whether this file is metadata actual binary content.LNOTE: Haddock lacks GADT support so constructors have only regular comments.>!Default format for each file typeFor most file types we don't have a choice; for the index the repository is only required to offer the GZip-compressed format so that is the default.?Default file info (see also >)@Helper function to implement repWithMirrors.C+Which remote files should we cache locally?B      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ?      !"#$%&'()*+,-./0123456789:;<=>?@ABC?=<56789:;01234>?"#$%&'()*+,-./ ! @AB    C      !" #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ None!"#$%&/0269:;<=DILOQRTe#^AVarious operations that we can perform on the index once its open Note that hq contains a fields both for the raw file contents and the parsed file contents; clients can choose which to use.In principle these callbacks will do verification (once we have implemented author signing). Right now they don't need to do that, because the index as a whole will have been verified.`Look up an entry by o Since these o/s must come from somewhere (probably from the r`), it is assumed that they are valid; if they are not, an (unchecked) exception will be thrown.This function also returns the o of the nextc file in the index (if any) for the benefit of clients who wish to walk through the entire index.aLook up an entry by Returns _ if the $ does not refer to an existing file.bVariation if both the o and the  are known1You might use this when scanning the index using w.c%Get (raw) cabal file (wrapper around a)d(Lookup package metadata (wrapper around a)0This will throw an (unchecked) exception if the  targets.json file could not be parsed.e/Get file info (including hash) (wrapper around a)f2Get the SHA256 hash for a package (wrapper around indexLookupInfo)(In addition to the exceptions thrown by indexLookupInfoH, this will also throw an exception if the SHA256 is not listed in the WA (again, this will not happen with a well-formed Hackage index.)gThe r for the indexWe provide this here because  will have read this anyway.h"Entry from the Hackage index; see .jThe raw path in the tarfilekThe parsed file (if recognised)lThe raw contentsAlthough this is a lazy bytestring, this is actually read into memory strictly (i.e., it can safely be used outside the scope of withIndex and friends).mThe parsed contentsaThis field is lazily constructed; the parser is not unless you do a pattern match on this value.n%The time of the entry in the tarfile.oEntry into the Hackage index.q/(Low-level) block number of the tar index entry0Exposed for the benefit of clients who read the .tar* file directly. For this reason also the 7 and  instances for o& just print and parse the underlying TarEntryOffset.rIndex directoryt!The first entry in the dictionaryu9The next available (i.e., one after last) directory entryv!Lookup an entry in the dictionaryThis is an efficient operation.wAn enumeration of all entriestThis field is lazily constructed, so if you don't need it, it does not incur a performance overhead. Moreover, the 5 is also created lazily so if you only need the raw  there is no parsing overhead.The entries are ordered by o: so that the entries can efficiently be read in sequence.uNOTE: This means that there are two ways to enumerate all entries in the tar file, since when lookup an entry using ` the oX of the next entry is also returned. However, this involves reading through the entire tar! file. If you only need to read someO files, it is significantly more efficient to enumerate the tar entries using w instead and only call ` when required.{/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.)You should pass Nothing for the UTCTime _only_ under exceptional circumstances (such as when the main server is down for longer than the expiry dates used in the timestamp files on mirrors).Update the root metadata`Note that the new root metadata is verified using the old root metadata, and only then trusted.We don't always have root file information available. If we notice during the normal update process that the root information has changed then the snapshot will give us the new file information; but if we need to update the root information due to a verification error we do not.dWe additionally delete the cached cached snapshot and timestamp. This is necessary for two reasons: fIf during the normal update process we notice that the root info was updated (because the hash of  root.json in the new snapshot is different from the old snapshot) we download new root info and start over, without (yet) downloading a (potential) new index. This means it is important that we not overwrite our local cached snapshot, because if we did we would then on the next iteration conclude there were no updates and we would fail to notice that we should have updated the index. However, unless we do something, this means that we would conclude on the next iteration once again that the root info has changed (because the hash in the new shapshot still doesn't match the hash in the cached snapshot), and we would loop until we throw a ] exception. By deleting the local snapshot we basically reset the client to its initial state, and we will not try to download the root info once again. The only downside of this is that we will also re-download the index after every root info change. However, this should be infrequent enough that this isn't an issue. See also  4https://github.com/theupdateframework/tuf/issues/285.!Additionally, deleting the local timestamp and snapshot protects against an attack where an attacker has set the file version of the snapshot or timestamp to MAX_INT, thereby making further updates impossible. (Such an attack would require a timestamp/snapshot key compromise.)3However, we _ONLY_ do this when the root information has actually changed. If we did this unconditionally it would mean that we delete the locally cached timestamp whenever the version on the remote timestamp is invalid, thereby rendering the file version on the timestamp and the snapshot useless. See Khttps://github.com/theupdateframework/tuf/issues/283#issuecomment-115739521Get all cached info (if any)|Download a package} Variation on | that takes a FilePath instead.~ Read the Hackage index directoryShould only be called after {.$Look up entries in the Hackage index This is in >N style so that clients can efficiently look up multiple files from the index.Should only be called after {.:Check if we need to bootstrap (i.e., if we have root info)Bootstrap the chain of trustNew 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.nClients 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 * only when this is the desired behaviour.>Variation on getRemote where we only expect one type of resultHRe-throw all exceptions thrown by the client API as unchecked exceptionsLocal files are assumed trustedThere is no point tracking chain of trust for local files because that chain would necessarily have to start at an implicitly trusted (though unverified) file: the root metadata.Just a simple wrapper around verifyFileInfo2Throws a VerificationError if verification failed.SUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{(To check expiry times against (if using)| RepositoryPackage to downloadDestination (see also })} RepositoryPackage to download Destination~For error messagesFile to verify@ABCDEFGHIJKLMNOPQRSTUVW^ijklmnopqrstuvwxyz{|}~ "UVWXYZ[\]^_e`abcdfghijklmnopqrstuvwxyz{|}~{xyz|}rstuvwopq~hijklmn^_`abcdefgDEF@ABCGHIJKL@AMNOPqrsopmnijklutvwxyz{|}~" \]UVWXYZ[+UVWXYZ[\]^ _`abcdefghijklmnopqrstuvw xyz{|}~None!"#$%&/0269:;<=DILOQRTAn IOJ action that represents an incoming response body coming from the server.tThe action gets a single chunk of data from the response body, or an empty bytestring if no more data is available.#This definition is copied from the  http-client package.Proxy configurationbAlthough actually setting the proxy is the purview of the initialization function for individual P implementations and therefore outside the scope of this module, we offer this ProxyConfiguration? type here as a way to uniformly configure proxies across all s.Don't use a proxyUse this specific proxyDIndividual HTTP backends use their own types for specifying proxies.Use automatic proxy settings"What precisely automatic means is < specific, though typically it will involve looking at the  HTTP_PROXY1 environment variable or the (Windows) registry.Response headersSince different libraries represent headers differently, here we just abstract over the few response headers that we might want to know about.$Server accepts byte-range requests (Accept-Ranges: bytes)HTTP status code200 OK206 Partial ContentAdditional request headersSince different libraries represent headers differently, here we just abstract over the few request headers that we might want to setSet Cache-Control: max-age=0Set Cache-Control: no-transformAbstraction over HTTP clientsThis avoids insisting on a particular implementation (such as the HTTP package) and allows for other implementations (such as a conduit based one).5NOTE: Library-specific exceptions MUST be wrapped in  .Download a fileDownload a byte range6Range is starting and (exclusive) end offset in bytes.aHTTP servers are normally expected to respond to a range request with a "206 Partial Content" response. However, servers can respond with a "200 OK" response, sending the entire file instead (for instance, this may happen for servers that don't actually support range rqeuests, but for which we optimistically assumed they did). Implementations of + may accept such a response and inform the hackage-securityf library that the whole file is being returned; the security library can then decide to execute the  anyway (downloading the entire file) or abort the request and try something else. For this reason the security library must be informed whether the server returned the full file or the requested range. Construct a Body reader from a lazy bytestringLThis is appropriate if the lazy bytestring is constructed, say, by calling  hGetContents on a network socket, and the chunks of the bytestring correspond to the chunks as they are returned from the OS network layer.LIf the lazy bytestring needs to be re-chunked this function is NOT suitable.None!"#$%&/0269:;<=DILOQRT &Location and layout of the local cache)Cache a previously downloaded remote fileRebuild the tarball index5Attempts to add to the existing index, if one exists.STODO: Use throwChecked rather than throwUnchecked, and deal with the fallout. See  8https://github.com/well-typed/hackage-security/issues/84. Get a cached file (if available)#Get the cached index (if available)Get the cached rootCalling c without root info available is a programmer error and will result in an unchecked exception. See requiresBootstrap.*Delete a previously downloaded remote fileLock the cachelThis avoids two concurrent processes updating the cache at the same time, provided they both take the lock. Variation on  that takes in the initial   None!"#$%&/0269:;<=DILOQRTLocation of the repositoryoNote that we regard the local repository as immutable; we cache files just like we do for remote repositories.<Initialize the repository (and cleanup resources afterwards)Like a remote repository, a local repository takes a RepoLayout as argument; but where the remote repository interprets this RepoLayout relative to a URL, the local repository interprets it relative to a local directory.0It uses the same cache as the remote repository.Get a file from the serverLocation of local repositoryLocation of local cacheRepository layout Index layoutLoggerCallbackNone!"#$%&/0269:;<=DILOQRTIf we download only the delta, we record both the path to where the "old" file is stored and the path to the temp file containing the delta. Then:When we verify the file, we need both of these paths if we compute the hash from scratch, or only the path to the delta if we attempt to compute the hash incrementally (TODO: incremental verification not currently implemented).When we copy a file over, we are additionally given a destination path. In this case, we expect that destination path to be equal to the path to the old file (and assert this to be the case).%How much of the existing file to keepRemote repository configuration(This is purely for internal convenience._The file we requested from the server was larger than expected (potential endless data attack))Download method (downloading or updating)ADownload this file (we never attempt to update this type of file)9Download this file (we cannot update this file right now),Attempt an (incremental) update of this fileWe select a mirror in  (the implementation of +). Outside the scope of ' no mirror is selected, and a call to P will throw an exception. If this exception is ever thrown its a bug: calls to  ($&) should _always_ be in the scope of +.,Repository options with a reasonable defaultClients should use defaultRepositoryOpts and override required settings.Allow additional mirrors?If this is set to True (default), in addition to the (out-of-band) specified mirrors we will also use mirrors reported by those out-of-band mirrors (that is,  mirrors.json).For most files we download we know the exact size beforehand (because this information comes from the snapshot or delegated info)|For some files we might not know the size beforehand, but we might be able to provide an upper bound (timestamp, root info)BInternal type recording the various server capabilities we support'Does the server support range requests?Server capabilitiesAs the library interacts with the server and receives replies, we may discover more information about the server's capabilities; for instance, we may discover that it supports incremental downloads.Default repository options<Initialize the repository (and cleanup resources afterwards)We allow to specify multiple mirrors to initialize the repository. These are mirrors that can be found "out of band" (out of the scope of the TUF protocol), for example in a  cabal.configS file. The TUF protocol itself will specify that any of these mirrors can serve a  mirrors.jsonq file that itself contains mirrors; we consider these as _additional_ mirrors to the ones that are passed here.aNOTE: The list of mirrors should be non-empty (and should typically include the primary server).TODO: In the future we could allow finer control over precisely which mirrors we use (which combination of the mirrors that are passed as arguments here and the mirrors that we get from  mirrors.json,) as well as indicating mirror preferences.Get the selected mirrorXThrows an exception if no mirror was selected (this would be a bug in the client code).NOTE: Cannot use l here, because the callback would be inside the scope of the withMVar, and there might be further calls to  withRemote# made by the callback argument to  withRemote, leading to deadlock.Get a file from the server HTTP optionsWe want to make sure caches don't transform files in any way (as this will mess things up with respect to hashes etc). Additionally, after a validation error we want to make sure caches get files upstream in case the validation error was because the cache updated files out of order.Mirror selection;Download the specified file using the given download methodExecute a body reader&TODO: Deal with minimum download rate.#Extracting or estimating file sizes"Bound on the size of the timestamp9This is intended as a permissive rather than tight bound. The timestamp signed with a single key is 420 bytes; the signature makes up just under 200 bytes of that. So even if the timestamp is signed with 10 keys it would still only be 2420 bytes. Doubling this amount, an upper bound of 4kB should definitely be sufficient.Bound on the size of the root9This is intended as a permissive rather than tight bound.+The variable parts of the root metadata are-Signatures, each of which are about 200 bytesdA key environment (mapping from key IDs to public keys), each is of which is also about 200 bytesMirrors, root, snapshot, targets, and timestamp role specifications. These contains key IDs, each of which is about 80 bytes.9A skeleton root metadata is about 580 bytes. Allowing for100 signatures[100 mirror keys, 1000 root keys, 100 snapshot keys, 1000 target keys, 100 timestamp keys5the corresponding 2300 entries in the key environment|We end up with a bound of about 665,000 bytes. Doubling this amount, an upper bound of 2MB should definitely be sufficient.7Template for the local file we use to download a URI to3Verify a file downloaded from the remote repositoryTODO: This currently still computes the hash for the whole file. If we cached the state of the hash generator we could compute the hash incrementally. However, profiling suggests that this would only be a minor improvement.Multiple exit points#We can simulate the imperative code Zif (cond1) return exp1; if (cond2) return exp2; if (cond3) return exp3; return exp4;as ~multipleExitPoints $ do when (cond1) $ exit exp1 when (cond) $ exit exp2 when (cond) exit exp3 return exp4Function exit point (see )E#Implementation of the HTTP protocol"Out of band" list of mirrorsRepository optionsLocation of local cacheRepository layout Index layoutLoggerCallback HTTP client MVar indicating currently mirrorLoggerOut-of-band mirrorsRepository options TUF mirrorsCallbackInternal configuration%Did a security check previously fail? File to getSelected format!File source (for error msgs only)Maximum file sizeHandle to write data too*The action to give us blocks from the file  # *None!"#$%&/0269:;<=DILOQRT   "#$%@ABCDEFGHIJKLMNOPQRSTUVW^ijklmnopqrstuvwxyz{|}~DEF@ABCGHIJKL@AMNOPqrsopmnijklutvwxyz{|}~ +,-+./+01+23+24+25+26+.7+.8+.9+.:+;<+;=+;>+;?+@A+@B+@C+@D+@EFGHIJKLMNOPQRSTTUVWXYZ[[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                  !" # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h ijjkkllmnnopqrstuvwxyz{|}'*~)                               !!!!!!!!!!!!!""""""""#####$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''''')))))))))))))))))))) ) ) )   !"#$%&'()*++,,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] ^ ^ _ ` a b b c c d d e f g h i j k l m m n o p q r s s t u u v w x y z z { | } ~                     +++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + +++++++++++++ !"#+$%+$&+$'+(+)+*+++,+-+.+/+0+1+2+3+4+5+6+7+8+9+:+;+<+=+>+?@+?A+?B+?C+D+E+F+G+H+IJ+IK+IL+I(+IM+IN+IO+IP+IQ+IR+IS+IT+IU+VW+VX+VY+VZ+V[+\+]+^+_`a`b`c`d`e`f`ghij+k+l+m`n`o`p`q+r`s`t+uv+uw+ux+yz+{+|+}~++++++++++++++++++I+I+I+I+I+I+I+I+I+++u+++++$+$++++++++?+?+?+?+?++++++*++++++++++++++++++++++++++++++++y+y+                       !"#$%&'()*+,-./0123456789 : # / ; 0 < 1 = 2 > ?@ABCDEFGHI}JKLMNOPQRS T U V W X Y Z![!\!]!^!_!`"a"b"c"d"["e"f"g#h#[#i#j#k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z${$|$}$~$$$u$$$$$$$$$$$!$#$$$%$$$$$$$$%[%%%%%%%&&&'['''''')))))                             +/hackage-security-0.5.0.2-HGq5ChzIr7p7iZfGEcZ84EHackage.Security.TrustedHackage.Security.Util.PathHackage.Security.Client.VerifyHackage.Security.Util.CheckedHackage.Security.Util.PrettyHackage.Security.Client.FormatsHackage.Security.Util.SomeHackage.Security.Util.IOText.JSON.CanonicalHackage.Security.JSONHackage.Security.ClientHackage.Security.Util.LensHackage.Security.Key.EnvHackage.Security.TUF.FileMap"Hackage.Security.Client.Repository*Hackage.Security.Client.Repository.HttpLib(Hackage.Security.Client.Repository.Cache(Hackage.Security.Client.Repository.Local)Hackage.Security.Client.Repository.RemotePreludeHackage.Security.Util.Stack#Hackage.Security.Util.TypedEmbeddedHackage.Security.Util.JSONHackage.Security.TUF.Paths Hackage.Security.TUF.Layout.RepoHackage.Security.Util.Base64Hackage.Security.KeyHackage.Security.TUF.CommonHackage.Security.TUF.FileInfoHackage.Security.TUF.Header!Hackage.Security.TUF.Layout.CacheHackage.Security.TUF.SignedHackage.Security.TUF.MirrorsHackage.Security.TUF.SnapshotHackage.Security.TUF.TimestampHackage.Security.TUF.PatternsHackage.Security.TUF.Targets!Hackage.Security.TUF.Layout.IndexHackage.Security.TUF.RootHackage.Security.TUFHackage.Security.Trusted.TCBHackage.Security.Serverbase GHC.StaticPtr StaticPtrGHC.IO.Handle.TypesHandleControl.Monad.IO.ClassliftIO GHC.IO.HandlehSeek hSetBuffering hFileSizehCloseBlockBuffering LineBuffering NoBuffering BufferMode GHC.IO.Device SeekFromEnd RelativeSeek AbsoluteSeekSeekMode GHC.IO.IOMode ReadWriteMode AppendMode WriteModeReadModeIOModeThrowsunthrow throwChecked catchChecked handleChecked tryCheckedcheckIOthrowUnchecked internalError $fThrowsCatchPrettyprettyWebTarFsPathFsRoottoAbsoluteFilePathHomeDirAbsoluteRelativeUnrootedPathunPathcastRoot takeDirectory takeFileName<.>splitExtensionrootPath unrootPathtoUnrootedFilePathfromUnrootedFilePathfragment joinFragmentsisPathPrefixOf toFilePath fromFilePath makeAbsolutefromAbsoluteFilePathwithFile openTempFile'readLazyByteStringreadStrictByteStringwriteLazyByteStringwriteStrictByteStringcopyFilecreateDirectorycreateDirectoryIfMissingremoveDirectory doesFileExistdoesDirectoryExist removeFilegetTemporaryDirectorygetDirectoryContentsgetRecursiveContents renameFilegetCurrentDirectorytarIndexLookup tarAppend toURIPath fromURIPathuriPath modifyUriPath $fPrettyPath$fFsRootHomeDir$fFsRootAbsolute$fFsRootRelative $fPrettyPath0 $fPrettyPath1 $fPrettyPath2 $fPrettyPath3 $fShowPath$fEqPath $fOrdPath HasFormatHFZHFSFormatsFsNoneFsUnFsGzFsUnGzFormatFUnFGzFormatGzFormatUnhasFormatAbsurd hasFormatGet formatsMap formatsMember formatsLookup$fFunctorFormats $fUnifyFormat$fShowHasFormat $fEqHasFormat $fShowFormats $fEqFormats $fEqFormat $fShowFormat SomePretty somePretty DictPrettySomeShowsomeShowDictShowSomeEqsomeEqDictEqSome typecheckSome $fPrettySome $fShowSome$fEqSome getFileSizehandleDoesNotExist withDirLocktimedIOVerify runVerifyacquire ifVerified openTempFile$fFunctorVerify$fApplicativeVerify $fMonadVerify$fMonadIOVerifyInt54JSValueJSNullJSBoolJSNumJSStringJSArrayJSObjectrenderCanonicalJSONparseCanonicalJSON $fReadInt54 $fShowInt54$fBoundedInt54 $fEnumInt54 $fEqInt54$fIntegralInt54 $fDataInt54 $fNumInt54 $fOrdInt54 $fRealInt54 $fIxInt54$fFiniteBitsInt54 $fBitsInt54$fStorableInt54$fPrintfArgInt54 $fShowJSValue $fReadJSValue $fEqJSValue $fOrdJSValueGotExpectedReportSchemaErrorsexpected FromObjectKey fromObjectKey ToObjectKey toObjectKeyFromJSONfromJSONToJSONtoJSON expected' fromJSObject fromJSFieldfromJSOptFieldmkObject CachePath CacheRoot IndexPath IndexRootRepoPathRepoRootanchorRepoPathLocallyanchorRepoPathRemotelyanchorCachePath RepoLayoutrepoLayoutRootrepoLayoutTimestamprepoLayoutSnapshotrepoLayoutMirrorsrepoLayoutIndexTarGzrepoLayoutIndexTarrepoLayoutPkgTarGzhackageRepoLayoutcabalLocalRepoLayoutLens'LensgetmodifysetlookupMHasKeyIdkeyIdKeyId keyIdStringKeyTypeKeyTypeEd25519 PrivateKeyPrivateKeyEd25519 PublicKeyPublicKeyEd25519Key KeyEd25519Ed25519 publicKey privateKey somePublicKeysomePublicKeyType someKeyId createKey createKey'signverifyKeyEnv keyEnvMapfromPublicKeysfromKeysemptynullinsertlookupunion$fFromJSONmKeyEnv$fToJSONmKeyEnv $fShowKeyEnv WriteJSONReadJSON_NoKeys_NoLayoutReadJSON_Keys_NoLayoutReadJSON_Keys_Layout MonadKeys localKeysaskKeysDeserializationErrorDeserializationErrorMalformedDeserializationErrorSchemaDeserializationErrorUnknownKeyDeserializationErrorValidationDeserializationErrorFileTypevalidate verifyType readKeyAsIdaddKeyswithKeys lookupKeyrunReadJSON_Keys_LayoutrunReadJSON_Keys_NoLayoutrunReadJSON_NoKeys_NoLayoutparseJSON_Keys_LayoutparseJSON_Keys_NoLayoutparseJSON_NoKeys_NoLayoutreadJSON_Keys_LayoutreadJSON_Keys_NoLayoutreadJSON_NoKeys_NoLayout runWriteJSON renderJSONrenderJSON_NoLayout writeJSONwriteJSON_NoLayout writeKeyAsId!$fMonadKeysReadJSON_Keys_NoLayout$fMonadKeysReadJSON_Keys_Layout+$fMonadReaderRepoLayoutReadJSON_Keys_Layout,$fReportSchemaErrorsReadJSON_NoKeys_NoLayout*$fReportSchemaErrorsReadJSON_Keys_NoLayout($fReportSchemaErrorsReadJSON_Keys_Layout$fPrettyDeserializationError$fExceptionDeserializationError$fFunctorReadJSON_Keys_Layout!$fApplicativeReadJSON_Keys_Layout$fMonadReadJSON_Keys_Layout $fMonadErrorReadJSON_Keys_Layout$fFunctorReadJSON_Keys_NoLayout#$fApplicativeReadJSON_Keys_NoLayout$fMonadReadJSON_Keys_NoLayout"$fMonadErrorReadJSON_Keys_NoLayout!$fFunctorReadJSON_NoKeys_NoLayout%$fApplicativeReadJSON_NoKeys_NoLayout$fMonadReadJSON_NoKeys_NoLayout$$fMonadErrorReadJSON_NoKeys_NoLayout$fFunctorWriteJSON$fApplicativeWriteJSON$fMonadWriteJSON$fMonadReaderWriteJSON$fShowDeserializationErrorHash KeyThreshold FileLength fileLengthFileInfofileInfoLengthfileInfoHashesHashFn HashFnSHA256fileInfocomputeFileInfoknownFileInfoEqualfileInfoSHA256 FileChange FileChanged FileDeleted TargetPathTargetPathRepoTargetPathIndexFileMap!fromListfileMapChanges$fFromObjectKeymTargetPath$fToObjectKeymTargetPath$fFromJSONmFileMap$fToJSONmFileMap$fPrettyTargetPath$fShowTargetPath$fEqTargetPath$fOrdTargetPath $fShowFileMap$fShowFileChangeHeader headerExpires headerVersion FileExpires FileVersion HasHeader fileExpires fileVersion expiresNever expiresInDays isExpiredversionInitialversionIncrement CacheLayoutcacheLayoutRootcacheLayoutTimestampcacheLayoutSnapshotcacheLayoutMirrorscacheLayoutIndexTarcacheLayoutIndexIdxcacheLayoutIndexTarGzcabalCacheLayout PreSignature presignature presigMethod presigKeyIdUninterpretedSignaturesuninterpretedSigneduninterpretedSignatures Signature signature signatureKey SignaturesSignedsigned signaturesunsignedwithSignatureswithSignatures' signRenderedverifySignaturesignedFromJSONverifySignaturesfromPreSignaturetoPreSignaturefromPreSignaturestoPreSignaturesMirrorDescription MirrorContent MirrorFullMirror mirrorUrlBase mirrorContentMirrorsmirrorsVersionmirrorsExpiresmirrorsMirrorsdescribeMirrorSnapshotsnapshotVersionsnapshotExpiressnapshotInfoRootsnapshotInfoMirrorssnapshotInfoTarGzsnapshotInfoTar TimestamptimestampVersiontimestampExpirestimestampInfoSnapshot DelegationDelegationSpecdelegationSpecKeysdelegationSpecThreshold delegation DelegationsdelegationsKeysdelegationsRolesTargetstargetsVersiontargetsExpirestargetsTargetstargetsDelegations targetsLookup IndexFileIndexPkgMetadata IndexPkgCabal IndexPkgPrefs IndexLayoutindexFileToPathindexFileFromPathhackageIndexLayoutindexLayoutPkgMetadataindexLayoutPkgCabalindexLayoutPkgPrefsRoleSpec roleSpecKeysroleSpecThreshold RootRoles rootRolesRootrootRolesSnapshotrootRolesTargetsrootRolesTimestamprootRolesMirrorsRoot rootVersion rootExpiresrootKeys rootRolesVerificationHistory RootUpdatedVerificationErrorVerificationErrorSignaturesVerificationErrorExpiredVerificationErrorVersionVerificationErrorFileInfoVerificationErrorUnknownTargetVerificationErrorMissingSHA256 VerificationErrorDeserializationVerificationErrorLoopSignaturesVerifiedsignaturesVerifiedTrustedDeclareTrustedtrusted trustStatic trustVerified trustApply trustElems verifyRole'verifyFingerprints VerifyRole verifyRole<$$>trustedFileInfoEqual$fVerifyRoleMirrors$fVerifyRoleSnapshot$fVerifyRoleTimestamp$fVerifyRoleRootIsCachedCacheAs DontCache CacheIndexSomeRemoteErrorDownloadedFiledownloadedVerifydownloadedReaddownloadedCopyTo UpdateFailureUpdateImpossibleUnsupportedUpdateImpossibleNoLocalCopyUpdateFailedTwice UpdateFailed LogMessageLogRootUpdatedLogVerificationErrorLogDownloading LogUpdatingLogSelectedMirrorLogCannotUpdateLogMirrorFailed AttemptNr Repository repGetRemote repGetCachedrepGetCachedRoot repClearCache repWithIndexrepGetIndexIdx repLockCache repWithMirrorrepLog repLayoutrepIndexLayoutrepDescription CachedFileCachedTimestamp CachedRootCachedSnapshot CachedMirrors RemoteFileRemoteTimestamp RemoteRootRemoteSnapshot RemoteMirrors RemoteIndexRemotePkgTarGzBinaryMetadataremoteFileDefaultFormatremoteFileDefaultInfomirrorsUnsupportedremoteRepoPathremoteRepoPath' mustCache$fPrettyUpdateFailure$fPrettyLogMessage$fPrettySomeRemoteError$fExceptionSomeRemoteError$fShowRepository$fPrettyCachedFile$fPrettyRemoteFile$fEqCachedFile$fOrdCachedFile$fShowCachedFile $fEqAttemptNr$fOrdAttemptNr$fNumAttemptNr$fShowIsCached $fEqIsCached$fShowSomeRemoteError$fShowRemoteFileInvalidFileInIndexinvalidFileInIndexinvalidFileInIndexRawinvalidFileInIndexErrorLocalFileCorruptedInvalidPackageExceptionIndexCallbacksindexLookupEntryindexLookupFileindexLookupFileEntryindexLookupCabalindexLookupMetadataindexLookupFileInfoindexLookupHashindexDirectory IndexEntryindexEntryPathindexEntryPathParsedindexEntryContentindexEntryContentParsedindexEntryTimeDirectoryEntrydirectoryEntryBlockNo DirectorydirectoryFirst directoryNextdirectoryLookupdirectoryEntries HasUpdates NoUpdatescheckForUpdatesdownloadPackagedownloadPackage' getDirectory withIndexrequiresBootstrap bootstrapuncheckClientErrors$fPrettyInvalidFileInIndex$fPrettyLocalFileCorrupted$fPrettyInvalidPackageException$fExceptionInvalidFileInIndex$fExceptionLocalFileCorrupted"$fExceptionInvalidPackageException$fReadDirectoryEntry$fShowDirectoryEntry$fShowHasUpdates$fEqHasUpdates$fOrdHasUpdates$fEqDirectoryEntry$fOrdDirectoryEntry$fShowInvalidFileInIndex$fShowLocalFileCorrupted$fShowInvalidPackageException BodyReader ProxyConfigProxyConfigNoneProxyConfigUseProxyConfigAutoHttpResponseHeaderHttpResponseAcceptRangesBytes HttpStatusHttpStatus200OKHttpStatus206PartialContentHttpRequestHeaderHttpRequestMaxAge0HttpRequestNoTransformHttpLibhttpGet httpGetRangebodyReaderFromBS$fEqHttpRequestHeader$fOrdHttpRequestHeader$fShowHttpRequestHeader$fEqHttpResponseHeader$fOrdHttpResponseHeader$fShowHttpResponseHeaderCache cacheRoot cacheLayoutcacheRemoteFile getCachedgetCachedIndex getCachedRoot getIndexIdx clearCache lockCache LocalFile LocalRepowithRepository$fDownloadedFileLocalFile RemoteTempRepoOptsrepoAllowAdditionalMirrorsFileSize FileSizeExact FileSizeBoundfileSizeWithinBoundsdefaultRepoOpts$fDownloadedFileRemoteTemp$fPrettyRemoteTemp$fExceptionFileTooLarge$fPrettyFileTooLarge$fShowFileSize$fShowFileTooLargeGHC.Base++ghc-primGHC.PrimseqGHC.Listfilterzip System.IOprint Data.Tuplefstsnd otherwisemap$GHC.Real fromIntegral realToFracGHC.EnumBoundedminBoundmaxBoundEnumenumFrom enumFromThenenumFromThenTo enumFromTofromEnumtoEnumsuccpred GHC.ClassesEq==/= GHC.FloatFloatingpiexplogsqrt**logBasesincostanasinacosatansinhcoshtanhasinhacoshatanh Fractional fromRational/recipIntegral toIntegerquotremdivmodquotRemdivModMonadfail>>=>>returnFunctorfmap<$GHC.NumNum*+-negate fromIntegerabssignumOrd>=minmax><<=compareGHC.ReadRead readsPrecreadListReal toRational RealFloat floatRadix floatDigits floatRange decodeFloat encodeFloatexponent significand scaleFloatisNaN isInfiniteisDenormalizedisNegativeZeroisIEEEatan2RealFracproperFractiontruncateroundceilingfloorGHC.ShowShow showsPrecshowshowList Applicativepure<*>*><* Data.FoldableFoldablefoldrfoldMaplengthfoldlfoldl1sumproductfoldr1maximumminimumelemData.Traversable TraversabletraversemapM sequenceAsequenceMonoidmemptymappendmconcat GHC.TypesBoolTrueFalseCharDoubleFloatInt integer-gmpGHC.Integer.TypeIntegerMaybeNothingJustOrderingLTEQGTRationalIOWord Data.EitherEitherLeftRightGHC.ErrerrortailreadFileGHC.IOFilePath Text.ReadreadzipWithStringText.ParserCombinators.ReadPReadSreadIOreadLn appendFile writeFileinteract getContentsgetLinegetCharputStrLnputStrputCharreadsGHC.IO.ExceptionioErrornotElemallanyorand concatMapconcat sequence_mapM_ userErrorIOErroreither Data.OldListunwordswordsunlineslineslex readParen Data.Functor<$>lcmgcd^^^oddeven showParen showStringshowCharshowsShowSunzip3unzipzipWith3zip3!!reversebreakspansplitAtdroptake dropWhile takeWhilecycle replicaterepeatiteratescanr1scanrscanl1scanlinitlasthead Data.MaybemaybeuncurrycurrysubtractasTypeOfuntil$!flip.constid=<< undefinederrorWithoutStackTrace&&||notWrapCatchProxyunWrap coerceWrapliftFPliftFP2 liftFromFP liftFromFP2liftToFP:-AsTypetypeOfTypedUnify:=:asTypeunifyTypeOfReflunVerifyCleanup Finaliser Data.Bits FiniteBitsp_number int54ToInt64s_values_strings_arrays_objectp_valuetokp_jvaluep_null p_booleanp_arrayp_stringp_object digitToInt54manyNcontainers-0.5.7.1 Data.Map.BaseMap unknownField$fFromJSONmURI $fToJSONmURI$fFromJSONmMap $fToJSONmMap$fFromJSONmUTCTime$fToJSONmUTCTime $fFromJSONm[] $fToJSONm[]$fFromJSONmInt54$fToJSONmInt54$fFromJSONm[]0 $fToJSONm[]0$fFromJSONmJSValue$fToJSONmJSValue$fFromObjectKeymPath$fToObjectKeymPath$fFromObjectKeym[]$fToObjectKeym[]Base64fromByteString toByteString$fFromJSONmBase64$fToJSONmBase64$fFromJSONmSome$fToJSONmKeyType$fFromJSONmSome0 $fToJSONmSome$fToJSONmSome0$fToJSONmSome1$fToJSONmPublicKey$fFromJSONmSome1 $fToJSONmKey $fHasKeyIdKey$fHasKeyIdPublicKey$fFromObjectKeymKeyId$fToObjectKeymKeyId$fTypedPrivateKey$fTypedPublicKey $fTypedKey$fUnifyKeyType$fSomeEqKeyType$fSomeShowKeyType$fSomeEqPrivateKey$fSomeEqPublicKey $fSomeEqKey$fSomeShowPrivateKey$fSomeShowPublicKey $fSomeShowKeycheckKeyEnvInvariant unWriteJSONunReadJSON_NoKeys_NoLayoutunReadJSON_Keys_NoLayoutunReadJSON_Keys_Layout expectedError$fFromJSONmHash$fFromJSONmFileLength$fFromJSONmKeyThreshold $fToJSONmHash$fToJSONmFileLength$fToJSONmKeyThreshold$fFromJSONmFileInfo$fToJSONmFileInfo$fFromObjectKeymHashFn$fToObjectKeymHashFnfileMaponeDay$fFromJSONmHeader$fFromJSONmFileExpires$fFromJSONmFileVersion$fToJSONmFileExpires$fToJSONmFileVersion$fHasHeaderHeader$fReadFileVersion$fShowFileVersion $fToJSONmUninterpretedSignatures"$fFromJSONmUninterpretedSignatures$fToJSONmPreSignature$fFromJSONmPreSignature$fFromJSONmSignatures$fToJSONmSignatures$fToJSONmSigned$fFromJSONmSigned$fFromJSONmMirrors$fFromJSONmMirror$fToJSONmMirrors$fToJSONmMirror$fHasHeaderMirrorspathRoot pathMirrorspathIndexTarGz pathIndexTar$fFromJSONmSnapshot$fToJSONmSnapshot$fHasHeaderSnapshot pathSnapshot$fFromJSONmTimestamp$fToJSONmTimestamp$fHasHeaderTimestamp PatternType ReplacementPatternidentityReplacement parsePatternparseReplacementqqd PatTypeNil PatTypeStr RepFileConst RepFileExt RepFileAny RepDirConst RepDirAny PatFileConst PatFileExt PatFileAny PatDirConst PatDirAnyBaseName ExtensionFileName matchPatternconstructReplacementmatchDelegation prettyPatternprettyReplacementparseDelegation_ex1_ex2_ex3$fToJSONmReplacement$fToJSONmPattern$fLiftDelegation$fLiftReplacement $fLiftPattern$fAsTypeReplacement$fTypedPattern$fUnifyPatternType$fFromJSONmTargets$fToJSONmTargets$fFromJSONmDelegations$fToJSONmDelegations$fFromJSONmDelegationSpec$fToJSONmDelegationSpec$fHasHeaderTargets$fSomePrettyIndexFile$fSomeShowIndexFile$fPrettyIndexFile$fFromJSONmRoleSpec$fToJSONmRoleSpec $fToJSONmRoot$fFromJSONmRootRoles$fToJSONmRootRoles$fHasHeaderRoot$fPrettyRootUpdated$fPrettyVerificationError$fExceptionRootUpdated$fExceptionVerificationError updateRoot getCachedInfo getRemote'trustLocalFileverifyFileInfo' CachedInfo cachedRoot cachedKeyEnvcachedTimestampcachedSnapshot cachedMirrorscachedInfoSnapshotcachedInfoRootcachedInfoMirrorscachedInfoTarGz cachedVersion readLocalRoot readLocalFile getRemoteFile getRemote withMirrorreadCachedJSONreadDownloadedJSONthrowErrorsUncheckedthrowErrorsChecked eitherToMayberebuildTarIndex addEntries"tar-0.5.0.1-8Nvs5LigAzY5ST0zD12ClTCodec.Archive.Tar.Indexbuild IndexBuilder tryReadIndexcachedFilePathcachedIndexPathcachedIndexIdxPathverifyLocalFileDownloadedDelta deltaSeek RemoteConfig FileTooLargeDownloadMethod NeverUpdated CannotUpdateUpdateSelectedMirrorServerCapabilities_serverAcceptRangesBytesServerCapabilitiesgetSelectedMirrorControl.Concurrent.MVarwithMVarhttpRequestHeadersgetFileexecBodyReaderremoteFileSizefileSizeBoundTimestampfileSizeBoundRoot uriTemplateverifyRemoteFilemultipleExitPointsexitDownloadedWhole wholeTemp deltaTemp deltaExisting cfgLayout cfgHttpLibcfgBasecfgCachecfgCaps cfgLoggercfgOptsfileTooLargePathfileTooLargeExpectedneverUpdatedFormatcannotUpdateFormatcannotUpdateReason updateFormat updateInfo updateLocal updateTailSCnewServerCapabilitiesupdateServerCapabilitiescheckServerCapabilitypickDownloadMethod remoteFileURI fileLength'