packages feed

cryptoconditions 0.1.0.0 → 0.2.2.1

raw patch · 6 files changed

+195/−48 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Network.CryptoConditions.Impl: typeMask :: Set Int -> ByteString
- Network.CryptoConditions.Impl: unTypeMask :: ByteString -> Set Int
+ Network.CryptoConditions: instance Data.Aeson.Types.FromJSON.FromJSON Network.CryptoConditions.Condition
+ Network.CryptoConditions: instance Data.Aeson.Types.ToJSON.ToJSON Network.CryptoConditions.Condition
+ Network.CryptoConditions.Encoding: fromBitString :: ByteString -> Set Int
+ Network.CryptoConditions.Encoding: toBitString :: Set Int -> ByteString
+ Network.CryptoConditions.Impl: readCondition :: IsCondition c => ByteString -> Either String c
+ Network.CryptoConditions.Json: parseJsonEd25519 :: IsCondition c => (PublicKey -> Maybe Signature -> c) -> Object -> Parser c
+ Network.CryptoConditions.Json: parseJsonPrefix :: (IsCondition c, FromJSON c) => (ByteString -> Int -> c -> c) -> Object -> Parser c
+ Network.CryptoConditions.Json: parseJsonPreimage :: IsCondition c => (ByteString -> c) -> Object -> Parser c
+ Network.CryptoConditions.Json: parseJsonThreshold :: (IsCondition c, FromJSON c) => (Word16 -> [c] -> c) -> Object -> Parser c
+ Network.CryptoConditions.Json: toJsonEd25519 :: PublicKey -> Maybe Signature -> Value
+ Network.CryptoConditions.Json: toJsonPrefix :: (IsCondition c, ToJSON c) => ByteString -> Int -> c -> Value
+ Network.CryptoConditions.Json: toJsonPreimage :: ByteString -> Value
+ Network.CryptoConditions.Json: toJsonThreshold :: (IsCondition c, ToJSON c) => Word16 -> [c] -> Value
- Network.CryptoConditions: fulfillEd25519 :: PublicKey -> Signature -> Condition -> Condition
+ Network.CryptoConditions: fulfillEd25519 :: PublicKey -> SecretKey -> Message -> Condition -> Condition
- Network.CryptoConditions.Impl: ed25519Fulfillment :: PublicKey -> Signature -> Fulfillment
+ Network.CryptoConditions.Impl: ed25519Fulfillment :: PublicKey -> Maybe Signature -> Maybe Fulfillment
- Network.CryptoConditions.Impl: parseEd25519 :: (PublicKey -> Signature -> c) -> ParseASN1 c
+ Network.CryptoConditions.Impl: parseEd25519 :: (PublicKey -> Maybe Signature -> c) -> ParseASN1 c

Files

