multihash (empty) → 0.1.0
raw patch · 7 files changed
+323/−0 lines, 7 filesdep +attoparsecdep +basedep +base58-bytestringsetup-changed
Dependencies added: attoparsec, base, base58-bytestring, base64-bytestring, byteable, bytestring, cryptohash, hex, io-streams, multihash, optparse-applicative
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- main/Main.hs +110/−0
- multihash.cabal +60/−0
- src/Data/Multihash/Base.hs +33/−0
- src/Data/Multihash/Digest.hs +76/−0
- src/System/IO/Streams/Crypto.hs +12/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Luke Hoersten++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Luke Hoersten nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ main/Main.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Crypto.Hash (Digest)+import qualified Crypto.Hash as CH+import Data.Byteable (toBytes)+import Data.ByteString (ByteString)+import Data.ByteString.Lazy (fromStrict, toStrict)+import Options.Applicative+import System.Exit (exitFailure, exitSuccess)++import System.IO.Streams (InputStream, stdin, stdout,+ withFileAsInput, write)+import System.IO.Streams.Crypto (hashInputStream)++import qualified Data.Multihash.Base as MB+import qualified Data.Multihash.Digest as MH+++data Termination = Null | Newline deriving (Show, Eq)+data Config =+ Config+ { cfFile :: Maybe FilePath+ , cfAlgo :: MH.HashAlgorithm+ , cfBase :: MB.BaseEncoding+ , cfHash :: Maybe MH.Digest+ , cfTerm :: Termination+ } deriving Show+++main :: IO ()+main = do+ -- TODO add file checking+ config <- execParser opts+ digest <- maybe (hashStdin config) (hashFile config) $ cfFile config+ write (multihash config digest) stdout+ where+ hashStdin config = hash (cfAlgo config) stdin+ hashFile config file = withFileAsInput file . hash $ cfAlgo config+ multihash (Config _file algo base _hash term) =+ Just . toStrict . line term . MB.encode base . MH.encode algo++ line Null = (<> "\0")+ line Newline = (<> "\n")+++-- TODO add BLAKE support+hash :: MH.HashAlgorithm -> InputStream ByteString -> IO MH.Digest+hash MH.SHA1 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA1))+hash MH.SHA256 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA256))+hash MH.SHA512 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA512))+hash MH.SHA3 is = toBytes <$> (hashInputStream is :: IO (Digest CH.SHA3_256))+hash MH.BLAKE2B _ = undefined+hash MH.BLAKE2S _ = undefined+++opts :: ParserInfo Config+opts = info+ (helper <*> (Config+ <$> fileArg+ <*> algoOpt+ <*> baseOpt+ <*> checkOpt+ <*> nullTermFlag+ ))+ (fullDesc+ <> header "Generate a multihash for the given input."+ <> progDesc "Hash from FILE or stdin if not given.")+++algoOpt :: Parser MH.HashAlgorithm+algoOpt =+ option auto+ $ long "algorithm"+ <> short 'a'+ <> metavar "ALGO"+ <> showDefault <> value MH.SHA256+ <> help ("Hash algorithm to apply to input, ignored if checking hash " <> show ([minBound..] :: [MH.HashAlgorithm]))+++baseOpt :: Parser MB.BaseEncoding+baseOpt =+ option auto+ $ long "encoding"+ <> short 'e'+ <> metavar "ENCODING"+ <> showDefault <> value MB.Base58+ <> help ("Base encoding of output digest, ignored if checking hash " <> show ([minBound..] :: [MB.BaseEncoding]))+++checkOpt :: Parser (Maybe MH.Digest)+checkOpt =+ optional . option auto+ $ long "check"+ <> short 'c'+ <> metavar "DIGEST"+ <> help "Check for matching digest"+++nullTermFlag :: Parser Termination+nullTermFlag =+ flag Newline Null+ $ long "print0"+ <> short '0'+ <> help "End filenames with NUL, for use with xargs"+++fileArg :: Parser (Maybe FilePath)+fileArg = optional . argument str $ metavar "FILE"
+ multihash.cabal view
@@ -0,0 +1,60 @@+name: multihash+version: 0.1.0+synopsis: Multihash library and CLI executable+description: Multihash is a protocol for encoding the hash algorithm+ and digest length at the start of the digest.+ More information available at https:\/\/github.com\/jbenet\/multihash\/.+ .+ Base32 encoding, Blake hashing, and file checking still to be added.+ .+license: BSD3+license-file: LICENSE+author: Luke Hoersten+maintainer: luke@hoersten.org+copyright: 2015 Luke Hoersten+category: Data+build-type: Simple+cabal-version: >=1.10++source-repository head+ type: git+ location: git://github.com/LukeHoersten/multihash.git+++library+ exposed-modules: Data.Multihash.Base+ , Data.Multihash.Digest+ , System.IO.Streams.Crypto++ hs-source-dirs: src+ default-language: Haskell2010++ build-depends: attoparsec >= 0.12 && < 0.13+ , base >= 4.8 && < 4.9+ -- , base32-bytestring >= 0.2 && < 0.3+ , base58-bytestring >= 0.1 && < 0.2+ , base64-bytestring >= 1.0 && < 1.1+ , bytestring >= 0.10 && < 0.11+ , cryptohash >= 0.11 && < 0.12+ , hex >= 0.1 && < 0.2+ , io-streams >= 1.2 && < 1.3+++executable multihash+ main-is: Main.hs+ hs-source-dirs: main+ default-language: Haskell2010+ ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2 -fdicts-cheap+ -fno-warn-orphans -fno-warn-unused-do-bind -rtsopts++ build-depends: base >= 4.8 && < 4.9+ -- , base32-bytestring >= 0.2 && < 0.3+ , base64-bytestring >= 1.0 && < 1.1+ , base58-bytestring >= 0.1 && < 0.2+ , byteable >= 0.1 && < 0.2+ , bytestring >= 0.10 && < 0.11+ , cryptohash >= 0.11 && < 0.12+ , hex >= 0.1 && < 0.2+ , io-streams >= 1.2 && < 1.3+ , multihash >= 0.1 && < 0.2+ , optparse-applicative >= 0.11 && < 0.12
+ src/Data/Multihash/Base.hs view
@@ -0,0 +1,33 @@+module Data.Multihash.Base where++-- import qualified Data.ByteString.Base32 as B32+import qualified Data.ByteString.Base58 as B58+import qualified Data.ByteString.Base64.Lazy as B64+import Data.ByteString.Lazy (ByteString, fromStrict, toStrict)+import Data.Either (Either)+import qualified Data.Hex as B16+++-- TODO add Base32 encoding+data BaseEncoding+ = Base2 -- ^ Raw binary encoding+ | Base16 -- ^ Hexadecimal encoding+ | Base58 -- ^ Bitcoin encoding+ | Base64+ deriving (Show, Read, Eq, Enum, Bounded)+++encode :: BaseEncoding -> ByteString -> ByteString+encode Base2 = id+encode Base16 = B16.hex+-- encode Base32 = B32.encode+encode Base58 = fromStrict . B58.encodeBase58 B58.bitcoinAlphabet . toStrict+encode Base64 = B64.encode+++decode :: BaseEncoding -> ByteString -> Either String ByteString+decode Base2 = return . id+decode Base16 = B16.unhex+-- decode Base32 = B32.decode+decode Base58 = maybe (Left "Failed to parse") (Right . fromStrict) . B58.decodeBase58 B58.bitcoinAlphabet . toStrict+decode Base64 = B64.decode
+ src/Data/Multihash/Digest.hs view
@@ -0,0 +1,76 @@+module Data.Multihash.Digest where+++import Data.Attoparsec.ByteString (Parser, parseOnly)+import qualified Data.Attoparsec.ByteString as A+import qualified Data.ByteString as BS+import Data.ByteString.Builder (Builder, byteString,+ toLazyByteString)+import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Lazy as BL+import Data.Monoid ((<>))+import Data.Word (Word8)+++data MultihashDigest =+ MultihashDigest+ { algorithm :: !HashAlgorithm+ , lenght :: !Length+ , digest :: !Digest+ } deriving (Show, Eq)+++type Length = Int+type Digest = BS.ByteString+++data HashAlgorithm+ = SHA1+ | SHA256+ | SHA512+ | SHA3+ | BLAKE2B+ | BLAKE2S+ deriving (Show, Read, Eq, Enum, Bounded)+++fromCode :: Int -> HashAlgorithm+fromCode 0x11 = SHA1+fromCode 0x12 = SHA256+fromCode 0x13 = SHA512+fromCode 0x14 = SHA3+fromCode 0x40 = BLAKE2B+fromCode 0x41 = BLAKE2S+fromCode _ = error "Unknown hash funciton code"+++toCode :: HashAlgorithm -> Int+toCode SHA1 = 0x11+toCode SHA256 = 0x12+toCode SHA512 = 0x13+toCode SHA3 = 0x14+toCode BLAKE2B = 0x40+toCode BLAKE2S = 0x41+++encode :: HashAlgorithm -> Digest -> BL.ByteString+encode h d = toLazyByteString $ encoder h d+++encoder :: HashAlgorithm -> Digest -> Builder+encoder h d+ = (BB.word8 . fromIntegral $ toCode h)+ <> (BB.word8 . fromIntegral $ BS.length d)+ <> byteString d+++decode :: BS.ByteString -> Either String MultihashDigest+decode = parseOnly decoder+++decoder :: Parser MultihashDigest+decoder = do+ h <- (fromCode . fromIntegral <$> A.anyWord8)+ l <- (fromIntegral <$> A.anyWord8)+ d <- A.take l+ return $ MultihashDigest h l d
+ src/System/IO/Streams/Crypto.hs view
@@ -0,0 +1,12 @@+module System.IO.Streams.Crypto where++import Control.Exception (bracket)+import Crypto.Hash (Digest, HashAlgorithm (..))+import Data.ByteString (ByteString)+import Data.Multihash.Digest (decoder)+import System.IO.Streams (InputStream, fold, inputFoldM)+++hashInputStream :: (HashAlgorithm h) => InputStream ByteString -> IO (Digest h)+hashInputStream = fmap hashFinalize . fold update hashInit+ where update ctx bs = hashUpdates ctx [bs]