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)