diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2017
+
+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 Author name here 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.
diff --git a/Network/CryptoConditions.hs b/Network/CryptoConditions.hs
new file mode 100644
--- /dev/null
+++ b/Network/CryptoConditions.hs
@@ -0,0 +1,107 @@
+--------------------------------------------------------------------------------
+-- Crypto Conditions Standard API
+--
+-- The Condition type defined in this module supports the standard
+-- condition types, library authors wishing to extend CryptoConditions
+-- should copy and paste this file into their own project and define their own
+-- Condition type.
+--------------------------------------------------------------------------------
+
+module Network.CryptoConditions
+  ( module CCI
+  , Condition(..)
+  , ed25519Condition
+  , preimageCondition
+  , fulfillEd25519
+  , readStandardFulfillment
+  ) where
+
+import qualified Crypto.PubKey.Ed25519 as Ed2
+
+import Data.ByteString as BS
+import Data.Word
+import qualified Data.Set as Set
+
+import Network.CryptoConditions.Impl as CCI
+
+
+data Condition =
+    Preimage Preimage
+  | Prefix Prefix Int Condition
+  | Threshold Word16 [Condition]
+--  Rsa
+  | Ed25519 Ed2.PublicKey (Maybe Ed2.Signature)
+  | Anon Int Fingerprint Int (Set.Set ConditionType)
+  deriving (Show, Eq)
+
+
+instance IsCondition Condition where
+  getType (Anon 0 _ _ _) = preimageType
+  getType (Anon 2 _ _ _) = thresholdType
+  getType (Anon 4 _ _ _) = ed25519Type
+  getType (Threshold _ _) = thresholdType
+  getType (Ed25519 _ _) = ed25519Type
+  getType (Preimage _) = preimageType
+  getType (Prefix _ _ _) = prefixType
+
+  getCost (Threshold t subs) = thresholdCost t subs
+  getCost (Ed25519 _ _) = ed25519Cost
+  getCost (Preimage pre) = preimageCost pre
+  getCost (Prefix pre mml c) = prefixCost pre mml c
+  getCost (Anon _ _ c _) = c
+
+  getFingerprint (Threshold t subs) = thresholdFingerprint t subs
+  getFingerprint (Ed25519 pk _) = ed25519Fingerprint pk
+  getFingerprint (Preimage pre) = preimageFingerprint pre
+  getFingerprint (Prefix pre mml c) = prefixFingerprint pre mml c
+  getFingerprint (Anon _ fp _ _) = fp
+
+  getFulfillment (Threshold t subs) = thresholdFulfillment t subs
+  getFulfillment (Ed25519 pk msig) = ed25519Fulfillment pk <$> msig
+  getFulfillment (Preimage pre) = Just $ preimageFulfillment pre
+  getFulfillment (Prefix pre mml c) =  prefixFulfillment pre mml c
+  getFulfillment (Anon _ _ _ _) = Nothing
+
+  getSubtypes (Threshold _ sts) = thresholdSubtypes sts
+  getSubtypes (Anon _ _ _ sts) = sts
+  getSubtypes (Prefix _ _ c)     = prefixSubtypes c
+  getSubtypes _                = mempty
+
+  parseFulfillment 0 = parsePreimage Preimage
+  parseFulfillment 1 = parsePrefix Prefix
+  parseFulfillment 2 = parseThreshold Threshold
+  parseFulfillment 4 = parseEd25519 (\a b -> Ed25519 a (Just b))
+
+  verifyMessage (Preimage image) = verifyPreimage image
+  verifyMessage (Prefix pre mml cond) = verifyPrefix pre mml cond
+  verifyMessage (Threshold m subs) = verifyThreshold m subs
+  verifyMessage (Ed25519 pk (Just sig)) = verifyEd25519 pk sig
+  verifyMessage _ = const False
+
+  anon t f c = Anon t f c . toConditionTypes
+
+
+toConditionTypes :: Set.Set Int -> Set.Set ConditionType
+toConditionTypes = Set.map $
+  let u = undefined in (\tid -> getType $ Anon tid u u u)
+
+
+preimageCondition :: BS.ByteString -> Condition
+preimageCondition = Preimage
+
+
+ed25519Condition :: Ed2.PublicKey -> Condition
+ed25519Condition pk = Ed25519 pk Nothing
+
+
+fulfillEd25519 :: Ed2.PublicKey -> Ed2.Signature
+               -> Condition -> Condition
+fulfillEd25519 pk sig (Threshold t subs) =
+  Threshold t $ fulfillEd25519 pk sig <$> subs
+fulfillEd25519 pk sig e@(Ed25519 pk' Nothing) =
+  if pk == pk' then Ed25519 pk (Just sig) else e
+fulfillEd25519 _ _ c = c
+
+
+readStandardFulfillment :: Fulfillment -> Either String Condition
+readStandardFulfillment = readFulfillment
diff --git a/Network/CryptoConditions/Encoding.hs b/Network/CryptoConditions/Encoding.hs
new file mode 100644
--- /dev/null
+++ b/Network/CryptoConditions/Encoding.hs
@@ -0,0 +1,89 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Network.CryptoConditions.Encoding
+  ( x690SortAsn
+  , b64EncodeStripped
+  , b64DecodeStripped
+  , asnSeq
+  , bytesOfUInt
+  , uIntFromBytes
+  , fiveBellsContainer
+  , toData
+  , toKey
+  , parseASN1
+  ) where
+
+
+import Crypto.Error (CryptoFailable(..))
+
+import Data.ASN1.BinaryEncoding
+import Data.ASN1.BinaryEncoding.Raw
+import Data.ASN1.Encoding
+import Data.ASN1.Parse
+import Data.ASN1.Types
+import Data.Bits
+import qualified Data.ByteArray as BA
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BL
+import Data.ByteString.Base64.URL as B64
+import qualified Data.ByteString.Char8 as C8
+import Data.List (sortOn)
+import Data.Monoid
+import Data.Word
+
+
+b64EncodeStripped :: BS.ByteString -> BS.ByteString
+b64EncodeStripped bs =
+  let b64 = B64.encode bs
+   in case C8.elemIndex '=' b64 of Just i -> BS.take i b64
+                                   Nothing -> b64
+
+
+b64DecodeStripped :: BS.ByteString -> Either String BS.ByteString
+b64DecodeStripped bs = 
+  let r = 4 - mod (BS.length bs) 4
+      n = if r == 4 then 0 else r
+   in B64.decode $ bs <> C8.replicate n '='
+
+
+x690SortAsn :: [[ASN1]] -> [[ASN1]]
+x690SortAsn = sortOn (\a -> let b = encodeASN1' DER a in (BS.length b, b))
+
+
+asnSeq :: ASN1ConstructionType -> [ASN1] -> [ASN1]
+asnSeq c args = [Start c] ++ args ++ [End c]
+
+
+fiveBellsContainer :: Integral i => i -> [BS.ByteString] -> [ASN1]
+fiveBellsContainer tid bs =
+  let c = Container Context $ fromIntegral tid
+   in asnSeq c [Other Context i s | (i,s) <- zip [0..] bs]
+
+
+bytesOfUInt :: Integer -> [Word8]
+bytesOfUInt = reverse . list
+  where list i | i <= 0xff = [fromIntegral i]
+               | otherwise = (fromIntegral i .&. 0xff)
+                             : list (i `shiftR` 8)
+
+
+uIntFromBytes :: [Word8] -> Integer
+uIntFromBytes ws =
+  let ns = zip (fromIntegral <$> reverse ws) [0..]
+   in foldl (\r (n,o) -> r .|. (n `shiftL` (o*8))) 0 ns
+
+
+parseASN1 :: BS.ByteString -> ParseASN1 a -> Either String a
+parseASN1 bs act = showErr decoded >>= runParseASN1 act
+  where decoded = decodeASN1 DER $ BL.fromStrict bs
+        showErr = either (Left . show) Right
+
+
+toKey :: CryptoFailable a -> Either String a
+toKey r = case r of
+   CryptoPassed a -> return a
+   CryptoFailed e -> Left $ show e
+
+
+toData :: BA.ByteArrayAccess a => a -> BS.ByteString
+toData = BS.pack . BA.unpack
diff --git a/Network/CryptoConditions/Impl.hs b/Network/CryptoConditions/Impl.hs
new file mode 100644
--- /dev/null
+++ b/Network/CryptoConditions/Impl.hs
@@ -0,0 +1,387 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+
+module Network.CryptoConditions.Impl where
+
+
+import Crypto.Hash
+import qualified Crypto.PubKey.Ed25519 as Ed2
+
+import Control.Monad (when)
+
+import Data.ASN1.BinaryEncoding
+import Data.ASN1.BinaryEncoding.Raw
+import Data.ASN1.Encoding
+import Data.ASN1.Parse
+import Data.ASN1.Types
+import Data.Bits
+import qualified Data.ByteArray as BA
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Base64.URL as B64
+import Data.List (sortOn)
+import Data.Maybe
+import Data.Monoid
+import qualified Data.Set as Set
+import qualified Data.Text as T
+import Data.Text.Encoding (decodeUtf8)
+import Data.Word
+
+import Network.CryptoConditions.Encoding
+
+
+--------------------------------------------------------------------------------
+-- | Class of things that are conditions
+--
+class Show c => IsCondition c where
+  getCost :: c -> Int
+  getType :: c -> ConditionType
+  getFingerprint :: c -> BS.ByteString
+  getFulfillment :: c -> Maybe BS.ByteString
+  getSubtypes :: c -> Set.Set ConditionType
+  parseFulfillment :: Int -> ParseASN1 c
+  verifyMessage :: c -> Message -> Bool
+  anon :: Int -> BS.ByteString -> Int -> Set.Set Int -> c
+
+
+-- Parameter aliases
+type Message = BS.ByteString
+type Fulfillment = BS.ByteString
+type Preimage = BS.ByteString
+type Prefix = BS.ByteString
+type Fingerprint = BS.ByteString
+
+
+encodeCondition :: IsCondition c => c -> BS.ByteString
+encodeCondition = encodeASN1' DER . encodeConditionASN
+
+
+encodeConditionASN :: IsCondition c => c -> [ASN1]
+encodeConditionASN c =
+  let ct = getType c
+      fingerprint = getFingerprint c
+      costBs = BS.pack $ bytesOfUInt $ fromIntegral $ getCost c
+      subtypes = "\a" <> (typeMask $ Set.map typeId $ getSubtypes c)
+      body = [fingerprint, costBs] ++
+             if hasSubtypes ct then [subtypes] else []
+   in fiveBellsContainer (typeId ct) body
+
+
+getConditionURI :: IsCondition c => c -> T.Text
+getConditionURI c =
+  let ct = getType c
+      f = decodeUtf8 $ b64EncodeStripped $ getFingerprint c
+      cost = T.pack $ show $ getCost c
+      subtypes = if hasSubtypes ct
+                    then "&subtypes=" <> typeNames (getSubtypes c)
+                    else ""
+   in "ni:///" <> hashFunc ct <> ";" <> f
+       <> "?fpt=" <> typeName ct <> "&cost="
+       <> cost <> subtypes
+
+
+getFulfillmentBase64 :: IsCondition c => c -> Maybe T.Text
+getFulfillmentBase64 =
+  fmap (decodeUtf8 . B64.encode) . getFulfillment
+
+
+readFulfillment :: IsCondition c => Fulfillment -> Either String c
+readFulfillment bs = parseASN1 bs parsePoly
+
+
+readFulfillmentBase64 :: IsCondition c => Fulfillment -> Either String c
+readFulfillmentBase64 = readFulfillment . B64.decodeLenient 
+
+
+parsePoly :: IsCondition c => ParseASN1 c
+parsePoly = withContainerContext parseFulfillment
+
+
+validate :: IsCondition c => T.Text -> c -> Message -> Bool
+validate condUri ffill msg =
+  verifyMessage ffill msg && getConditionURI ffill == condUri 
+
+
+--------------------------------------------------------------------------------
+-- | Type of a condition
+--
+data ConditionType = CT
+  { typeId :: Int
+  , typeName :: T.Text
+  , hasSubtypes :: Bool
+  , hashFunc :: T.Text
+  }
+  deriving (Show)
+
+
+-- Eq and Ord instances consider only the ID
+--
+instance Eq ConditionType where
+  ct == ct' = typeId ct == typeId ct'
+
+
+instance Ord ConditionType where
+  ct <= ct' = typeId ct <= typeId ct'
+
+
+-- Functions for working with sets of condition types
+--
+typeNames :: Set.Set ConditionType -> T.Text
+typeNames = T.intercalate "," . map typeName . Set.toAscList
+
+
+-- This should figure prepend the range itself.
+typeMask :: Set.Set Int -> BS.ByteString
+typeMask cts =
+  let op i = shiftL 1 (7 - mod i 8)
+      bits = Set.map op cts
+      mask = foldl (.|.) 0 bits :: Int
+   in BS.singleton $ fromIntegral mask
+
+
+unTypeMask :: BS.ByteString -> Set.Set Int
+unTypeMask maskbs = Set.fromList $
+  let [n, w] = fromIntegral <$> BS.unpack maskbs
+   in filter (\i -> 0 /= w .&. (shiftL 1 (n-i))) [0..n]
+
+
+--------------------------------------------------------------------------------
+-- | (0) Preimage Condition
+--
+
+preimageType :: ConditionType
+preimageType = CT 0 "preimage-sha-256" False "sha-256"
+
+
+preimageFulfillment :: BS.ByteString -> Fulfillment
+preimageFulfillment pre = encodeASN1' DER body
+  where body = fiveBellsContainer (typeId preimageType) [pre]
+
+
+preimageCost :: BS.ByteString -> Int
+preimageCost = BS.length
+
+
+preimageFingerprint :: Preimage -> Fingerprint
+preimageFingerprint = sha256
+
+
+parsePreimage :: (Preimage -> c) -> ParseASN1 c
+parsePreimage construct = construct <$> parseOther 0
+
+
+-- | The preimage is assumed to be correct if it has been provided;
+--   it'll show up during URI comparison if it's wrong.
+verifyPreimage :: Preimage -> Message -> Bool
+verifyPreimage _ _ = True
+
+
+--------------------------------------------------------------------------------
+-- | (1) Prefix condition
+
+
+prefixType :: ConditionType
+prefixType = CT 1 "prefix-sha-256" True "sha-256"
+
+
+prefixCost :: IsCondition c => Prefix -> Int -> c -> Int
+prefixCost pre maxMessageLength c =
+  BS.length pre + getCost c + 1024 + maxMessageLength
+
+
+prefixFingerprint :: IsCondition c => Prefix -> Int -> c -> Fingerprint
+prefixFingerprint pre mml cond = sha256 body
+  where body = encodeASN1' DER asn
+        mmlbs = BS.pack $ bytesOfUInt $ fromIntegral mml
+        condAsn = encodeConditionASN cond
+        asn = asnSeq Sequence $
+              [ Other Context 0 pre
+              , Other Context 1 mmlbs
+              ] ++ asnSeq (Container Context 2) condAsn
+
+
+prefixFulfillment :: IsCondition c => Prefix -> Int -> c -> Maybe Fulfillment
+prefixFulfillment pre mml cond =
+  let mmlbs = BS.pack $ bytesOfUInt $ fromIntegral mml
+      msubffill = getFulfillment cond
+      getAsn subffill =
+        let subAsn = either (error . show) id $ decodeASN1' DER $ subffill
+         in asnSeq (Container Context 1) $
+             [ Other Context 0 pre
+             , Other Context 1 mmlbs
+             ] ++ asnSeq (Container Context 2) subAsn
+   in encodeASN1' DER . getAsn <$> msubffill
+
+
+prefixSubtypes :: IsCondition c => c -> Set.Set ConditionType
+prefixSubtypes cond =
+  let cts = Set.singleton $ getType cond
+      all' = Set.union cts $ getSubtypes cond
+   in Set.delete prefixType all'
+
+
+parsePrefix :: IsCondition c => (Prefix -> Int -> c -> c) -> ParseASN1 c
+parsePrefix construct = do
+  (pre, mmlbs) <- (,) <$> parseOther 0 <*> parseOther 1
+  let mml = fromIntegral $ uIntFromBytes $ BS.unpack mmlbs
+  cond <- onNextContainer (Container Context 2) parsePoly
+  pure $ construct pre mml cond
+
+
+verifyPrefix :: IsCondition c => Prefix -> Int -> c -> Message -> Bool
+verifyPrefix prefix mml cond msg =
+  let ok = mml >= BS.length msg
+   in verifyMessage cond (prefix <> msg)
+
+
+--------------------------------------------------------------------------------
+-- | (2) Threshold condition
+--
+
+thresholdType :: ConditionType
+thresholdType = CT 2 "threshold-sha-256" True "sha-256"
+
+
+thresholdFulfillment :: IsCondition c => Word16 -> [c] -> Maybe Fulfillment
+thresholdFulfillment t subs =
+  let ti = fromIntegral t
+      withFf = zip subs (getFulfillment <$> subs)
+      byCost = sortOn ffillCost withFf
+      ffills = take ti $ catMaybes $ snd <$> byCost
+      conds = encodeConditionASN . fst <$> drop ti byCost
+      ffills' = either (error . show) id . decodeASN1' DER <$> ffills
+      asn = asnSeq (Container Context 2) $
+              asnSeq (Container Context 0) (concat ffills') ++
+              asnSeq (Container Context 1) (concat conds)
+      encoded = encodeASN1' DER asn
+   in if length ffills == ti then Just encoded else Nothing
+  where
+    -- order by has ffill then cost of ffill
+    ffillCost (c, Just _) = (0::Int, getCost c)
+    ffillCost _           = (1, 0)
+
+
+thresholdFingerprint :: IsCondition c => Word16 -> [c] -> Fingerprint
+thresholdFingerprint t subs =
+  let asns = encodeConditionASN <$> subs
+   in thresholdFingerprintFromAsns t asns
+
+
+thresholdFingerprintFromAsns :: Word16 -> [[ASN1]] -> Fingerprint
+thresholdFingerprintFromAsns t asns = 
+  let subs' = x690SortAsn asns -- TODO: encode only once?
+      c = Container Context 1
+      asn = asnSeq Sequence $
+        [ Other Context 0 (BS.pack $ bytesOfUInt $ fromIntegral t)
+        , Start c
+        ] ++ concat subs' ++ [End c]
+   in sha256 $ encodeASN1' DER asn
+
+
+thresholdSubtypes :: IsCondition c => [c] -> Set.Set ConditionType
+thresholdSubtypes subs =
+  let cts = Set.fromList (getType <$> subs)
+      all' = Set.unions (cts : (getSubtypes <$> subs))
+   in Set.delete thresholdType all'
+
+
+thresholdCost :: IsCondition c => Word16 -> [c] -> Int
+thresholdCost t subs =
+  let largest = take (fromIntegral t) $ sortOn (*(-1)) $ getCost <$> subs
+   in sum largest + 1024 * length subs
+
+
+parseThreshold :: IsCondition c => (Word16 -> [c] -> c) -> ParseASN1 c
+parseThreshold construct = do
+  ffills <- onNextContainer (Container Context 0) $ getMany parsePoly
+  conds <- onNextContainer (Container Context 1) $ getMany parseCondition
+  let t = fromIntegral $ length ffills
+  pure $ construct t (conds ++ ffills)
+
+
+parseCondition :: IsCondition c => ParseASN1 c
+parseCondition = withContainerContext $ \tid -> do
+  (bs, costbs) <- (,) <$> parseOther 0 <*> parseOther 1
+  let cost = fromIntegral $ uIntFromBytes $ BS.unpack costbs
+      condPart = anon tid bs cost
+  subtypes <- if hasSubtypes $ getType $ condPart mempty
+                 then unTypeMask <$> parseOther 2 else pure mempty
+  pure $ condPart subtypes
+
+
+verifyThreshold :: IsCondition c => Word16 -> [c] -> Message -> Bool
+verifyThreshold m subs msg =
+  let m' = fromIntegral m
+      doVerify c = verifyMessage c msg
+   in m' == length (take m' $ filter (==True) $ map doVerify subs)
+
+
+--------------------------------------------------------------------------------
+-- | (3) RSA-SHA256 Condition
+--
+
+
+--------------------------------------------------------------------------------
+-- | (4) ED25519-SHA256 Condition
+--
+
+
+ed25519Type :: ConditionType
+ed25519Type = CT 4 "ed25519-sha-256" False "sha-256"
+
+
+ed25519Cost :: Int
+ed25519Cost = 131072
+
+
+ed25519Fingerprint :: Ed2.PublicKey -> Fingerprint
+ed25519Fingerprint pk = sha256 body
+  where body = encodeASN1' DER asn
+        asn = [ Start Sequence
+              , Other Context 0 $ toData pk
+              ]
+
+
+ed25519Fulfillment :: Ed2.PublicKey -> Ed2.Signature -> Fulfillment
+ed25519Fulfillment pk sig = encodeASN1' DER body
+  where body = fiveBellsContainer (typeId ed25519Type) [toData pk, toData sig]
+
+
+parseEd25519 :: (Ed2.PublicKey -> Ed2.Signature -> c) -> ParseASN1 c
+parseEd25519 construct = do
+  (bspk, bssig) <- (,) <$> parseOther 0 <*> parseOther 1
+  either throwParseError pure $
+    construct <$> toKey (Ed2.publicKey bspk)
+              <*> toKey (Ed2.signature bssig)
+
+
+verifyEd25519 :: Ed2.PublicKey -> Ed2.Signature -> Message -> Bool
+verifyEd25519 pk = flip (Ed2.verify pk)
+
+--------------------------------------------------------------------------------
+-- Utilities
+
+
+sha256 :: BA.ByteArrayAccess a => a -> BS.ByteString
+sha256 a = BS.pack $ BA.unpack $ (hash a :: Digest SHA256)
+
+
+withContainerContext :: (Int -> ParseASN1 a) -> ParseASN1 a
+withContainerContext fp = do
+  asn <- getNext
+  case asn of
+    (Start c@(Container Context tid)) -> do
+      res <- fp tid
+      end <- getNext
+      if end /= End c then throwParseError "Failed parsing end"
+                      else pure res
+    other -> throwParseError ("Not a container context: " ++ show other)
+
+
+parseOther :: Int -> ParseASN1 BS.ByteString
+parseOther n = do
+  asn <- getNext
+  case asn of
+    (Other Context i bs) ->
+      if n == i then pure bs
+                else throwParseError $ "Invalid context id: " ++ show (n,i)
+    other -> throwParseError "agh" -- TODO
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Crypto Conditions
+
+Targeting spec: draft-thomas-crypto-conditions-02 of December 20, 2016
+
+## Current status
+
+Supports all standard condition types except RSA.
+
+Needs more testing. [Some tests](./test/) exist.
+
+## Design
+
+This approach to Crypto Conditions in Haskell has the goals:
+
+* Simple to understand and work with
+* Easily extensible
+
+The bottleneck to achieving these goals is extensibility; Haskell does not
+support dynamic dispatch like OOP languages, and runtime type casting
+in Haskell is very unnatural and somewhat unsafe.
+
+The solution is to decouple the condition type from the implementation,
+such that the core algorithms and serialization can work with instances
+of an "IsCondition" class, and a polymorphic data type can be implemented
+separately to support the desired condition types and behaviours.
+
+The module [Network.CryptoConditions](./Network/CryptoConditions.hs) supports the standard
+condition types, library authors wishing to extend CryptoConditions
+should copy and paste this file into their own project and define their own
+Condition type.
+
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/cryptoconditions.cabal b/cryptoconditions.cabal
new file mode 100644
--- /dev/null
+++ b/cryptoconditions.cabal
@@ -0,0 +1,55 @@
+name:                cryptoconditions
+version:             0.1.0.0
+synopsis:            Interledger Crypto-Conditions
+description:         Please see README.md
+homepage:            https://github.com/libscott/cryptoconditions-hs
+license:             BSD3
+license-file:        LICENSE
+author:              Scott Sadler
+maintainer:          Scott Sadler <scott@scottsadler.de>
+category:            Crypto, Finance, Network
+build-type:          Simple
+extra-source-files:  README.md
+copyright:           Copyright (C) 2017 Scott Sadler
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      .
+  exposed-modules:     Network.CryptoConditions
+                     , Network.CryptoConditions.Encoding
+                     , Network.CryptoConditions.Impl
+  build-depends:       base >= 4.7 && < 5
+                     , asn1-encoding
+                     , asn1-parse
+                     , asn1-types
+                     , base64-bytestring
+                     , bytestring
+                     , containers
+                     , cryptonite
+                     , memory
+                     , text
+  default-language:    Haskell2010
+
+test-suite cryptoconditions-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Tests.hs
+  build-depends:       base
+                     , cryptoconditions
+                     , aeson
+                     , asn1-encoding
+                     , base16-bytestring
+                     , base64-bytestring
+                     , bytestring
+                     , cryptonite
+                     , aeson-quick
+                     , text
+                     , transformers
+                     , tasty
+                     , tasty-hunit
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/libscott/cryptoconditions-hs
diff --git a/test/Tests.hs b/test/Tests.hs
new file mode 100644
--- /dev/null
+++ b/test/Tests.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import TestFiveBells
+import TestStandard
+
+
+main :: IO ()
+main = defaultMain $ testGroup "Tests" [ fiveBellsSuite
+                                       , standardTests
+                                       ]
+
+
