executable-hash (empty) → 0.1.1.1
raw patch · 11 files changed
+277/−0 lines, 11 filesdep +basedep +bytestringdep +cryptohashbuild-type:Customsetup-changed
Dependencies added: base, bytestring, cryptohash, directory, executable-hash, executable-path, file-embed
Files
- ChangeLog.md +4/−0
- LICENSE +21/−0
- README.md +35/−0
- Setup.hs +9/−0
- System/Executable/Hash.hs +60/−0
- System/Executable/Hash/Internal.hs +50/−0
- app/inject-executable-hash.hs +11/−0
- executable-hash.cabal +64/−0
- test/test-compute.hs +9/−0
- test/test-inject.hs +7/−0
- test/test-no-inject.hs +7/−0
+ ChangeLog.md view
@@ -0,0 +1,4 @@+0.1.1.1+=======++First public version
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License (MIT)++Copyright (c) 2015 FP Complete++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,35 @@+# executable-hash++Provides the SHA1 hash of the executable. This hash can either be+injected into the executable as a step after compilation, or+calculated at runtime.++## Usage++The main function expected to be used by the user is+`System.Executable.Hash.executableHash`. This function yields a SHA1+hash determined by the contents of the executable. However, note that+this may not be the actual SHA1 of the executable, since the hash can+be injected into the executable (which changes its "actual" hash).++Installing this package will also install the `inject-executable-hash`+executable. Running this program on a binary, like+`inject-executable-hash <binary-name>` will replace a dummy+`ByteString` (via the `file-embed` package) in the binary with its+hash.++Alternatively, you can put this in a `Setup.hs` file, and set+`build-type: Custom` in your `.cabal`:++```haskell+import Distribution.Simple+import System.Executable.Hash.Internal (maybeInjectExecutableHash)++main :: IO ()+main = defaultMainWithHooks $ simpleUserHooks+ { postBuild = \_ _ _ _ ->+ maybeInjectExecutableHash "dist/build/path-to/your-executable"+ }+```++(Note: you'll need to change the executable path)
+ Setup.hs view
@@ -0,0 +1,9 @@+import Distribution.Simple+import System.Executable.Hash.Internal (maybeInjectExecutableHash)++main :: IO ()+main = defaultMainWithHooks $ simpleUserHooks+ { postBuild = \_ _ _ _ ->+ maybeInjectExecutableHash "dist/build/test-inject/test-inject"+ }+
+ System/Executable/Hash.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE TemplateHaskell #-}++-- | This module provides functions for accessing or computing a SHA1+-- hash of the program's executable. Most users are expected to use+-- the 'executableHash' function.+--+-- To inject the hash into the executable, you can use the+-- @inject-executable-hash@ program installed along with this package.+-- Alternatively, to do this automatically with cabal, place this in+-- your @Setup.hs@:+--+-- @+-- import Distribution.Simple+-- import System.Executable.Hash.Internal (maybeInjectExecutableHash)+--+-- main :: IO ()+-- main = defaultMainWithHooks $ simpleUserHooks+-- { postBuild = \_ _ _ _ ->+-- maybeInjectExecutableHash "dist\/build\/path-to\/your-executable"+-- }+-- @+--+-- (Note: you'll need to change the executable path)+module System.Executable.Hash+ ( executableHash+ , computeExecutableHash+ ) where++import Control.Applicative ((<$>))+import Crypto.Hash.SHA1 (hash)+import qualified Data.ByteString as BS+import System.Environment.Executable (getScriptPath, ScriptPath(..))+import System.Executable.Hash.Internal++-- | If a SHA1 hash of the executable has been injected into it, then+-- it's directly yielded by this function. Otherwise, a hash is+-- computed with 'computeExecutableHash'.+--+-- Note that you shouldn't rely on the result being the actual SHA1+-- hash of the executable, because injecting the hash modifies the+-- binary, and so changes the result of 'computeExecutableHash'.+-- Instead, this should only be used as a way to uniquely identify the+-- contents of the executable.+--+-- This yields 'Nothing' when run with @runhaskell@ or @ghci@.+executableHash :: IO (Maybe BS.ByteString)+executableHash =+ case injectedExecutableHash of+ Just x -> return (Just x)+ Nothing -> computeExecutableHash++-- | Computes the SHA1 hash of the program executable.+--+-- This yields 'Nothing' when run with @runhaskell@ or @ghci@.+computeExecutableHash :: IO (Maybe BS.ByteString)+computeExecutableHash = do+ sp <- getScriptPath+ case sp of+ Executable fp -> Just . hash <$> BS.readFile fp+ _ -> return Nothing
+ System/Executable/Hash/Internal.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Internals related to reading and writing an injected executable+-- hash.+module System.Executable.Hash.Internal where++import Crypto.Hash.SHA1 (hash)+import qualified Data.ByteString as BS+import Data.FileEmbed (dummySpaceWith, injectWith)+import System.Directory (doesFileExist)++-- | Yields a 'Just' value of a hash which has been injected into the+-- executable via 'injectExecutableHash'.+injectedExecutableHash :: Maybe BS.ByteString+injectedExecutableHash+ | BS.all (== toEnum (fromEnum '0')) bs = Nothing+ | otherwise = Just bs+ where+ bs = $(dummySpaceWith "executable-hash" 20)++-- | Given the path to an executable, computes its hash and injects it+-- into the binary, such that when that program demands the value of+-- 'injectedExecutableHash', it yields a 'Just' value.+--+-- See the documentation in "System.Executable.Hash" for an example of+-- how to use this with a cabal @postBuild@ hook+injectExecutableHash :: FilePath -> IO ()+injectExecutableHash fp = do+ binary <- BS.readFile fp+ let sha1 = hash binary+ case injectWith "executable-hash" sha1 binary of+ Nothing -> fail "Impossible: dummy space too small for executable-hash."+ Just binary' -> do+ BS.writeFile fp binary'+ putStrLn $ "Successfully wrote " ++ fp ++ " with injected hash."++-- | Injects an executable hash into the specified binary. If it+-- doesn't exist, then this prints a message to stdout indicating that+-- it failed to inject the hash.+maybeInjectExecutableHash :: FilePath -> IO ()+maybeInjectExecutableHash fp = do+ exists <- doesFileExist fp+ if exists+ then injectExecutableHash fp+ else putStrLn $ concat+ [ "Not injecting executable hash into "+ , fp+ , ", as it doesn't exist."+ ]
+ app/inject-executable-hash.hs view
@@ -0,0 +1,11 @@+module Main where++import System.Environment (getArgs)+import System.Executable.Hash.Internal (injectExecutableHash)++main :: IO ()+main = do+ args <- getArgs+ case args of+ [exe] -> injectExecutableHash exe+ _ -> error "Usage: inject-executable-hash <executable>"
+ executable-hash.cabal view
@@ -0,0 +1,64 @@+name: executable-hash+version: 0.1.1.1+synopsis: Provides the SHA1 hash of the program executable+description: See README.md+homepage: https://github.com/fpco/executable-hash+license: MIT+license-file: LICENSE+copyright: 2015 FP Complete+author: Michael Sloan+maintainer: FP Complete <sloan@fpcomplete.com>+homepage: http://github.com/fpco/executable-hash+bug-reports: http://github.com/fpco/executable-hash/issues+category: System+stability: Stable+build-type: Custom+extra-source-files: README.md, ChangeLog.md+cabal-version: >= 1.10+source-repository head+ type: git+ location: git://github.com/fpco/executable-hash++library+ exposed-modules: System.Executable.Hash+ , System.Executable.Hash.Internal+ build-depends: base >= 4.0 && < 5.0+ , executable-path >= 0.0.3 && < 0.1+ , file-embed >= 0.0.8 && < 0.1+ , bytestring+ , cryptohash+ , directory+ default-language: Haskell2010+ ghc-options: -Wall+++executable inject-executable-hash+ main-is: inject-executable-hash.hs+ hs-source-dirs: app+ default-language: Haskell2010+ build-depends: base, executable-hash+ ghc-options: -Wall++test-suite test-compute+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: test-compute.hs+ build-depends: base, executable-hash+ default-language: Haskell2010+ ghc-options: -Wall++test-suite test-inject+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: test-inject.hs+ build-depends: base, executable-hash+ default-language: Haskell2010+ ghc-options: -Wall++test-suite test-no-inject+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: test-no-inject.hs+ build-depends: base, executable-hash+ default-language: Haskell2010+ ghc-options: -Wall
+ test/test-compute.hs view
@@ -0,0 +1,9 @@+import System.Executable.Hash++main :: IO ()+main = do+ mhash <- computeExecutableHash+ case mhash of+ Nothing ->+ fail "Failed to compute executable hash. This should only happen when running in GHCI."+ Just _ -> return ()
+ test/test-inject.hs view
@@ -0,0 +1,7 @@+import System.Executable.Hash.Internal (injectedExecutableHash)++main :: IO ()+main =+ case injectedExecutableHash of+ Nothing -> fail "Expected executable hash."+ Just _ -> return ()
+ test/test-no-inject.hs view
@@ -0,0 +1,7 @@+import System.Executable.Hash.Internal (injectedExecutableHash)++main :: IO ()+main =+ case injectedExecutableHash of+ Nothing -> return ()+ Just x -> fail $ "Expected no injected executable hash. Instead got: " ++ show x