never executed always true always false
1 {-# LANGUAGE CPP #-}
2 {-# LANGUAGE DeriveDataTypeable #-}
3 {-# LANGUAGE DeriveGeneric #-}
4 module Distribution.Client.HashValue (
5 HashValue,
6 hashValue,
7 truncateHash,
8 showHashValue,
9 readFileHashValue,
10 hashFromTUF,
11 ) where
12
13 import Distribution.Client.Compat.Prelude
14 import Prelude ()
15
16 import qualified Hackage.Security.Client as Sec
17
18 import qualified Crypto.Hash.SHA256 as SHA256
19 import qualified Data.ByteString.Base16 as Base16
20 import qualified Data.ByteString.Char8 as BS
21 import qualified Data.ByteString.Lazy.Char8 as LBS
22
23 import System.IO (IOMode (..), withBinaryFile)
24
25 -----------------------------------------------
26 -- The specific choice of hash implementation
27 --
28
29 -- Is a crypto hash necessary here? One thing to consider is who controls the
30 -- inputs and what's the result of a hash collision. Obviously we should not
31 -- install packages we don't trust because they can run all sorts of code, but
32 -- if I've checked there's no TH, no custom Setup etc, is there still a
33 -- problem? If someone provided us a tarball that hashed to the same value as
34 -- some other package and we installed it, we could end up re-using that
35 -- installed package in place of another one we wanted. So yes, in general
36 -- there is some value in preventing intentional hash collisions in installed
37 -- package ids.
38
39 newtype HashValue = HashValue BS.ByteString
40 deriving (Eq, Generic, Show, Typeable)
41
42 -- Cannot do any sensible validation here. Although we use SHA256
43 -- for stuff we hash ourselves, we can also get hashes from TUF
44 -- and that can in principle use different hash functions in future.
45 --
46 -- Therefore, we simply derive this structurally.
47 instance Binary HashValue
48 instance Structured HashValue
49
50 -- | Hash some data. Currently uses SHA256.
51 --
52 hashValue :: LBS.ByteString -> HashValue
53 hashValue = HashValue . SHA256.hashlazy
54
55 showHashValue :: HashValue -> String
56 showHashValue (HashValue digest) = BS.unpack (Base16.encode digest)
57
58 -- | Hash the content of a file. Uses SHA256.
59 --
60 readFileHashValue :: FilePath -> IO HashValue
61 readFileHashValue tarball =
62 withBinaryFile tarball ReadMode $ \hnd ->
63 evaluate . hashValue =<< LBS.hGetContents hnd
64
65 -- | Convert a hash from TUF metadata into a 'PackageSourceHash'.
66 --
67 -- Note that TUF hashes don't neessarily have to be SHA256, since it can
68 -- support new algorithms in future.
69 --
70 hashFromTUF :: Sec.Hash -> HashValue
71 hashFromTUF (Sec.Hash hashstr) =
72 --TODO: [code cleanup] either we should get TUF to use raw bytestrings or
73 -- perhaps we should also just use a base16 string as the internal rep.
74 case Base16.decode (BS.pack hashstr) of
75 #if MIN_VERSION_base16_bytestring(1,0,0)
76 Right hash -> HashValue hash
77 Left _ -> error "hashFromTUF: cannot decode base16"
78 #else
79 (hash, trailing) | not (BS.null hash) && BS.null trailing
80 -> HashValue hash
81 _ -> error "hashFromTUF: cannot decode base16 hash"
82 #endif
83
84 -- | Truncate a 32 byte SHA256 hash to
85 --
86 -- For example 20 bytes render as 40 hex chars, which we use for unit-ids.
87 -- Or even 4 bytes for 'hashedInstalledPackageIdShort'
88 --
89 truncateHash :: Int -> HashValue -> HashValue
90 truncateHash n (HashValue h) = HashValue (BS.take n h)