Safe Haskell | None |
---|---|
Language | Haskell2010 |
- Checking for updates
- Downloading targets
- Access to the Hackage index
- Bootstrapping
- Re-exports
- Types
- Utility
- Cache layout
- Repository layout
- Repository layout
- TUF types
- Repository
- Index
- Cache
- 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
Synopsis
- checkForUpdates :: (Throws VerificationError, Throws SomeRemoteError) => Repository down -> Maybe UTCTime -> IO HasUpdates
- data HasUpdates
- downloadPackage :: (Throws SomeRemoteError, Throws VerificationError, Throws InvalidPackageException) => Repository down -> PackageIdentifier -> Path Absolute -> IO ()
- downloadPackage' :: (Throws SomeRemoteError, Throws VerificationError, Throws InvalidPackageException) => Repository down -> PackageIdentifier -> FilePath -> IO ()
- data Directory = Directory {
- directoryFirst :: DirectoryEntry
- directoryNext :: DirectoryEntry
- directoryLookup :: forall dec. IndexFile dec -> Maybe DirectoryEntry
- directoryEntries :: [(DirectoryEntry, IndexPath, Maybe (Some IndexFile))]
- newtype DirectoryEntry = DirectoryEntry {}
- getDirectory :: Repository down -> IO Directory
- data IndexFile :: * -> * where
- IndexPkgMetadata :: PackageIdentifier -> IndexFile (Signed Targets)
- IndexPkgCabal :: PackageIdentifier -> IndexFile ()
- IndexPkgPrefs :: PackageName -> IndexFile ()
- data IndexEntry dec = IndexEntry {}
- data IndexCallbacks = IndexCallbacks {
- indexLookupEntry :: DirectoryEntry -> IO (Some IndexEntry, Maybe DirectoryEntry)
- indexLookupFile :: forall dec. IndexFile dec -> IO (Maybe (IndexEntry dec))
- indexLookupFileEntry :: forall dec. DirectoryEntry -> IndexFile dec -> IO (IndexEntry dec)
- indexLookupCabal :: Throws InvalidPackageException => PackageIdentifier -> IO (Trusted ByteString)
- indexLookupMetadata :: Throws InvalidPackageException => PackageIdentifier -> IO (Trusted Targets)
- indexLookupFileInfo :: (Throws InvalidPackageException, Throws VerificationError) => PackageIdentifier -> IO (Trusted FileInfo)
- indexLookupHash :: (Throws InvalidPackageException, Throws VerificationError) => PackageIdentifier -> IO (Trusted Hash)
- indexDirectory :: Directory
- withIndex :: Repository down -> (IndexCallbacks -> IO a) -> IO a
- requiresBootstrap :: Repository down -> IO Bool
- bootstrap :: (Throws SomeRemoteError, Throws VerificationError) => Repository down -> [KeyId] -> KeyThreshold -> IO ()
- newtype FileLength = FileLength {
- fileLength :: Int54
- newtype Hash = Hash String
- newtype KeyThreshold = KeyThreshold Int54
- data FileInfo = FileInfo {}
- data HashFn
- newtype Hash = Hash String
- fileInfo :: ByteString -> FileInfo
- computeFileInfo :: FsRoot root => Path root -> IO FileInfo
- compareTrustedFileInfo :: FileInfo -> FileInfo -> Bool
- knownFileInfoEqual :: FileInfo -> FileInfo -> Bool
- fileInfoSHA256 :: FileInfo -> Maybe Hash
- data Int54
- data FileChange
- data TargetPath
- data FileMap
- fileMapChanges :: FileMap -> FileMap -> Map TargetPath FileChange
- class HasHeader a where
- fileExpires :: Lens' a FileExpires
- fileVersion :: Lens' a FileVersion
- newtype FileVersion = FileVersion Int54
- newtype FileExpires = FileExpires (Maybe UTCTime)
- data Header = Header {}
- expiresInDays :: UTCTime -> Integer -> FileExpires
- expiresNever :: FileExpires
- isExpired :: UTCTime -> FileExpires -> Bool
- versionInitial :: FileVersion
- versionIncrement :: FileVersion -> FileVersion
- data CacheLayout = CacheLayout {}
- cabalCacheLayout :: CacheLayout
- data IndexLayout = IndexLayout {
- indexFileToPath :: forall dec. IndexFile dec -> IndexPath
- indexFileFromPath :: IndexPath -> Maybe (Some IndexFile)
- data IndexFile :: * -> * where
- IndexPkgMetadata :: PackageIdentifier -> IndexFile (Signed Targets)
- IndexPkgCabal :: PackageIdentifier -> IndexFile ()
- IndexPkgPrefs :: PackageName -> IndexFile ()
- hackageIndexLayout :: IndexLayout
- indexLayoutPkgMetadata :: IndexLayout -> PackageIdentifier -> IndexPath
- indexLayoutPkgCabal :: IndexLayout -> PackageIdentifier -> IndexPath
- indexLayoutPkgPrefs :: IndexLayout -> PackageName -> IndexPath
- data RepoLayout = RepoLayout {}
- hackageRepoLayout :: RepoLayout
- cabalLocalRepoLayout :: RepoLayout
- data Mirrors = Mirrors {}
- data Mirror = Mirror {}
- data MirrorContent = MirrorFull
- type MirrorDescription = String
- describeMirror :: Mirror -> MirrorDescription
- data RepoRoot
- type RepoPath = Path RepoRoot
- anchorRepoPathLocally :: Path root -> RepoPath -> Path root
- anchorRepoPathRemotely :: Path Web -> RepoPath -> Path Web
- data IndexRoot
- type IndexPath = Path IndexRoot
- data CacheRoot
- type CachePath = Path CacheRoot
- anchorCachePath :: Path root -> CachePath -> Path root
- 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 = 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
- trusted :: Trusted a -> a
- data Repository down
- class DownloadedFile (down :: * -> *) where
- downloadedVerify :: down a -> Trusted FileInfo -> IO Bool
- downloadedRead :: down Metadata -> IO ByteString
- downloadedCopyTo :: down a -> Path Absolute -> IO ()
- data SomeRemoteError :: * where
- SomeRemoteError :: Exception e => e -> SomeRemoteError
- data LogMessage
- uncheckClientErrors :: ((Throws VerificationError, Throws SomeRemoteError, Throws InvalidPackageException) => IO a) -> IO a
- data VerificationError
- = VerificationErrorSignatures TargetPath
- | VerificationErrorExpired TargetPath
- | VerificationErrorVersion TargetPath
- | VerificationErrorFileInfo TargetPath
- | VerificationErrorUnknownTarget TargetPath
- | VerificationErrorMissingSHA256 TargetPath
- | VerificationErrorDeserialization TargetPath DeserializationError
- | VerificationErrorLoop VerificationHistory
- type VerificationHistory = [Either RootUpdated VerificationError]
- data RootUpdated = RootUpdated
- data InvalidPackageException = InvalidPackageException PackageIdentifier
- data InvalidFileInIndex = InvalidFileInIndex {}
- data LocalFileCorrupted = LocalFileCorrupted DeserializationError
Checking for updates
:: (Throws VerificationError, Throws SomeRemoteError) | |
=> Repository down | |
-> Maybe UTCTime | To check expiry times against (if using) |
-> IO HasUpdates |
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).
data HasUpdates #
Instances
Eq HasUpdates # | |
Defined in Hackage.Security.Client (==) :: HasUpdates -> HasUpdates -> Bool # (/=) :: HasUpdates -> HasUpdates -> Bool # | |
Ord HasUpdates # | |
Defined in Hackage.Security.Client compare :: HasUpdates -> HasUpdates -> Ordering # (<) :: HasUpdates -> HasUpdates -> Bool # (<=) :: HasUpdates -> HasUpdates -> Bool # (>) :: HasUpdates -> HasUpdates -> Bool # (>=) :: HasUpdates -> HasUpdates -> Bool # max :: HasUpdates -> HasUpdates -> HasUpdates # min :: HasUpdates -> HasUpdates -> HasUpdates # | |
Show HasUpdates # | |
Defined in Hackage.Security.Client showsPrec :: Int -> HasUpdates -> ShowS # show :: HasUpdates -> String # showList :: [HasUpdates] -> ShowS # |
Downloading targets
:: (Throws SomeRemoteError, Throws VerificationError, Throws InvalidPackageException) | |
=> Repository down | Repository |
-> PackageIdentifier | Package to download |
-> Path Absolute | Destination (see also |
-> IO () |
Download a package
:: (Throws SomeRemoteError, Throws VerificationError, Throws InvalidPackageException) | |
=> Repository down | Repository |
-> PackageIdentifier | Package to download |
-> FilePath | Destination |
-> IO () |
Variation on downloadPackage
that takes a FilePath instead.
Access to the Hackage index
Index directory
Directory | |
|
newtype DirectoryEntry #
Entry into the Hackage index.
DirectoryEntry | |
|
Instances
Eq DirectoryEntry # | |
Defined in Hackage.Security.Client (==) :: DirectoryEntry -> DirectoryEntry -> Bool # (/=) :: DirectoryEntry -> DirectoryEntry -> Bool # | |
Ord DirectoryEntry # | |
Defined in Hackage.Security.Client compare :: DirectoryEntry -> DirectoryEntry -> Ordering # (<) :: DirectoryEntry -> DirectoryEntry -> Bool # (<=) :: DirectoryEntry -> DirectoryEntry -> Bool # (>) :: DirectoryEntry -> DirectoryEntry -> Bool # (>=) :: DirectoryEntry -> DirectoryEntry -> Bool # max :: DirectoryEntry -> DirectoryEntry -> DirectoryEntry # min :: DirectoryEntry -> DirectoryEntry -> DirectoryEntry # | |
Read DirectoryEntry # | |
Defined in Hackage.Security.Client readsPrec :: Int -> ReadS DirectoryEntry # readList :: ReadS [DirectoryEntry] # | |
Show DirectoryEntry # | |
Defined in Hackage.Security.Client showsPrec :: Int -> DirectoryEntry -> ShowS # show :: DirectoryEntry -> String # showList :: [DirectoryEntry] -> ShowS # |
getDirectory :: Repository down -> IO Directory #
Read the Hackage index directory
Should only be called after checkForUpdates
.
data IndexFile :: * -> * where #
Files that we might request from the index
The 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..
IndexPkgMetadata :: PackageIdentifier -> IndexFile (Signed Targets) | |
IndexPkgCabal :: PackageIdentifier -> IndexFile () | |
IndexPkgPrefs :: PackageName -> IndexFile () |
Instances
SomePretty IndexFile # | |
Defined in Hackage.Security.TUF.Layout.Index somePretty :: DictPretty (IndexFile a) # | |
SomeShow IndexFile # | |
Defined in Hackage.Security.TUF.Layout.Index | |
Show (IndexFile dec) # | |
Pretty (IndexFile dec) # | |
Defined in Hackage.Security.TUF.Layout.Index |
data IndexEntry dec #
Entry from the Hackage index; see withIndex
.
IndexEntry | |
|
data IndexCallbacks #
Various operations that we can perform on the index once its open
Note that IndexEntry
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.
IndexCallbacks | |
|
withIndex :: Repository down -> (IndexCallbacks -> IO a) -> IO a #
Look up entries in the Hackage index
This is in withFile
style so that clients can efficiently look up multiple
files from the index.
Should only be called after checkForUpdates
.
Bootstrapping
requiresBootstrap :: Repository down -> IO Bool #
Check if we need to bootstrap (i.e., if we have root info)
bootstrap :: (Throws SomeRemoteError, Throws VerificationError) => Repository down -> [KeyId] -> KeyThreshold -> IO () #
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 #
File length
Having verified file length information means we can protect against endless data attacks and similar.
Instances
Eq FileLength # | |
Defined in Hackage.Security.TUF.Common (==) :: FileLength -> FileLength -> Bool # (/=) :: FileLength -> FileLength -> Bool # | |
Ord FileLength # | |
Defined in Hackage.Security.TUF.Common compare :: FileLength -> FileLength -> Ordering # (<) :: FileLength -> FileLength -> Bool # (<=) :: FileLength -> FileLength -> Bool # (>) :: FileLength -> FileLength -> Bool # (>=) :: FileLength -> FileLength -> Bool # max :: FileLength -> FileLength -> FileLength # min :: FileLength -> FileLength -> FileLength # | |
Show FileLength # | |
Defined in Hackage.Security.TUF.Common showsPrec :: Int -> FileLength -> ShowS # show :: FileLength -> String # showList :: [FileLength] -> ShowS # | |
ReportSchemaErrors m => FromJSON m FileLength # | |
Defined in Hackage.Security.TUF.Common fromJSON :: JSValue -> m FileLength # | |
Monad m => ToJSON m FileLength # | |
Defined in Hackage.Security.TUF.Common toJSON :: FileLength -> m JSValue # |
File hash
newtype KeyThreshold #
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
.
Instances
Eq KeyThreshold # | |
Defined in Hackage.Security.TUF.Common (==) :: KeyThreshold -> KeyThreshold -> Bool # (/=) :: KeyThreshold -> KeyThreshold -> Bool # | |
Ord KeyThreshold # | |
Defined in Hackage.Security.TUF.Common compare :: KeyThreshold -> KeyThreshold -> Ordering # (<) :: KeyThreshold -> KeyThreshold -> Bool # (<=) :: KeyThreshold -> KeyThreshold -> Bool # (>) :: KeyThreshold -> KeyThreshold -> Bool # (>=) :: KeyThreshold -> KeyThreshold -> Bool # max :: KeyThreshold -> KeyThreshold -> KeyThreshold # min :: KeyThreshold -> KeyThreshold -> KeyThreshold # | |
Show KeyThreshold # | |
Defined in Hackage.Security.TUF.Common showsPrec :: Int -> KeyThreshold -> ShowS # show :: KeyThreshold -> String # showList :: [KeyThreshold] -> ShowS # | |
ReportSchemaErrors m => FromJSON m KeyThreshold # | |
Defined in Hackage.Security.TUF.Common fromJSON :: JSValue -> m KeyThreshold # | |
Monad m => ToJSON m KeyThreshold # | |
Defined in Hackage.Security.TUF.Common toJSON :: KeyThreshold -> m JSValue # |
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.
Instances
Show FileInfo # | |
ReportSchemaErrors m => FromJSON m FileInfo # | |
Defined in Hackage.Security.TUF.FileInfo | |
Monad m => ToJSON m FileInfo # | |
Defined in Hackage.Security.TUF.FileInfo |
Instances
Eq HashFn # | |
Ord HashFn # | |
Show HashFn # | |
ReportSchemaErrors m => FromObjectKey m HashFn # | |
Defined in Hackage.Security.TUF.FileInfo fromObjectKey :: String -> m (Maybe HashFn) # | |
Monad m => ToObjectKey m HashFn # | |
Defined in Hackage.Security.TUF.FileInfo toObjectKey :: HashFn -> m String # |
File hash
Utility
fileInfo :: ByteString -> FileInfo #
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.
knownFileInfoEqual :: FileInfo -> FileInfo -> Bool #
Re-exports
54-bit integer values
JavaScript 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 fromInteger
to do bounds checking, give different instances
for type classes such as Bounded
and FiniteBits
, etc.
Instances
data FileChange #
FileChanged FileInfo | File got added or modified; we record the new file info |
FileDeleted | File got deleted |
Instances
Show FileChange # | |
Defined in Hackage.Security.TUF.FileMap showsPrec :: Int -> FileChange -> ShowS # show :: FileChange -> String # showList :: [FileChange] -> ShowS # |
data TargetPath #
Entries in FileMap
either talk about the repository or the index
Instances
Mapping from paths to file info
File maps are used in target files; the paths are relative to the location of the target files containing the file map.
Instances
Show FileMap # | |
ReportSchemaErrors m => FromJSON m FileMap # | |
Defined in Hackage.Security.TUF.FileMap | |
Monad m => ToJSON m FileMap # | |
Defined in Hackage.Security.TUF.FileMap |
:: FileMap | Old |
-> FileMap | New |
-> Map TargetPath FileChange |
fileExpires :: Lens' a FileExpires #
File expiry date
fileVersion :: Lens' a FileVersion #
File version (monotonically increasing counter)
Instances
HasHeader Header # | |
Defined in Hackage.Security.TUF.Header | |
HasHeader Mirrors # | |
Defined in Hackage.Security.TUF.Mirrors | |
HasHeader Timestamp # | |
Defined in Hackage.Security.TUF.Timestamp | |
HasHeader Targets # | |
Defined in Hackage.Security.TUF.Targets | |
HasHeader Snapshot # | |
Defined in Hackage.Security.TUF.Snapshot | |
HasHeader Root # | |
Defined in Hackage.Security.TUF.Root |
newtype FileVersion #
File version
The file version is a flat integer which must monotonically increase on every file update.
Show
and Read
instance are defined in terms of the underlying Int
(this is use for example by hackage during the backup process).
Instances
newtype FileExpires #
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.)
Instances
Eq FileExpires # | |
Defined in Hackage.Security.TUF.Header (==) :: FileExpires -> FileExpires -> Bool # (/=) :: FileExpires -> FileExpires -> Bool # | |
Ord FileExpires # | |
Defined in Hackage.Security.TUF.Header compare :: FileExpires -> FileExpires -> Ordering # (<) :: FileExpires -> FileExpires -> Bool # (<=) :: FileExpires -> FileExpires -> Bool # (>) :: FileExpires -> FileExpires -> Bool # (>=) :: FileExpires -> FileExpires -> Bool # max :: FileExpires -> FileExpires -> FileExpires # min :: FileExpires -> FileExpires -> FileExpires # | |
Show FileExpires # | |
Defined in Hackage.Security.TUF.Header showsPrec :: Int -> FileExpires -> ShowS # show :: FileExpires -> String # showList :: [FileExpires] -> ShowS # | |
ReportSchemaErrors m => FromJSON m FileExpires # | |
Defined in Hackage.Security.TUF.Header fromJSON :: JSValue -> m FileExpires # | |
Monad m => ToJSON m FileExpires # | |
Defined in Hackage.Security.TUF.Header toJSON :: FileExpires -> m JSValue # |
Occassionally it is useful to read only a header from a file.
HeaderOnly
intentionally only has a FromJSON
instance (no ToJSON
).
Instances
HasHeader Header # | |
Defined in Hackage.Security.TUF.Header | |
ReportSchemaErrors m => FromJSON m Header # | |
Defined in Hackage.Security.TUF.Header |
Utility
expiresInDays :: UTCTime -> Integer -> FileExpires #
isExpired :: UTCTime -> FileExpires -> Bool #
Cache layout
data CacheLayout #
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 #
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).
Repository layout
data IndexLayout #
Layout of the files within the index tarball
IndexLayout | |
|
data IndexFile :: * -> * where #
Files that we might request from the index
The 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..
IndexPkgMetadata :: PackageIdentifier -> IndexFile (Signed Targets) | |
IndexPkgCabal :: PackageIdentifier -> IndexFile () | |
IndexPkgPrefs :: PackageName -> IndexFile () |
Instances
SomePretty IndexFile # | |
Defined in Hackage.Security.TUF.Layout.Index somePretty :: DictPretty (IndexFile a) # | |
SomeShow IndexFile # | |
Defined in Hackage.Security.TUF.Layout.Index | |
Show (IndexFile dec) # | |
Pretty (IndexFile dec) # | |
Defined in Hackage.Security.TUF.Layout.Index |
hackageIndexLayout :: IndexLayout #
The layout of the index as maintained on Hackage
Utility
indexLayoutPkgPrefs :: IndexLayout -> PackageName -> IndexPath #
Repository layout
data RepoLayout #
Layout of a repository
RepoLayout | |
|
Instances
MonadReader RepoLayout WriteJSON # | |
Defined in Hackage.Security.JSON ask :: WriteJSON RepoLayout # local :: (RepoLayout -> RepoLayout) -> WriteJSON a -> WriteJSON a # reader :: (RepoLayout -> a) -> WriteJSON a # | |
MonadReader RepoLayout ReadJSON_Keys_Layout # | |
Defined in Hackage.Security.JSON ask :: ReadJSON_Keys_Layout RepoLayout # local :: (RepoLayout -> RepoLayout) -> ReadJSON_Keys_Layout a -> ReadJSON_Keys_Layout a # reader :: (RepoLayout -> a) -> ReadJSON_Keys_Layout a # |
hackageRepoLayout :: RepoLayout #
The layout used on Hackage
cabalLocalRepoLayout :: RepoLayout #
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.
TUF types
Instances
HasHeader Mirrors # | |
Defined in Hackage.Security.TUF.Mirrors | |
VerifyRole Mirrors # | |
Defined in Hackage.Security.Trusted verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Mirrors -> Either VerificationError (SignaturesVerified Mirrors) # | |
(MonadError DeserializationError m, ReportSchemaErrors m) => FromJSON m Mirrors # | |
Defined in Hackage.Security.TUF.Mirrors | |
Monad m => ToJSON m Mirrors # | |
Defined in Hackage.Security.TUF.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
.
Instances
Show Mirror # | |
ReportSchemaErrors m => FromJSON m Mirror # | |
Defined in Hackage.Security.TUF.Mirrors | |
Monad m => ToJSON m Mirror # | |
Defined in Hackage.Security.TUF.Mirrors |
data MirrorContent #
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.
Instances
Show MirrorContent # | |
Defined in Hackage.Security.TUF.Mirrors showsPrec :: Int -> MirrorContent -> ShowS # show :: MirrorContent -> String # showList :: [MirrorContent] -> ShowS # |
Utility
type MirrorDescription = String #
describeMirror :: Mirror -> MirrorDescription #
Give a human-readable description of a particular mirror
(for use in error messages)
Repository
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
Web
-- 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.
anchorRepoPathLocally :: Path root -> RepoPath -> Path root #
Index
The root of the index tarball
Cache
The cache directory
anchorCachePath :: Path root -> CachePath -> Path root #
Anchor a cache path to the location of the cache
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 | |
|
Instances
HasHeader Root # | |
Defined in Hackage.Security.TUF.Root | |
VerifyRole Root # | |
Defined in Hackage.Security.Trusted verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Root -> Either VerificationError (SignaturesVerified Root) # | |
Monad m => ToJSON m Root # | |
Defined in Hackage.Security.TUF.Root | |
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. |
Role specification
The phantom type indicates what kind of type this role is meant to verify.
TUF types
Signed | |
|
Instances
MonadKeys m => FromJSON m (Signed Mirrors) # | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Timestamp) # | |
MonadKeys m => FromJSON m (Signed Targets) # | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Snapshot) # | |
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) # | |
Defined in Hackage.Security.TUF.Signed |
newtype Signatures #
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.
Instances
MonadKeys m => FromJSON m Signatures # | |
Defined in Hackage.Security.TUF.Signed fromJSON :: JSValue -> m Signatures # | |
Monad m => ToJSON m Signatures # | |
Defined in Hackage.Security.TUF.Signed toJSON :: Signatures -> m JSValue # |
Construction and verification
withSignatures :: ToJSON WriteJSON a => RepoLayout -> [Some Key] -> a -> Signed a #
Sign a document
withSignatures' :: ToJSON Identity a => [Some Key] -> a -> Signed a #
Variation on withSignatures
that doesn't need the repo layout
signRendered :: [Some Key] -> ByteString -> Signatures #
Construct signatures for already rendered value
verifySignature :: ByteString -> Signature -> Bool #
JSON aids
signedFromJSON :: (MonadKeys m, FromJSON m a) => JSValue -> m (Signed a) #
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 #
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 #
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.
Instances
(ReportSchemaErrors m, FromJSON m a) => FromJSON m (UninterpretedSignatures a) # | |
Defined in Hackage.Security.TUF.Signed fromJSON :: JSValue -> m (UninterpretedSignatures a) # | |
(Monad m, ToJSON m a) => ToJSON m (UninterpretedSignatures a) # | |
Defined in Hackage.Security.TUF.Signed toJSON :: UninterpretedSignatures a -> m JSValue # | |
Show a => Show (UninterpretedSignatures a) # | |
Defined in Hackage.Security.TUF.Signed showsPrec :: Int -> UninterpretedSignatures a -> ShowS # show :: UninterpretedSignatures a -> String # showList :: [UninterpretedSignatures a] -> ShowS # |
data PreSignature #
A signature with a key ID (rather than an actual key)
This corresponds precisely to the TUF representation of a signature.
Instances
Show PreSignature # | |
Defined in Hackage.Security.TUF.Signed showsPrec :: Int -> PreSignature -> ShowS # show :: PreSignature -> String # showList :: [PreSignature] -> ShowS # | |
ReportSchemaErrors m => FromJSON m PreSignature # | |
Defined in Hackage.Security.TUF.Signed fromJSON :: JSValue -> m PreSignature # | |
Monad m => ToJSON m PreSignature # | |
Defined in Hackage.Security.TUF.Signed toJSON :: PreSignature -> m JSValue # |
Utility
fromPreSignature :: MonadKeys m => PreSignature -> m Signature #
Convert a pre-signature to a signature
Verifies that the key type matches the advertised method.
fromPreSignatures :: MonadKeys m => [PreSignature] -> m Signatures #
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 #
Convert signature to pre-signature
toPreSignatures :: Signatures -> [PreSignature] #
Convert list of pre-signatures to a list of signatures
Snapshot | |
|
Instances
HasHeader Snapshot # | |
Defined in Hackage.Security.TUF.Snapshot | |
VerifyRole Snapshot # | |
Defined in Hackage.Security.Trusted verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Snapshot -> Either VerificationError (SignaturesVerified Snapshot) # | |
(MonadReader RepoLayout m, MonadError DeserializationError m, ReportSchemaErrors m) => FromJSON m Snapshot # | |
Defined in Hackage.Security.TUF.Snapshot | |
MonadReader RepoLayout m => ToJSON m Snapshot # | |
Defined in Hackage.Security.TUF.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 #
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
.
Instances
Show Delegations # | |
Defined in Hackage.Security.TUF.Targets showsPrec :: Int -> Delegations -> ShowS # show :: Delegations -> String # showList :: [Delegations] -> ShowS # | |
MonadKeys m => FromJSON m Delegations # | |
Defined in Hackage.Security.TUF.Targets fromJSON :: JSValue -> m Delegations # | |
Monad m => ToJSON m Delegations # | |
Defined in Hackage.Security.TUF.Targets toJSON :: Delegations -> m JSValue # |
data DelegationSpec #
Delegation specification
NOTE: This is a close analogue of RoleSpec
.
Instances
Show DelegationSpec # | |
Defined in Hackage.Security.TUF.Targets showsPrec :: Int -> DelegationSpec -> ShowS # show :: DelegationSpec -> String # showList :: [DelegationSpec] -> ShowS # | |
MonadKeys m => FromJSON m DelegationSpec # | |
Defined in Hackage.Security.TUF.Targets fromJSON :: JSValue -> m DelegationSpec # | |
Monad m => ToJSON m DelegationSpec # | |
Defined in Hackage.Security.TUF.Targets toJSON :: DelegationSpec -> m JSValue # |
data Delegation #
A delegation
A delegation is a pair of a pattern and a replacement.
See match
for an example.
Delegation (Pattern a) (Replacement a) |
Instances
Show Delegation # | |
Defined in Hackage.Security.TUF.Patterns showsPrec :: Int -> Delegation -> ShowS # show :: Delegation -> String # showList :: [Delegation] -> ShowS # | |
Lift Delegation # | |
Defined in Hackage.Security.TUF.Patterns lift :: Delegation -> Q Exp # |
Util
targetsLookup :: TargetPath -> Targets -> Maybe FileInfo #
Instances
HasHeader Timestamp # | |
Defined in Hackage.Security.TUF.Timestamp | |
VerifyRole Timestamp # | |
Defined in Hackage.Security.Trusted verifyRole :: Trusted Root -> TargetPath -> Maybe FileVersion -> Maybe UTCTime -> Signed Timestamp -> Either VerificationError (SignaturesVerified Timestamp) # | |
(MonadReader RepoLayout m, MonadError DeserializationError m, ReportSchemaErrors m) => FromJSON m Timestamp # | |
Defined in Hackage.Security.TUF.Timestamp | |
MonadReader RepoLayout m => ToJSON m Timestamp # | |
Defined in Hackage.Security.TUF.Timestamp | |
(MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Timestamp) # | |
Key types
Types abstracting over key types
KeyEd25519 :: PublicKey -> SecretKey -> Key Ed25519 |
Instances
SomeShow Key # | |
Defined in Hackage.Security.Key | |
SomeEq Key # | |
Defined in Hackage.Security.Key | |
HasKeyId Key # | |
Defined in Hackage.Security.Key | |
ReportSchemaErrors m => FromJSON m (Some Key) # | |
Monad m => ToJSON m (Some Key) # | |
Monad m => ToJSON m (Key typ) # | |
Defined in Hackage.Security.Key | |
Eq (Key typ) # | |
Show (Key typ) # | |
Instances
SomeShow PublicKey # | |
Defined in Hackage.Security.Key | |
SomeEq PublicKey # | |
Defined in Hackage.Security.Key | |
HasKeyId PublicKey # | |
Defined in Hackage.Security.Key | |
ReportSchemaErrors m => FromJSON m (Some PublicKey) # | |
Monad m => ToJSON m (Some PublicKey) # | |
Monad m => ToJSON m (PublicKey typ) # | |
Defined in Hackage.Security.Key | |
Eq (PublicKey typ) # | |
Show (PublicKey typ) # | |
data PrivateKey a where #
Instances
SomeShow PrivateKey # | |
Defined in Hackage.Security.Key someShow :: DictShow (PrivateKey a) # | |
SomeEq PrivateKey # | |
Defined in Hackage.Security.Key someEq :: DictEq (PrivateKey a) # | |
Eq (PrivateKey typ) # | |
Defined in Hackage.Security.Key (==) :: PrivateKey typ -> PrivateKey typ -> Bool # (/=) :: PrivateKey typ -> PrivateKey typ -> Bool # | |
Show (PrivateKey typ) # | |
Defined in Hackage.Security.Key showsPrec :: Int -> PrivateKey typ -> ShowS # show :: PrivateKey typ -> String # showList :: [PrivateKey typ] -> ShowS # |
Key types in isolation
Instances
SomeShow KeyType # | |
Defined in Hackage.Security.Key | |
SomeEq KeyType # | |
Defined in Hackage.Security.Key | |
ReportSchemaErrors m => FromJSON m (Some KeyType) # | |
Monad m => ToJSON m (KeyType typ) # | |
Defined in Hackage.Security.Key | |
Monad m => ToJSON m (Some KeyType) # | |
Eq (KeyType typ) # | |
Show (KeyType typ) # | |
Hiding key types
Operations on keys
privateKey :: Key a -> PrivateKey a #
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.
Instances
Eq KeyId # | |
Ord KeyId # | |
Show KeyId # | |
Monad m => FromObjectKey m KeyId # | |
Defined in Hackage.Security.Key fromObjectKey :: String -> m (Maybe KeyId) # | |
Monad m => ToObjectKey m KeyId # | |
Defined in Hackage.Security.Key toObjectKey :: KeyId -> m String # |
Compute the key ID of a key
Signing
sign :: PrivateKey typ -> ByteString -> ByteString #
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 #
We only a few bits from .Repository
data Repository down #
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.
Instances
Show (Repository down) # | |
Defined in Hackage.Security.Client.Repository showsPrec :: Int -> Repository down -> ShowS # show :: Repository down -> String # showList :: [Repository down] -> ShowS # |
class DownloadedFile (down :: * -> *) where #
downloadedVerify :: down a -> Trusted FileInfo -> IO Bool #
Verify a download file
downloadedRead :: down Metadata -> IO ByteString #
Read the file we just downloaded into memory
We never read binary data, only metadata.
downloadedCopyTo :: down a -> Path Absolute -> IO () #
Copy a downloaded file to its destination
Instances
DownloadedFile RemoteTemp # | |
Defined in Hackage.Security.Client.Repository.Remote downloadedVerify :: RemoteTemp a -> Trusted FileInfo -> IO Bool # downloadedRead :: RemoteTemp Metadata -> IO ByteString # downloadedCopyTo :: RemoteTemp a -> Path Absolute -> IO () # | |
DownloadedFile LocalFile # | |
Defined in Hackage.Security.Client.Repository.Local downloadedVerify :: LocalFile a -> Trusted FileInfo -> IO Bool # downloadedRead :: LocalFile Metadata -> IO ByteString # |
data SomeRemoteError :: * where #
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 |
Instances
Show SomeRemoteError # | |
Defined in Hackage.Security.Client.Repository showsPrec :: Int -> SomeRemoteError -> ShowS # show :: SomeRemoteError -> String # showList :: [SomeRemoteError] -> ShowS # | |
Exception SomeRemoteError # | |
Defined in Hackage.Security.Client.Repository | |
Pretty SomeRemoteError # | |
Defined in Hackage.Security.Client.Repository pretty :: SomeRemoteError -> String # |
data LogMessage #
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 (RemoteFile fs typ) | Download a file from a repository |
LogUpdating (RemoteFile fs Binary) | Incrementally updating a file from a repository |
LogSelectedMirror MirrorDescription | Selected a particular mirror |
LogCannotUpdate (RemoteFile fs Binary) UpdateFailure | Updating a file failed (we will instead download it whole) |
LogMirrorFailed MirrorDescription SomeException | We got an exception with a particular mirror (we will try with a different mirror if any are available) |
Instances
Pretty LogMessage # | |
Defined in Hackage.Security.Client.Repository pretty :: LogMessage -> String # |
Exceptions
uncheckClientErrors :: ((Throws VerificationError, Throws SomeRemoteError, Throws InvalidPackageException) => IO a) -> IO a #
Re-throw all exceptions thrown by the client API as unchecked exceptions
data VerificationError #
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 |
VerificationErrorMissingSHA256 TargetPath | The metadata for the specified target is missing a SHA256 |
VerificationErrorDeserialization TargetPath DeserializationError | Some verification errors materialize as deserialization errors For example: if we try to deserialize a timestamp file but the timestamp
key has been rolled over, deserialization of the file will fail with
|
VerificationErrorLoop VerificationHistory | The spec stipulates that if a verification error occurs during the check for updates, we must download new root information and start over. However, we limit how often we attempt this. We record all verification errors that occurred before we gave up. |
Instances
Show VerificationError # | |
Defined in Hackage.Security.Trusted.TCB showsPrec :: Int -> VerificationError -> ShowS # show :: VerificationError -> String # showList :: [VerificationError] -> ShowS # | |
Exception VerificationError # | |
Defined in Hackage.Security.Trusted.TCB | |
Pretty VerificationError # | |
Defined in Hackage.Security.Trusted.TCB pretty :: VerificationError -> String # |
data RootUpdated #
Root metadata updated (as part of the normal update process)
Instances
Show RootUpdated # | |
Defined in Hackage.Security.Trusted.TCB showsPrec :: Int -> RootUpdated -> ShowS # show :: RootUpdated -> String # showList :: [RootUpdated] -> ShowS # | |
Exception RootUpdated # | |
Defined in Hackage.Security.Trusted.TCB | |
Pretty RootUpdated # | |
Defined in Hackage.Security.Trusted.TCB pretty :: RootUpdated -> String # |
data InvalidPackageException #
Instances
Show InvalidPackageException # | |
Defined in Hackage.Security.Client showsPrec :: Int -> InvalidPackageException -> ShowS # show :: InvalidPackageException -> String # showList :: [InvalidPackageException] -> ShowS # | |
Exception InvalidPackageException # | |
Pretty InvalidPackageException # | |
Defined in Hackage.Security.Client pretty :: InvalidPackageException -> String # |
data InvalidFileInIndex #
Instances
Show InvalidFileInIndex # | |
Defined in Hackage.Security.Client showsPrec :: Int -> InvalidFileInIndex -> ShowS # show :: InvalidFileInIndex -> String # showList :: [InvalidFileInIndex] -> ShowS # | |
Exception InvalidFileInIndex # | |
Defined in Hackage.Security.Client | |
Pretty InvalidFileInIndex # | |
Defined in Hackage.Security.Client pretty :: InvalidFileInIndex -> String # |
data LocalFileCorrupted #
Instances
Show LocalFileCorrupted # | |
Defined in Hackage.Security.Client showsPrec :: Int -> LocalFileCorrupted -> ShowS # show :: LocalFileCorrupted -> String # showList :: [LocalFileCorrupted] -> ShowS # | |
Exception LocalFileCorrupted # | |
Defined in Hackage.Security.Client | |
Pretty LocalFileCorrupted # | |
Defined in Hackage.Security.Client pretty :: LocalFileCorrupted -> String # |