Network/CryptoConditions.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -------------------------------------------------------------------------------- -- Crypto Conditions Standard API --@@ -18,11 +20,14 @@  import qualified Crypto.PubKey.Ed25519 as Ed2 +import Data.Aeson.Types import Data.ByteString as BS+import Data.Monoid import Data.Word import qualified Data.Set as Set  import Network.CryptoConditions.Impl as CCI+import Network.CryptoConditions.Json as CCJ   data Condition =@@ -37,6 +42,7 @@  instance IsCondition Condition where   getType (Anon 0 _ _ _) = preimageType+  getType (Anon 1 _ _ _) = prefixType   getType (Anon 2 _ _ _) = thresholdType   getType (Anon 4 _ _ _) = ed25519Type   getType (Threshold _ _) = thresholdType@@ -57,7 +63,7 @@   getFingerprint (Anon _ fp _ _) = fp    getFulfillment (Threshold t subs) = thresholdFulfillment t subs-  getFulfillment (Ed25519 pk msig) = ed25519Fulfillment pk <$> msig+  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@@ -70,7 +76,7 @@   parseFulfillment 0 = parsePreimage Preimage   parseFulfillment 1 = parsePrefix Prefix   parseFulfillment 2 = parseThreshold Threshold-  parseFulfillment 4 = parseEd25519 (\a b -> Ed25519 a (Just b))+  parseFulfillment 4 = parseEd25519 Ed25519    verifyMessage (Preimage image) = verifyPreimage image   verifyMessage (Prefix pre mml cond) = verifyPrefix pre mml cond@@ -94,14 +100,35 @@ 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+fulfillEd25519 :: Ed2.PublicKey -> Ed2.SecretKey+               -> Message -> Condition -> Condition+fulfillEd25519 pk sk msg c@(Ed25519 pk' _) =+  if pk == pk' then Ed25519 pk (Just $ Ed2.sign sk pk msg) else c+fulfillEd25519 pk sk msg (Threshold t subs) =+  Threshold t $ fulfillEd25519 pk sk msg <$> subs+fulfillEd25519 pk sk msg (Prefix pre mml sub) =+  Prefix pre mml $ fulfillEd25519 pk sk (pre <> msg) sub+fulfillEd25519 _ _ _ c = c   readStandardFulfillment :: Fulfillment -> Either String Condition readStandardFulfillment = readFulfillment+++instance ToJSON Condition where+  toJSON (Threshold t subs) = toJsonThreshold t subs+  toJSON (Ed25519 pk msig) = toJsonEd25519 pk msig+  toJSON (Prefix pre mml c) = toJsonPrefix pre mml c+  toJSON (Preimage img) = toJsonPreimage img+++instance FromJSON Condition where+  parseJSON = withObject "condition" $ \o -> do+    typeName <- o .: "type"+    let method = case typeName of+         "preimage-sha-256" -> CCJ.parseJsonPreimage Preimage+         "prefix-sha-256" -> parseJsonPrefix Prefix+         "threshold-sha-256" -> parseJsonThreshold Threshold+         "ed25519-sha-256" -> parseJsonEd25519 Ed25519+         _                 -> fail ("Unknown Crypto-Condition type: " ++ typeName)+    method o
Network/CryptoConditions/Encoding.hs view
@@ -11,6 +11,8 @@   , toData   , toKey   , parseASN1+  , toBitString+  , fromBitString   ) where  @@ -18,6 +20,7 @@  import Data.ASN1.BinaryEncoding import Data.ASN1.BinaryEncoding.Raw+import Data.ASN1.BitArray import Data.ASN1.Encoding import Data.ASN1.Parse import Data.ASN1.Types@@ -29,6 +32,7 @@ import qualified Data.ByteString.Char8 as C8 import Data.List (sortOn) import Data.Monoid+import qualified Data.Set as Set import Data.Word  @@ -40,7 +44,7 @@   b64DecodeStripped :: BS.ByteString -> Either String BS.ByteString-b64DecodeStripped bs = +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 '='@@ -87,3 +91,20 @@  toData :: BA.ByteArrayAccess a => a -> BS.ByteString toData = BS.pack . BA.unpack+++-- TODO: Support larger bitstrings++toBitString :: Set.Set Int -> BS.ByteString+toBitString types =+    let words = Set.map fromIntegral types+        bitArray = foldl bitArraySetBit (BitArray 32 "\0") words+        maxId = foldl max 0 types+        bitsUnused = fromIntegral $ 7 - mod maxId 8+     in BS.singleton bitsUnused <> bitArrayGetData bitArray+++fromBitString :: BS.ByteString -> Set.Set Int+fromBitString maskbs = Set.fromList $+  let [_, w] = fromIntegral <$> BS.unpack maskbs :: [Int]+   in filter (\i -> 0 /= w .&. (shiftL 1 (7-i))) [0..7]
Network/CryptoConditions/Impl.hs view
@@ -9,6 +9,7 @@  import Control.Monad (when) +import qualified Data.Aeson.Types as Aeson import Data.ASN1.BinaryEncoding import Data.ASN1.BinaryEncoding.Raw import Data.ASN1.Encoding@@ -60,7 +61,7 @@   let ct = getType c       fingerprint = getFingerprint c       costBs = BS.pack $ bytesOfUInt $ fromIntegral $ getCost c-      subtypes = "\a" <> (typeMask $ Set.map typeId $ getSubtypes c)+      subtypes = toBitString $ Set.map typeId $ getSubtypes c       body = [fingerprint, costBs] ++              if hasSubtypes ct then [subtypes] else []    in fiveBellsContainer (typeId ct) body@@ -92,15 +93,29 @@ readFulfillmentBase64 = readFulfillment . B64.decodeLenient   +readCondition :: IsCondition c => BS.ByteString -> Either String c+readCondition bs = parseASN1 bs parseCondition++ 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 +  verifyMessage ffill msg && getConditionURI ffill == condUri  +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 fromBitString <$> parseOther 2 else pure mempty+  pure $ condPart subtypes++ -------------------------------------------------------------------------------- -- | Type of a condition --@@ -123,27 +138,10 @@   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 --@@ -268,7 +266,7 @@  thresholdFingerprintFromAsns :: Word16 -> [[ASN1]] -> Fingerprint thresholdFingerprintFromAsns t asns = -  let subs' = x690SortAsn asns -- TODO: encode only once?+  let subs' = x690SortAsn asns       c = Container Context 1       asn = asnSeq Sequence $         [ Other Context 0 (BS.pack $ bytesOfUInt $ fromIntegral t)@@ -298,16 +296,6 @@   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@@ -341,17 +329,21 @@               ]  -ed25519Fulfillment :: Ed2.PublicKey -> Ed2.Signature -> Fulfillment-ed25519Fulfillment pk sig = encodeASN1' DER body-  where body = fiveBellsContainer (typeId ed25519Type) [toData pk, toData sig]+ed25519Fulfillment :: Ed2.PublicKey -> Maybe Ed2.Signature -> Maybe Fulfillment+ed25519Fulfillment pk msig = Just $ encodeASN1' DER body+  where body = fiveBellsContainer (typeId ed25519Type) [toData pk, sig]+        sig = maybe "" toData msig  -parseEd25519 :: (Ed2.PublicKey -> Ed2.Signature -> c) -> ParseASN1 c+parseEd25519 :: (Ed2.PublicKey -> Maybe Ed2.Signature -> c) -> ParseASN1 c parseEd25519 construct = do   (bspk, bssig) <- (,) <$> parseOther 0 <*> parseOther 1+  let msig = case bssig of+                  "" -> pure Nothing+                  bs -> Just <$> (toKey (Ed2.signature bs))   either throwParseError pure $     construct <$> toKey (Ed2.publicKey bspk)-              <*> toKey (Ed2.signature bssig)+              <*> msig   verifyEd25519 :: Ed2.PublicKey -> Ed2.Signature -> Message -> Bool
+ Network/CryptoConditions/Json.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.CryptoConditions.Json+  ( parseJsonPreimage+  , parseJsonPrefix+  , parseJsonThreshold+  , parseJsonEd25519+  , toJsonPreimage+  , toJsonPrefix+  , toJsonThreshold+  , toJsonEd25519+  ) where+++import Crypto.PubKey.Ed25519+import Crypto.Error++import Data.Aeson+import Data.Aeson.Types+import qualified Data.ByteArray as BA+import Data.ByteString as BS+import Data.Text+import Data.Text.Encoding+import Data.Word++import Network.CryptoConditions.Encoding+import Network.CryptoConditions.Impl+++-- Parsing+--++parseJsonThreshold :: (IsCondition c, FromJSON c) => (Word16 -> [c] -> c) -> Object -> Parser c+parseJsonThreshold f obj = f <$> obj .: "threshold" <*> obj .: "subfulfillments"+++parseJsonEd25519 :: IsCondition c => (PublicKey -> Maybe Signature -> c) -> Object -> Parser c+parseJsonEd25519 f obj = do+  pub <- obj .: "publicKey" >>= parseKey publicKey+  msig <- obj .:? "signature" >>= mapM (parseKey signature)+  pure $ f pub msig+++parseJsonPrefix :: (IsCondition c, FromJSON c) => (ByteString -> Int -> c -> c) -> Object -> Parser c+parseJsonPrefix f obj = do+  pre <- obj .: "prefix" >>= fromB64+  f pre <$> obj .: "maxMessageLength" <*> obj .: "subfulfillment"+++parseJsonPreimage :: IsCondition c => (ByteString -> c) -> Object -> Parser c+parseJsonPreimage f obj =+  f <$> (obj .: "preimage" >>= fromB64)+++-- Encoding+--++toJsonPreimage :: ByteString -> Value+toJsonPreimage img = object ["type" .= String "preimage-sha-256", "preimage" .= binToJson img]+++toJsonPrefix :: (IsCondition c, ToJSON c) => ByteString -> Int -> c -> Value+toJsonPrefix pre mml sub =+  object [ "type".= String "prefix-sha-256"+         , "prefix" .= binToJson pre+         , "subfulfillment" .= sub+         ]+++toJsonThreshold :: (IsCondition c, ToJSON c) => Word16 -> [c] -> Value+toJsonThreshold threshold subs =+  object [ "type" .= String "threshold-sha-256"+         , "threshold" .= threshold+         , "subfulfillments" .= subs+         ]+++toJsonEd25519 :: PublicKey -> Maybe Signature -> Value+toJsonEd25519 pk msig =+  let sigItem = maybe [] (\sig -> ["signature" .= keyToJson sig]) msig+   in object $ ["type" .= String "ed25519-sha-256", "publicKey" .= keyToJson pk] ++ sigItem+++-- Util+--++fromB64 :: Text -> Parser ByteString+fromB64 = either fail pure . b64DecodeStripped . encodeUtf8+++parseKey :: (ByteString -> CryptoFailable b) -> Text -> Parser b+parseKey f bs = do+  bin <- either fail pure $ b64DecodeStripped $ encodeUtf8 bs+  onCryptoFailure (fail . show) pure $ f bin+++keyToJson :: BA.ByteArrayAccess k => k -> Value+keyToJson = String . decodeUtf8 . b64EncodeStripped . BS.pack . BA.unpack+++binToJson :: ByteString -> Value+binToJson = String . decodeUtf8 . b64EncodeStripped
cryptoconditions.cabal view
@@ -1,5 +1,5 @@ name:                cryptoconditions-version:             0.1.0.0+version:             0.2.2.1 synopsis:            Interledger Crypto-Conditions description:         Please see README.md homepage:            https://github.com/libscott/cryptoconditions-hs@@ -18,7 +18,9 @@   exposed-modules:     Network.CryptoConditions                      , Network.CryptoConditions.Encoding                      , Network.CryptoConditions.Impl+                     , Network.CryptoConditions.Json   build-depends:       base >= 4.7 && < 5+                     , aeson                      , asn1-encoding                      , asn1-parse                      , asn1-types@@ -41,6 +43,7 @@                      , base16-bytestring                      , base64-bytestring                      , bytestring+                     , containers                      , cryptonite                      , aeson-quick                      , text
test/Tests.hs view
@@ -3,13 +3,15 @@ import Test.Tasty import Test.Tasty.HUnit -import TestFiveBells import TestStandard+import TestUnits+import TestVectors   main :: IO ()-main = defaultMain $ testGroup "Tests" [ fiveBellsSuite+main = defaultMain $ testGroup "Tests" [ unitTests                                        , standardTests+                                       , vectorSuite                                        ]