diff --git a/Data/HashTree.hs b/Data/HashTree.hs
new file mode 100644
--- /dev/null
+++ b/Data/HashTree.hs
@@ -0,0 +1,48 @@
+-- | Two-way (binary) Merkle Hash Trees which implements append-only logs and
+--   provides both inclusion proof and consistency proof.
+--   The API design is inspired by Certificate Transparency defined in RFC 6962.
+module Data.HashTree (
+    -- * Settings
+    Settings
+  , defaultSettings
+    -- ** Accessors
+  , hash0
+  , hash1
+  , hash2
+    -- * Merkle Hash Trees
+  , MerkleHashTrees
+    -- ** Accessors
+  , info
+  , size
+  , digest
+    -- ** Related types
+  , TreeSize
+  , Index
+    -- ** Creating Merkle Hash Trees
+  , empty
+  , fromList
+    -- ** Appending an element
+  , add
+    -- * Inclusion Proof
+  , InclusionProof
+  , defaultInclusionProof
+    -- ** Accessors
+  , leafIndex
+  , treeSize
+  , inclusion
+    -- ** Proof and verification
+  , generateInclusionProof
+  , verifyInclusionProof
+    -- * Consistency Proof
+  , ConsistencyProof
+  , defaultConsistencyProof
+    -- ** Accessors
+  , firstTreeSize
+  , secondTreeSize
+  , consistency
+    -- ** Proof and verification
+  , generateConsistencyProof
+  , verifyConsistencyProof
+  ) where
+
+import Data.HashTree.Internal
diff --git a/Data/HashTree/Internal.hs b/Data/HashTree/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/HashTree/Internal.hs
@@ -0,0 +1,408 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Data.HashTree.Internal (
+    Settings(..)
+  , defaultSettings
+  , MerkleHashTrees(..)
+  , digest
+  , info
+  , currentHead
+  , empty
+  , fromList
+  , toHashTree
+  , add
+  , InclusionProof(..)
+  , defaultInclusionProof
+  , generateInclusionProof
+  , verifyInclusionProof
+  , ConsistencyProof(..)
+  , defaultConsistencyProof
+  , TreeSize
+  , Index
+  , generateConsistencyProof
+  , verifyConsistencyProof
+  ) where
+
+import Crypto.Hash (Digest, SHA256, HashAlgorithm, hash)
+import Data.Bits (testBit, finiteBitSize, countLeadingZeros, (.&.), unsafeShiftR)
+import Data.ByteArray (ByteArrayAccess)
+import qualified Data.ByteArray as BA
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import Data.ByteString.Char8 ()
+import Data.List (foldl')
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as Map
+import Data.IntMap.Strict (IntMap)
+import qualified Data.IntMap.Strict as IntMap
+
+-- $setup
+-- >>> :set -XOverloadedStrings
+
+----------------------------------------------------------------
+
+-- | Settings for Merkle Hash Trees.
+--   The first parameter is input data type.
+--   The second one is digest data type.
+--
+-- To create this, use 'defaultSettings':
+--
+-- > defaultSettings { hash0 = ..., hash1 = ..., hash2 = ... }
+data Settings inp ha = Settings {
+    -- | A hash value for non input element.
+    hash0 :: Digest ha
+    -- | A hash function for one input element to calculate the leaf digest.
+  , hash1 :: inp -> Digest ha
+    -- | A hash function for two input elements to calculate the internal digest.
+  , hash2 :: Digest ha -> Digest ha -> Digest ha
+  }
+
+sha256 :: ByteString -> Digest SHA256
+sha256 = hash
+
+-- | A default Settings with 'ByteString' and 'SHA256'.
+--   This can be used for CT(Certificate Transparency) defined in RFC 6962.
+defaultSettings :: Settings ByteString SHA256
+defaultSettings = Settings {
+    hash0 = sha256 ""
+  , hash1 = \x -> sha256 (BS.singleton 0x00 `BS.append` x)
+  , hash2 = \x y -> sha256 $ BS.concat [BS.singleton 0x01, BA.convert x, BA.convert y]
+  }
+
+----------------------------------------------------------------
+
+-- | The position of the target element from 0.
+type Index = Int
+
+-- | The size of hash tree.
+type TreeSize = Int
+
+-- | The data type for Merkle Hash Trees.
+--   The first parameter is input data type.
+--   The second one is digest data type.
+data MerkleHashTrees inp ha = MerkleHashTrees {
+    settings  :: !(Settings inp ha)
+    -- | Getting the log size
+  , size      :: !TreeSize
+    -- index is size of HashTree
+    -- 0 for Empty
+    -- 1 for Leaf 0 0
+    -- 'size' for the last HashTree
+  , hashtrees :: !(IntMap (HashTree inp ha)) -- the Int key is TreeSize
+  , indices   :: !(Map (Digest ha) Index)
+  }
+
+-- | Getting the Merkle Tree Hash.
+digest :: TreeSize -> MerkleHashTrees inp ha -> Maybe (Digest ha)
+digest tsiz mht = case IntMap.lookup tsiz (hashtrees mht) of
+    Nothing -> Nothing
+    Just ht -> Just $ value ht
+
+currentHead :: MerkleHashTrees inp ha -> Maybe (HashTree inp ha)
+currentHead (MerkleHashTrees _ tsiz htdb _) = IntMap.lookup tsiz htdb
+
+-- | Getting the root information of the Merkle Hash Tree.
+--   A pair of the current size and the current Merle Tree Hash is returned.
+info :: MerkleHashTrees inp ha -> (TreeSize, Digest ha)
+info mht = (siz, h)
+  where
+    siz = size mht
+    Just h = digest siz mht
+
+----------------------------------------------------------------
+
+data HashTree inp ha =
+    Empty !(Digest ha)
+  | Leaf  !(Digest ha) !Index inp
+  | Node  !(Digest ha) !Index !Index !(HashTree inp ha) !(HashTree inp ha)
+  deriving (Eq, Show)
+
+-- | Creating an empty 'MerkleHashTrees'.
+--
+-- >>> info $ empty defaultSettings
+-- (0,e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)
+empty :: Settings inp ha -> MerkleHashTrees inp ha
+empty set = MerkleHashTrees {
+    settings  = set
+  , size      = 0
+  , hashtrees = IntMap.insert 0 (Empty (hash0 set)) IntMap.empty
+  , indices   = Map.empty
+  }
+
+value :: HashTree inp ha -> Digest ha
+value (Empty ha)         = ha
+value (Leaf  ha _ _)     = ha
+value (Node  ha _ _ _ _) = ha
+
+----------------------------------------------------------------
+
+idxl :: HashTree inp ha -> Index
+idxl (Leaf _ i _)     = i
+idxl (Node _ i _ _ _) = i
+idxl _                = error "idxl"
+
+idxr :: HashTree inp ha -> Index
+idxr (Leaf _ i _)     = i
+idxr (Node _ _ i _ _) = i
+idxr (Empty _)        = error "idxr"
+
+----------------------------------------------------------------
+
+-- | Creating a Merkle Hash Tree from a list of elements. O(n log n)
+--
+-- >>> info $ fromList defaultSettings ["0","1","2"]
+-- (3,725d5230db68f557470dc35f1d8865813acd7ebb07ad152774141decbae71327)
+fromList :: (ByteArrayAccess inp, HashAlgorithm ha)
+         => Settings inp ha -> [inp] -> MerkleHashTrees inp ha
+fromList set xs = foldl' (flip add) (empty set) xs
+
+-- | Adding (appending) an element. O(log n)
+--
+-- >>> info $ add "1" $ empty defaultSettings
+-- (1,2215e8ac4e2b871c2a48189e79738c956c081e23ac2f2415bf77da199dfd920c)
+add :: (ByteArrayAccess inp, HashAlgorithm ha)
+     => inp -> MerkleHashTrees inp ha -> MerkleHashTrees inp ha
+add a mht@(MerkleHashTrees set tsiz htdb idb) =
+    case Map.lookup hx idb of
+        Just _  -> mht
+        Nothing -> case IntMap.lookup tsiz htdb of
+            Just ht -> let ht' = newht ht
+                           htdb' = IntMap.insert tsiz' ht' htdb
+                       in MerkleHashTrees set tsiz' htdb' idb'
+            Nothing -> mht -- never reach
+  where
+    tsiz' = tsiz + 1
+    hx = hash1 set a
+    idb' = Map.insert hx tsiz idb
+
+    newht ht = ins ht
+      where
+        ix = tsiz
+        x = Leaf hx ix a
+
+        hash2' = hash2 set
+        ins (Empty _)           = x
+        ins l@(Leaf hl il _ )   = Node (hash2' hl hx) il ix l x
+        ins t@(Node h il ir l r)
+          | isPowerOf2 sz = Node (hash2' h hx) il ix t x
+          | otherwise     = let r' = ins r
+                                h' = hash2' (value l) (value r')
+                            in Node h' il ix l r'
+          where
+            sz = ir - il + 1
+
+----------------------------------------------------------------
+
+-- | A simple algorithm to create a binary balanced tree. O(n log n)
+--   This is just for testing.
+toHashTree :: (ByteArrayAccess inp, HashAlgorithm ha)
+           => Settings inp ha -> [inp] -> HashTree inp ha
+toHashTree set [] = Empty $ hash0 set -- not used
+toHashTree set xs = ht
+  where
+    toLeaf = uncurry (leaf set)
+    leaves = map toLeaf $ zip xs [0..]
+    ht = buildup set leaves
+
+leaf :: (ByteArrayAccess inp, HashAlgorithm ha)
+     => Settings inp ha -> inp -> Index -> HashTree inp ha
+leaf set x i = Leaf (hash1 set x) i x
+
+link :: (ByteArrayAccess inp, HashAlgorithm ha)
+     => Settings inp ha -> HashTree inp ha -> HashTree inp ha -> HashTree inp ha
+link set l r = Node h (idxl l) (idxr r) l r
+  where
+    h = hash2 set (value l) (value r)
+
+buildup :: (ByteArrayAccess inp, HashAlgorithm ha)
+         => Settings inp ha -> [HashTree inp ha] -> HashTree inp ha
+buildup _   [ht] = ht
+buildup set hts  = buildup set (pairing set hts)
+
+pairing :: (ByteArrayAccess inp, HashAlgorithm ha)
+        => Settings inp ha -> [HashTree inp ha] -> [HashTree inp ha]
+pairing set (t:u:vs) = link set t u : pairing set vs
+pairing _       hts  = hts
+
+----------------------------------------------------------------
+
+-- | The type for inclusion proof (aka audit proof).
+data InclusionProof ha = InclusionProof {
+    -- | The index for the target.
+    leafIndex :: !Index
+    -- | The hash tree size.
+  , treeSize  :: !TreeSize
+    -- | A list of digest for inclusion.
+  , inclusion :: ![Digest ha]
+  } deriving (Eq, Show)
+
+-- | The default value for 'InclusionProof' just to create a new value.
+defaultInclusionProof :: InclusionProof ha
+defaultInclusionProof = InclusionProof {
+    leafIndex = 0
+  , treeSize  = 1
+  , inclusion = []
+  }
+
+-- | Generating 'InclusionProof' for the target at the server side.
+generateInclusionProof :: Digest ha -- ^ The target hash (leaf digest)
+                       -> TreeSize  -- ^ The tree size
+                       -> MerkleHashTrees inp ha
+                       -> Maybe (InclusionProof ha)
+generateInclusionProof h tsiz (MerkleHashTrees _ _ htdb idb) = do
+    ht <- IntMap.lookup tsiz htdb
+    i <- Map.lookup h idb
+    if i < tsiz then do
+        let digests = reverse $ path i ht
+        Just $ InclusionProof i tsiz digests
+      else
+        Nothing
+  where
+    path m (Node _ _ _ l r)
+      | m <= idxr l = value r : path m l
+      | otherwise   = value l : path m r
+    path _ _ = []
+
+-- | Verifying 'InclusionProof' at the client side.
+--
+-- >>> let target = "3"
+-- >>> let mht = fromList defaultSettings ["0","1","2",target,"4","5","6"]
+-- >>> let treeSize = 5
+-- >>> let leafDigest = hash1 defaultSettings target
+-- >>> let Just proof = generateInclusionProof leafDigest treeSize mht
+-- >>> let Just rootDigest = digest treeSize mht
+-- >>> verifyInclusionProof defaultSettings leafDigest rootDigest proof
+-- True
+verifyInclusionProof :: (ByteArrayAccess inp, HashAlgorithm ha)
+                     => Settings inp ha
+                     -> Digest ha         -- ^ The target hash (leaf digest)
+                     -> Digest ha         -- ^ Merkle Tree Hash (root digest) for the tree size
+                     -> InclusionProof ha -- ^ InclusionProof of the target
+                     -> Bool
+verifyInclusionProof set leafDigest rootDigest (InclusionProof idx tsiz pps)
+  | idx >= tsiz = False
+  | otherwise   = verify (idx,tsiz - 1) leafDigest pps
+  where
+    verify (_,sn) r []             = sn == 0 && r == rootDigest
+    verify (_,0)  _ _              = False
+    verify fsn@(fn,sn) r (p:ps)
+      | fn `testBit` 0 || fn == sn = let r' = hash2 set p r
+                                         fsn' = shiftR1 $ untilSet fsn
+                                     in verify fsn' r' ps
+      | otherwise                  = let r' = hash2 set r p
+                                         fsn' = shiftR1 fsn
+                                     in verify fsn' r' ps
+
+----------------------------------------------------------------
+
+-- | The type for consistency proof.
+data ConsistencyProof ha = ConsistencyProof {
+    -- | The first hash tree size.
+    firstTreeSize :: !TreeSize
+    -- | The second hash tree size.
+  , secondTreeSize :: !TreeSize
+    -- | A list of digest for consistency.
+  , consistency :: ![Digest ha]
+  } deriving (Eq, Show)
+
+-- | The default value for 'ConsistencyProof' just to create a new value.
+defaultConsistencyProof :: ConsistencyProof ha
+defaultConsistencyProof = ConsistencyProof {
+    firstTreeSize = 1
+  , secondTreeSize = 2
+  , consistency = []
+  }
+
+-- | Generating 'ConsistencyProof' for the target at the server side.
+generateConsistencyProof :: TreeSize -> TreeSize -> MerkleHashTrees inp ha -> Maybe (ConsistencyProof ha)
+generateConsistencyProof m n (MerkleHashTrees _ _ htdb _)
+  | m < 0 || n < 0 = Nothing
+  | m > n          = Nothing
+  | m == 0         = do
+      htn <- IntMap.lookup n htdb
+      return $ ConsistencyProof m n [value htn]
+  | otherwise = do
+      htm <- IntMap.lookup m htdb
+      htn <- IntMap.lookup n htdb
+      let digests = prove htm htn True
+      return $ ConsistencyProof m n digests
+  where
+    prove htm htn flag
+      | idxl htm == idxl htn && idxr htm == idxr htn
+                   = if flag then [] else [value htm]
+    prove htm@(Leaf _ _ _) (Node _ _ _ ln rn) flag
+                   = prove htm ln flag ++ [value rn]
+    prove htm@(Node _ midxl midxr lm rm) (Node _ nidxl nidxr ln rn) flag
+      | sizm <= k  = prove htm ln flag ++ [value rn]
+      | otherwise  = prove rm rn False ++ [value lm]
+      where
+        sizm = midxr - midxl + 1
+        sizn = nidxr - nidxl + 1
+        k = maxPowerOf2 (sizn - 1) -- e.g. if 8, take 4.
+    prove _ _ _    = error "generateConsistencyProof:prove"
+
+-- | Verifying 'ConsistencyProof' at the client side.
+--
+-- >>> let mht0 = fromList defaultSettings ["0","1","2","3"]
+-- >>> let (m, digestM) = info mht0
+-- >>> let mht1 = add "6" $ add "5" $ add "4" mht0
+-- >>> let (n, digestN) = info mht1
+-- >>> let Just proof = generateConsistencyProof m n mht1
+-- >>> verifyConsistencyProof defaultSettings digestM digestN proof
+-- True
+verifyConsistencyProof :: (ByteArrayAccess inp, HashAlgorithm ha)
+                       => Settings inp ha
+                       -> Digest ha -- start
+                       -> Digest ha -- end
+                       -> ConsistencyProof ha
+                       -> Bool
+verifyConsistencyProof set firstHash secondHash (ConsistencyProof first second path)
+  | first == 0      = case path of
+      [c] -> secondHash == c
+      _   -> False
+  | first == second = null path && firstHash == secondHash
+  | otherwise       = case path' of
+      []   -> False
+      c:cs -> verify (untilNotSet (first - 1, second - 1)) c c cs -- fixme:cs
+  where
+    path'
+      | isPowerOf2 first = firstHash : path
+      | otherwise        = path
+    verify _     fr sr [] = fr == firstHash && sr == secondHash
+    verify (_,0) _ _ _    = error "verifyConsistencyProof:verify"
+    verify fsn@(fn,sn) fr sr (c:cs)
+      | fn `testBit` 0 || fn == sn = let fr' = hash2 set c fr
+                                         sr' = hash2 set c sr
+                                         fsn'
+                                          | not (fn `testBit` 0) = untilSet fsn
+                                          | otherwise           = fsn
+                                         fsn'' = shiftR1 fsn'
+                                     in verify fsn'' fr' sr' cs
+      | otherwise = let sr' = hash2 set sr c
+                        fsn' = shiftR1 fsn
+                    in verify fsn' fr sr' cs
+
+----------------------------------------------------------------
+
+width :: Int -> Int
+width x = finiteBitSize x - countLeadingZeros x
+
+isPowerOf2 :: Int -> Bool
+isPowerOf2 n = (n .&. (n - 1)) == 0
+
+maxPowerOf2 :: Int -> Int
+maxPowerOf2 n = 2 ^ (width n - 1)
+
+shiftR1 :: (Int,Int) -> (Int,Int)
+shiftR1 (x,y) = (x `unsafeShiftR` 1, y `unsafeShiftR` 1)
+
+untilNotSet :: (Int,Int) -> (Int,Int)
+untilNotSet fsn@(fn,_)
+  | fn `testBit` 0 = untilNotSet $ shiftR1 fsn
+  | otherwise      = fsn
+
+untilSet :: (Int,Int) -> (Int,Int)
+untilSet fsn@(fn,_)
+  | fn == 0        = fsn
+  | fn `testBit` 0 = fsn
+  | otherwise      = untilSet $ shiftR1 fsn
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2017, IIJ Innovation Institute Inc.
+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 the copyright holders nor the names of its
+    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.
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/hash-tree.cabal b/hash-tree.cabal
new file mode 100644
--- /dev/null
+++ b/hash-tree.cabal
@@ -0,0 +1,46 @@
+Name:                   hash-tree
+Version:                0.0.0
+Author:                 Kazu Yamamoto <kazu@iij.ad.jp>
+Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>
+License:                BSD3
+License-File:           LICENSE
+Synopsis:               Merkle Hash Tree
+Description:            Purely functional Merkle hash tree which
+                        implements appe nd-only logs and
+                        provides both inclusion proof and consistency proof.
+Category:               Data
+Cabal-Version:          >= 1.10
+Build-Type:             Simple
+
+Library
+  Default-Language:     Haskell2010
+  GHC-Options:          -Wall
+  Exposed-Modules:      Data.HashTree
+  Other-Modules:        Data.HashTree.Internal
+  Build-Depends:        base >= 4 && < 5
+                      , bytestring
+                      , containers
+                      , cryptonite
+                      , memory
+
+Test-Suite spec
+  Type:                 exitcode-stdio-1.0
+  Default-Language:     Haskell2010
+  HS-Source-Dirs:       test, .
+  Ghc-Options:          -Wall
+  Main-Is:              Spec.hs
+  Other-Modules:        HashTreeSpec
+                        Data.HashTree.Internal
+  Build-Depends:        base >= 4 && < 5
+                      , QuickCheck
+                      , bytestring
+                      , base64-bytestring
+                      , containers
+                      , cryptonite
+                      , hspec
+                      , memory
+
+Source-Repository head
+  Type:                 git
+  Location:             https://github.com/kazu-yamamoto/hash-tree
+
diff --git a/test/HashTreeSpec.hs b/test/HashTreeSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/HashTreeSpec.hs
@@ -0,0 +1,148 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module HashTreeSpec where
+
+import Crypto.Hash
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import Data.ByteString.Base64
+import Data.ByteString.Char8 ()
+import Data.HashTree
+import Data.HashTree.Internal
+import Data.List (nub)
+import Data.Maybe
+import Test.Hspec
+import Test.Hspec.QuickCheck
+import Test.QuickCheck
+
+
+newtype Input = Input [ByteString] deriving Show -- non empty list
+
+instance Arbitrary ByteString where
+    arbitrary = BS.pack <$> listOf arbitrary -- Gen Word8
+
+instance Arbitrary Input where
+    arbitrary = Input . getNonEmpty <$> arbitrary -- Gen (NonEmptyList ByteString)
+
+spec :: Spec
+spec = do
+    let set = defaultSettings
+    describe "info" $ do
+        prop "The root hash of 1-size tree is equal to the leaf hash" $ \bs ->
+            let mht = add (bs :: ByteString) $ empty set
+                h1 = snd $ info mht
+                h2 = hash1 set bs
+            in h1 == h2
+    describe "fromList" $ do
+        prop "create a perfectly branched tree" $ \(Input bss) ->
+            let Just ht = currentHead $ fromList set bss
+                ht' = toHashTree set $ nub bss
+            in ht == ht'
+    describe "verifyInclusionProof" $ do
+        it "can verify the certificate transparency" $
+            verifyInclusionProof set dl d2 iProof `shouldBe` True
+        prop "can verify for a good target" $ \(Input bss0) x0 y0 ->
+            let bss = nub bss0
+                mht = fromList set bss
+                len = length bss
+                x0' = adjust x0 len
+                y0' = adjust y0 len
+                (i,tsiz)
+                   | x0' < y0' = (x0',y0')
+                   | x0' > y0' = (y0',x0')
+                   | otherwise = (x0',y0'+1)
+                target = bss !! i
+                leafDigest = hash1 set target
+                proof = fromJust $ generateInclusionProof leafDigest tsiz mht
+                Just rootDigest = digest tsiz mht
+            in verifyInclusionProof set leafDigest rootDigest proof
+    describe "verifyConsistencyProof" $ do
+        it "can verify the certificate transparency" $
+            verifyConsistencyProof set d1 d2 cProof `shouldBe` True
+        prop "can verify" $ \(Input bss) m0 n0 ->
+            let mht = fromList set bss
+                siz = size mht
+                m0' = adjust m0 siz
+                n0' = adjust n0 siz
+                (m,n) = if m0' <= n0' then (m0',n0') else (n0',m0')
+                proof = fromJust $ generateConsistencyProof m n mht
+                Just dm = digest m mht
+                Just dn = digest n mht
+            in verifyConsistencyProof set dm dn proof
+
+adjust :: Int -> Int -> Int
+adjust x b
+  | x < 0     = negate x `mod` b
+  | otherwise = x `mod` b
+
+toDigest :: ByteString -> Digest SHA256
+toDigest x = d
+  where
+    Right r = decode x
+    Just d = digestFromByteString r
+
+
+{-
+% wget https://ct.googleapis.com/icarus/ct/v1/get-sth
+{"tree_size":129596132,"timestamp":1508198131197,"sha256_root_hash":"qyJv/TObheataX8uFjAJsR+dJtJgNlfjzTe5CX8Ej2s=","tree_head_signature":"BAMASDBGAiEAhtcpfpVSKRuZOxEMpPRRiu5nPdd8UL3JnQBk23Zrk/ACIQD+n4YSigh3o1KaJvBTvJkcmq2CW5QtrIBdI2oDiItN1A=="}
+-}
+d1 :: Digest SHA256
+d1 = toDigest "qyJv/TObheataX8uFjAJsR+dJtJgNlfjzTe5CX8Ej2s="
+
+{-
+% wget https://ct.googleapis.com/icarus/ct/v1/get-sth
+{"tree_size":129721434,"timestamp":1508216080417,"sha256_root_hash":"jhnkzC7e95jLNBJSiQRnuH2sXYfpY6sASO3ezfUBU0w=","tree_head_signature":"BAMARzBFAiBkiNQdBXnYQLwbcSxPYjvKYatziAbADq124OifIeZ2oQIhAOzYM60DNII1ILtWDMvCPcGqTapkb/Ru4KIJJwdTi+dg"}
+-}
+d2 :: Digest SHA256
+d2 = toDigest "jhnkzC7e95jLNBJSiQRnuH2sXYfpY6sASO3ezfUBU0w="
+
+{-
+% wget 'https://ct.googleapis.com/icarus/ct/v1/get-sth-consistency?first=129596132&second=129721434'
+{"consistency":["5mrKRX9GRLriSIAtkX0kdvlnjt/f+P0vTrhmzOdhnzs=","VEqL3Z55VZTVRp45WX7gTdcwxOorltn+uCQpHjv9rjE=","2Euj4VXixjEAGj9p23OMo+ciB1tQ2KXH97TkWgcZLpI=","rG0l6AHdk3TRkgAnwTK1Ys5kU7uuKRkMQSkrES0ocj0=","JSxii5BAGCuwifRNIgIsarVA/aSC/QJNv4kfT9zii3Y=","WPaErDHN/lYCuv8TsvosmAsIDbEOfPNZ/LxqLvuQcdY=","HjN1fCoS6OYw8VH+DAcQPQkCjksWljkvr3AJiZTsi6Y=","liuu+DeQohOzAyQadR3imE0SLo8pSsv9K0Y9f1dGrwA=","oUQV+oip0wozcwv4mmMv4MrP545ugwXDIMU6HkZrfl0=","V9BPc7aq1Sq1XwFV3gMx7/9Wv+8l/81S5Gb+bDriRZE=","h3PJZDxVUsGMPBMEaJsQnqAVjlpuxUPGAUfYCY6TkWY=","Rj09QnLzZ2MmyKjJYkcSO8Ko6lGhgwrnOydn+RdHdeI=","Dc7oYAljc9VEYI8JuvYVyJtTHP8uW7igIKVqnWXgvFs=","tJfBVa+YE0GK8T1L74hkkGmT3QO+mznMf+XrKnS3YOE=","V9uRJUhCpCMwss1i4c85X5ceJnNePXdjAVrxtU6EezY=","nf6Yq8xIs56hgor/mk30dvpaUO1/1w+MUlz8thiEb3o=","SEVW3kh8HQ9Za+LjCoGaAkbbUfYmiwNs3SDasWKS6xo=","96r7XxdWTPAZSygfov9mTTd4Gs6chZfdkznd8HAmzKc=","t6OBvH84uFTwEubekO8rdcv+AlqPcpuZX9pwLYopKGg=","nl6nrmkzk5qSYBiUHN609SD8IH3HuTPYR6r1YWKtWag=","NbPA38XBZUXC5ca/cNcU/mNhq31qg2okTS3kJdptnxg=","Ur4mOpiyDl1Zq/MH/SGszUlBP7jmOfupNVodMT6Mg2M=","LUTvZyrtuEoSL7PNqR2Iv2FbcbtI5lQrq5/97sMFIsU=","Bh+FIkHuIdFAcvCYOTlkKMxR15aDaaT9UlUsrCeuLUY="]}
+-}
+cProof :: ConsistencyProof SHA256
+cProof = ConsistencyProof 129596132 129721434 cs
+  where
+    bs = ["5mrKRX9GRLriSIAtkX0kdvlnjt/f+P0vTrhmzOdhnzs="
+         ,"VEqL3Z55VZTVRp45WX7gTdcwxOorltn+uCQpHjv9rjE="
+         ,"2Euj4VXixjEAGj9p23OMo+ciB1tQ2KXH97TkWgcZLpI="
+         ,"rG0l6AHdk3TRkgAnwTK1Ys5kU7uuKRkMQSkrES0ocj0="
+         ,"JSxii5BAGCuwifRNIgIsarVA/aSC/QJNv4kfT9zii3Y="
+         ,"WPaErDHN/lYCuv8TsvosmAsIDbEOfPNZ/LxqLvuQcdY="
+         ,"HjN1fCoS6OYw8VH+DAcQPQkCjksWljkvr3AJiZTsi6Y="
+         ,"liuu+DeQohOzAyQadR3imE0SLo8pSsv9K0Y9f1dGrwA="
+         ,"oUQV+oip0wozcwv4mmMv4MrP545ugwXDIMU6HkZrfl0="
+         ,"V9BPc7aq1Sq1XwFV3gMx7/9Wv+8l/81S5Gb+bDriRZE="
+         ,"h3PJZDxVUsGMPBMEaJsQnqAVjlpuxUPGAUfYCY6TkWY="
+         ,"Rj09QnLzZ2MmyKjJYkcSO8Ko6lGhgwrnOydn+RdHdeI="
+         ,"Dc7oYAljc9VEYI8JuvYVyJtTHP8uW7igIKVqnWXgvFs="
+         ,"tJfBVa+YE0GK8T1L74hkkGmT3QO+mznMf+XrKnS3YOE="
+         ,"V9uRJUhCpCMwss1i4c85X5ceJnNePXdjAVrxtU6EezY="
+         ,"nf6Yq8xIs56hgor/mk30dvpaUO1/1w+MUlz8thiEb3o="
+         ,"SEVW3kh8HQ9Za+LjCoGaAkbbUfYmiwNs3SDasWKS6xo="
+         ,"96r7XxdWTPAZSygfov9mTTd4Gs6chZfdkznd8HAmzKc="
+         ,"t6OBvH84uFTwEubekO8rdcv+AlqPcpuZX9pwLYopKGg="
+         ,"nl6nrmkzk5qSYBiUHN609SD8IH3HuTPYR6r1YWKtWag="
+         ,"NbPA38XBZUXC5ca/cNcU/mNhq31qg2okTS3kJdptnxg="
+         ,"Ur4mOpiyDl1Zq/MH/SGszUlBP7jmOfupNVodMT6Mg2M="
+         ,"LUTvZyrtuEoSL7PNqR2Iv2FbcbtI5lQrq5/97sMFIsU="
+         ,"Bh+FIkHuIdFAcvCYOTlkKMxR15aDaaT9UlUsrCeuLUY="
+         ]
+    cs = map toDigest bs
+
+{-
+% wget 'https://ct.googleapis.com/icarus/ct/v1/get-entry-and-proof?leaf_index=120000000&tree_size=129721434'
+{"leaf_input":"AAAAAAFexROk9wAAAAUdMIIFGTCCBAGgAwIBAgISBC/2U32dBHf/8H9aIod2qqPFMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNzA5MjcxOTQzMDBaFw0xNzEyMjYxOTQzMDBaMBsxGTAXBgNVBAMTEGdlbS1qZXdlbGVycy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB61H4Kvny8rX0gBuvPciX9VpPzfkdYHjRwmZLU3uj3awfN+4qouO7ipz9Cyz1saCI53NXisHr3lttxAF1ISnidvywgRyaO9G0BVAn6Tl5zarOhwrf56wopfyEGaHfpyO8dCV5nfNWM99gbmrcJgWqs5/7Np9L+Y0ZMah751X6oZsJqKSs6FCUbAtyeAhXBQMuSOqkd85nObSpnf2W0CAfWYy66VPbDL8OAhq0A/3U+gh9ThWCAPPn9pVghazBKsSL+c1KW84xsXuHWkkgLjJ3f/oL9FPTzNBfqYzzeuAVAEwazYXPyEu5SlbdqzDi1Lesp0+2yoMO9h7MWy9Mom05AgMBAAGjggImMIICIjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDQaIW5tEaFbKN9qRmVdZEk2eB4GMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wMQYDVR0RBCowKIIQZ2VtLWpld2VsZXJzLmNvbYIUd3d3LmdlbS1qZXdlbGVycy5jb20wgf4GA1UdIASB9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAMMIZaoRMUdLpIyc7TmFmjz8h9q24xnwY04J61CALDVV8c8IjzRwuheqcCzOmBAfI4ejBnAb3wxIYIq/FlYmWbLHEdpOviPFgyV6uskNGGAyEWjU+ah3E9NXxJ2+/h/GmDOLk3D6aXeLyFpZr1nAhl9+Sr+Gj/gWijiAhpKKT/al73u4ZG8exhsRlfTr84Mtgn29NwolMAQ5anMuLgk3d7YTOpsi602vQTyEAj5G7SUryBvf+lYhDvyZwLkbEqaQnuUvHu2uLGbtRb002CAptZeqTLQ84hIjS1JJ5YZgdEtPlbUqduuR6CB3nIA6cKdLKyoTw3Yxwh9Xeowe6OEVEIwAA","extra_data":"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","audit_path":["xNXDvcL4JYFTwHXfnl0an0vyCjS0am5+B8dxDozc5ag=","LJc7lPt79J4pgFqY0hbGYXZi401EnFVix2vFUSovrB8=","QFcF13awbePeLAqSfFoNB6cYYU40wwY7aLYQXk/ss/A=","Wlc9EJxIODGOKavf7zXfPOrymuWwviwGUI8K6h9EBFQ=","aigt2QVYyMjXSLFAhuaEvBmqctsFVKFZEgtJYl/gPL8=","DALuGNF8WzgEgdYT34KFCzbRlV+wx5Ie1jrUsfJS+Jo=","wfZyGm8U+u587alYY91O5sOCVVMPqNA1wwst8/CLsWA=","frRFNtjphdfOKw0X1I1AZfU6yuCMZ+/bC9sHXg6L4DQ=","5DgE84Ns18cCD9zOiiPUuUP17nOmxMm4copkw2MHY9g=","ggacmAKa8hnTxehF0WUAbknaQk1Ks6c33x2g/zOVWgw=","I1YeaNL1rb0YNDCb7Xe9VPosJKP0fv3msScVnNsn4PA=","BPVDHS1+mcuIEsShBg19U6UCwIQKJwRZeXIECFA6zcw=","7smS/3DRbnKci59UoBg7xDf+NlAVGAZ2qtG4ILaAK/Q=","75SJNIAzyepGCUnfvUTVrZSddSfWaSxxXXmBiHewa8g=","YxHvS/I+2i1yJLilSU/ZKjyr5jc5ABhuU4Wh6H+oBUY=","7DBov9qK6s/uy8g0iL//5A97ZAELOSE6SjGMGVBK8dY=","JsXCAk0Ztn8YZFmyfUkuJYzAzlV70D+mefthbBvRjaY=","YHBP2MPkXLUeRIMQTdQ9LVxolKbn1onla0Uedgp1x+Y=","yE4z+aQ1n/7sLJRG8jM5xKLq+JlHg3GoN3ApPovnh/o=","fwU2Yg7p+8ACX6v3e7xiJGwDeYo0b+33hbxy+KMdMfA=","5gj0c1LVruisvtT8VA1J9IeWyRaTf0+dwuLnamseTl8=","HQec1yNY1oQtctj/ZfpEtDZZf3aT19Qdo4aaBPvaT+I=","FPtmqvOXlhE5gi/rc/sfWQpvLT7tPfHKJiSCEgmf3Hk=","EtTWrgka0wMLdqJ4oFojyDzjsuLoSyZpyzKzff+yFxU=","Ur4mOpiyDl1Zq/MH/SGszUlBP7jmOfupNVodMT6Mg2M=","LUTvZyrtuEoSL7PNqR2Iv2FbcbtI5lQrq5/97sMFIsU=","Bh+FIkHuIdFAcvCYOTlkKMxR15aDaaT9UlUsrCeuLUY="]}
+-}
+
+dl :: Digest SHA256
+dl = hash1 defaultSettings r
+  where
+   Right r = decode "AAAAAAFexROk9wAAAAUdMIIFGTCCBAGgAwIBAgISBC/2U32dBHf/8H9aIod2qqPFMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNzA5MjcxOTQzMDBaFw0xNzEyMjYxOTQzMDBaMBsxGTAXBgNVBAMTEGdlbS1qZXdlbGVycy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB61H4Kvny8rX0gBuvPciX9VpPzfkdYHjRwmZLU3uj3awfN+4qouO7ipz9Cyz1saCI53NXisHr3lttxAF1ISnidvywgRyaO9G0BVAn6Tl5zarOhwrf56wopfyEGaHfpyO8dCV5nfNWM99gbmrcJgWqs5/7Np9L+Y0ZMah751X6oZsJqKSs6FCUbAtyeAhXBQMuSOqkd85nObSpnf2W0CAfWYy66VPbDL8OAhq0A/3U+gh9ThWCAPPn9pVghazBKsSL+c1KW84xsXuHWkkgLjJ3f/oL9FPTzNBfqYzzeuAVAEwazYXPyEu5SlbdqzDi1Lesp0+2yoMO9h7MWy9Mom05AgMBAAGjggImMIICIjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDQaIW5tEaFbKN9qRmVdZEk2eB4GMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9yZy8wMQYDVR0RBCowKIIQZ2VtLWpld2VsZXJzLmNvbYIUd3d3LmdlbS1qZXdlbGVycy5jb20wgf4GA1UdIASB9jCB8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENlcnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRlIFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAMMIZaoRMUdLpIyc7TmFmjz8h9q24xnwY04J61CALDVV8c8IjzRwuheqcCzOmBAfI4ejBnAb3wxIYIq/FlYmWbLHEdpOviPFgyV6uskNGGAyEWjU+ah3E9NXxJ2+/h/GmDOLk3D6aXeLyFpZr1nAhl9+Sr+Gj/gWijiAhpKKT/al73u4ZG8exhsRlfTr84Mtgn29NwolMAQ5anMuLgk3d7YTOpsi602vQTyEAj5G7SUryBvf+lYhDvyZwLkbEqaQnuUvHu2uLGbtRb002CAptZeqTLQ84hIjS1JJ5YZgdEtPlbUqduuR6CB3nIA6cKdLKyoTw3Yxwh9Xeowe6OEVEIwAA"
+
+iProof :: InclusionProof SHA256
+iProof = InclusionProof 120000000 129721434 is
+  where
+    bs = ["xNXDvcL4JYFTwHXfnl0an0vyCjS0am5+B8dxDozc5ag=","LJc7lPt79J4pgFqY0hbGYXZi401EnFVix2vFUSovrB8=","QFcF13awbePeLAqSfFoNB6cYYU40wwY7aLYQXk/ss/A=","Wlc9EJxIODGOKavf7zXfPOrymuWwviwGUI8K6h9EBFQ=","aigt2QVYyMjXSLFAhuaEvBmqctsFVKFZEgtJYl/gPL8=","DALuGNF8WzgEgdYT34KFCzbRlV+wx5Ie1jrUsfJS+Jo=","wfZyGm8U+u587alYY91O5sOCVVMPqNA1wwst8/CLsWA=","frRFNtjphdfOKw0X1I1AZfU6yuCMZ+/bC9sHXg6L4DQ=","5DgE84Ns18cCD9zOiiPUuUP17nOmxMm4copkw2MHY9g=","ggacmAKa8hnTxehF0WUAbknaQk1Ks6c33x2g/zOVWgw=","I1YeaNL1rb0YNDCb7Xe9VPosJKP0fv3msScVnNsn4PA=","BPVDHS1+mcuIEsShBg19U6UCwIQKJwRZeXIECFA6zcw=","7smS/3DRbnKci59UoBg7xDf+NlAVGAZ2qtG4ILaAK/Q=","75SJNIAzyepGCUnfvUTVrZSddSfWaSxxXXmBiHewa8g=","YxHvS/I+2i1yJLilSU/ZKjyr5jc5ABhuU4Wh6H+oBUY=","7DBov9qK6s/uy8g0iL//5A97ZAELOSE6SjGMGVBK8dY=","JsXCAk0Ztn8YZFmyfUkuJYzAzlV70D+mefthbBvRjaY=","YHBP2MPkXLUeRIMQTdQ9LVxolKbn1onla0Uedgp1x+Y=","yE4z+aQ1n/7sLJRG8jM5xKLq+JlHg3GoN3ApPovnh/o=","fwU2Yg7p+8ACX6v3e7xiJGwDeYo0b+33hbxy+KMdMfA=","5gj0c1LVruisvtT8VA1J9IeWyRaTf0+dwuLnamseTl8=","HQec1yNY1oQtctj/ZfpEtDZZf3aT19Qdo4aaBPvaT+I=","FPtmqvOXlhE5gi/rc/sfWQpvLT7tPfHKJiSCEgmf3Hk=","EtTWrgka0wMLdqJ4oFojyDzjsuLoSyZpyzKzff+yFxU=","Ur4mOpiyDl1Zq/MH/SGszUlBP7jmOfupNVodMT6Mg2M=","LUTvZyrtuEoSL7PNqR2Iv2FbcbtI5lQrq5/97sMFIsU=","Bh+FIkHuIdFAcvCYOTlkKMxR15aDaaT9UlUsrCeuLUY="]
+    is = map toDigest bs
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
