packages feed

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 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