diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright: (c) Vo Minh Thu, 2013.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. 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.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/System/IO/Streams/SHA.hs b/System/IO/Streams/SHA.hs
new file mode 100644
--- /dev/null
+++ b/System/IO/Streams/SHA.hs
@@ -0,0 +1,121 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+module System.IO.Streams.SHA where
+
+import Control.Exception (Exception, throwIO)
+import Data.Binary.Get
+import Data.Typeable (Typeable)
+import System.IO.Streams.Internal (InputStream (..))
+import qualified System.IO.Streams as S
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as C
+import Data.IORef (IORef, newIORef, readIORef, writeIORef)
+
+import Data.Digest.Pure.SHA
+
+sha1Input :: InputStream ByteString -> IO (InputStream ByteString, IO (Digest SHA1State))
+sha1Input = shaInput sha1Incremental completeSha1Incremental
+
+sha224Input :: InputStream ByteString -> IO (InputStream ByteString, IO (Digest SHA256State))
+sha224Input = shaInput sha224Incremental completeSha224Incremental
+
+sha256Input :: InputStream ByteString -> IO (InputStream ByteString, IO (Digest SHA256State))
+sha256Input = shaInput sha256Incremental completeSha256Incremental
+
+sha384Input :: InputStream ByteString -> IO (InputStream ByteString, IO (Digest SHA512State))
+sha384Input = shaInput sha384Incremental completeSha384Incremental
+
+sha512Input :: InputStream ByteString -> IO (InputStream ByteString, IO (Digest SHA512State))
+sha512Input = shaInput sha512Incremental completeSha512Incremental
+
+checkedSha1Input :: Digest SHA1State -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha1Input = checkedShaInput sha1Incremental completeSha1Incremental . showDigest
+
+checkedSha224Input :: Digest SHA256State -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha224Input = checkedShaInput sha224Incremental completeSha224Incremental . showDigest
+
+checkedSha256Input :: Digest SHA256State -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha256Input = checkedShaInput sha256Incremental completeSha256Incremental . showDigest
+
+checkedSha384Input :: Digest SHA512State -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha384Input = checkedShaInput sha384Incremental completeSha384Incremental . showDigest
+
+checkedSha512Input :: Digest SHA512State -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha512Input = checkedShaInput sha512Incremental completeSha512Incremental . showDigest
+
+checkedSha1Input' :: String -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha1Input' = checkedShaInput sha1Incremental completeSha1Incremental
+
+checkedSha224Input' :: String -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha224Input' = checkedShaInput sha224Incremental completeSha224Incremental
+
+checkedSha256Input' :: String -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha256Input' = checkedShaInput sha256Incremental completeSha256Incremental
+
+checkedSha384Input' :: String -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha384Input' = checkedShaInput sha384Incremental completeSha384Incremental
+
+checkedSha512Input' :: String -> InputStream ByteString -> IO (InputStream ByteString)
+checkedSha512Input' = checkedShaInput sha512Incremental completeSha512Incremental
+
+-- | Inspired by `S.countInput`. The returned IO action can be run only
+-- when the input stream is exhausted, otherwise an error occurs.
+shaInput :: Decoder a -> (Decoder a -> Int -> Digest a)
+  -> InputStream ByteString -> IO (InputStream ByteString, IO (Digest a))
+shaInput increment end is = do
+  ref <- newIORef (increment, 0)
+  is' <- S.makeInputStream $ prod ref
+  return $! (is', readIORef ref >>= uncurry complete)
+
+  where
+
+  prod ref = do
+    mbs <- S.read is
+    maybe
+      (return Nothing)
+      (\bs -> (modifyRef ref (uncurry $ modify bs)) >> (return $! Just bs))
+      mbs
+
+  complete decoder c = return $! end decoder c
+  modify bs decoder c = (pushChunk decoder bs, c + (fromIntegral $ C.length bs))
+
+-- | This returns an input stream exactly as the one being wrapped, but throws
+-- an error if the computed SHA hash does not match the one given.
+checkedShaInput :: Decoder a -> (Decoder a -> Int -> Digest a)
+  -> String -> InputStream ByteString -> IO (InputStream ByteString)
+checkedShaInput increment end digest is = do
+  ref <- newIORef (increment, 0)
+  is' <- S.makeInputStream $ prod ref
+  return $! is'
+
+  where
+
+  prod ref = do
+    mbs <- S.read is
+    maybe
+      (do r <- readIORef ref
+          digest' <- uncurry complete r
+          if digest == showDigest digest'
+            then return Nothing
+            else throwIO UnmatchedSHAException)
+      (\bs -> (modifyRef ref (uncurry $ modify bs)) >> (return $! Just bs))
+      mbs
+
+  complete decoder c = return $! end decoder c
+  modify bs decoder c = (pushChunk decoder bs, c + (fromIntegral $ C.length bs))
+
+-- | Taken from System.IO.Streams.ByteString.
+{-# INLINE modifyRef #-}
+modifyRef :: IORef a -> (a -> a) -> IO ()
+modifyRef ref f = do
+    x <- readIORef ref
+    writeIORef ref $! f x
+
+-- | Exception raised by `checkedShaInput`.
+data UnmatchedSHAException = UnmatchedSHAException
+  deriving (Typeable)
+
+instance Show UnmatchedSHAException where
+    show _ = "Unmatched SHA exception."
+
+instance Exception UnmatchedSHAException
diff --git a/bin/sha-streams.hs b/bin/sha-streams.hs
new file mode 100644
--- /dev/null
+++ b/bin/sha-streams.hs
@@ -0,0 +1,29 @@
+module Main (main) where
+
+import qualified System.IO.Streams as S
+
+import Data.Digest.Pure.SHA
+import System.IO.Streams.SHA
+
+main :: IO ()
+main = do
+  d1 <- S.withFileAsInput "bin/sha-streams.hs" $ \is -> do
+    (is1, getSha1) <- sha1Input is
+    (is224, getSha224) <- sha224Input is1
+    (is256, getSha256) <- sha256Input is224
+    (is384, getSha384) <- sha384Input is256
+    (is512, getSha512) <- sha512Input is384
+    S.skipToEof is512
+    d1 <- getSha1
+    putStrLn $ showDigest d1
+    getSha224 >>= putStrLn . showDigest
+    getSha256 >>= putStrLn . showDigest
+    getSha384 >>= putStrLn . showDigest
+    getSha512 >>= putStrLn . showDigest
+    return d1
+
+  -- This must throw an UnmatchedSHAException.
+  S.withFileAsInput "System/IO/Streams/SHA.hs" $ \is -> do
+    (is1, _) <- sha1Input is
+    is1' <- checkedSha1Input d1 is1
+    S.skipToEof is1'
diff --git a/sha-streams.cabal b/sha-streams.cabal
new file mode 100644
--- /dev/null
+++ b/sha-streams.cabal
@@ -0,0 +1,34 @@
+name:                sha-streams
+version:             0.1.0
+Cabal-Version:       >= 1.8
+synopsis:            SHA hashes for io-streams.
+description:         SHA hashes for io-streams.
+category:            System
+license:             BSD3
+license-file:        LICENSE
+author:              Vo Minh Thu
+maintainer:          thu@hypered.io
+build-type:          Simple
+homepage:            https://github.com/noteed/sha-streams
+
+source-repository head
+  type: git
+  location: git://github.com/noteed/sha-streams.git
+
+library
+  build-depends:       base == 4.*,
+                       binary == 0.7.*,
+                       bytestring == 0.9.*,
+                       io-streams == 1.1.*,
+                       SHA >= 1.6.3
+  exposed-modules:     System.IO.Streams.SHA
+  ghc-options:         -Wall
+
+executable sha-streams
+  hs-source-dirs:      bin
+  main-is:             sha-streams.hs
+  build-depends:       base == 4.*,
+                       io-streams == 1.1.*,
+                       SHA >= 1.6.3,
+                       sha-streams
+  ghc-options:         -Wall
