diff --git a/fission-cli/library/Fission/CLI/Connected.hs b/fission-cli/library/Fission/CLI/Connected.hs index d413a643e..7dd397736 100644 --- a/fission-cli/library/Fission/CLI/Connected.hs +++ b/fission-cli/library/Fission/CLI/Connected.hs @@ -16,7 +16,7 @@ import qualified Network.IPFS.Process.Error as IPFS.Process import qualified Network.IPFS.Timeout.Types as IPFS import qualified Network.IPFS.Types as IPFS -import Web.DID.Types +import Web.DID.Types as DID import qualified Web.UCAN.Resolver.Error as UCAN.Resolver @@ -155,10 +155,7 @@ mkConnected inCfg ipfsTimeout = do let ignoredFiles = Environment.ignored config - cliDID = DID - { publicKey = Key.Ed25519PublicKey $ Ed25519.toPublic secretKey - , method = Key - } + cliDID = DID.Key $ Key.Ed25519PublicKey $ Ed25519.toPublic secretKey cfg = Config { httpManager = getField @"httpManager" inCfg, ..} diff --git a/fission-cli/library/Fission/CLI/Handler/Setup.hs b/fission-cli/library/Fission/CLI/Handler/Setup.hs index 76df7b174..3c0bc4dde 100644 --- a/fission-cli/library/Fission/CLI/Handler/Setup.hs +++ b/fission-cli/library/Fission/CLI/Handler/Setup.hs @@ -7,7 +7,7 @@ import RIO.FilePath import Network.DNS as DNS import Servant.Client -import Web.DID.Types +import Web.DID.Types as DID import qualified Web.UCAN.Types as UCAN @@ -96,7 +96,7 @@ setup maybeOS maybeUsername maybeEmail maybeKeyFile = do Right username -> do baseURL <- getRemoteBaseUrl signingPK <- Key.Store.fetchPublic (Proxy @SigningKey) - _ <- WNFS.create (DID Key $ Key.Ed25519PublicKey signingPK) "/" + _ <- WNFS.create (DID.Key $ Key.Ed25519PublicKey signingPK) "/" Env.init username baseURL Nothing Display.putOk $ "Done! Welcome to Fission, " <> textDisplay username <> " ✨" diff --git a/fission-cli/library/Fission/CLI/Handler/User/Login.hs b/fission-cli/library/Fission/CLI/Handler/User/Login.hs index 8dcec9b80..082a67a52 100644 --- a/fission-cli/library/Fission/CLI/Handler/User/Login.hs +++ b/fission-cli/library/Fission/CLI/Handler/User/Login.hs @@ -48,7 +48,7 @@ import qualified Fission.Web.Serialization as Web.Serializatio -- 🛂 JWT/UCAN -import Web.DID.Types +import Web.DID.Types as DID import qualified Web.UCAN.Claims.Error as UCAN.Claims import qualified Web.UCAN.Proof as UCAN.Proof import qualified Web.UCAN.Resolver.Class as UCAN @@ -159,7 +159,7 @@ consume signingSK baseURL optUsername = do signingPK <- Key.Store.toPublic (Proxy @SigningKey) signingSK let - myDID = DID Key (Ed25519PublicKey signingPK) + myDID = DID.Key (Ed25519PublicKey signingPK) topic = PubSub.Topic $ textDisplay targetDID PubSub.connect baseURL topic \conn -> reattempt 10 do @@ -167,7 +167,7 @@ consume signingSK baseURL optUsername = do aesConn <- secure conn () \(rsaConn :: Secure.Connection m (RSA.PublicKey, RSA.PrivateKey)) -> reattempt 10 do let Secure.Connection {key = (pk, _sk)} = rsaConn - sessionDID = DID Key (RSAPublicKey pk) + sessionDID = DID.Key (RSAPublicKey pk) logDebug @Text "🤝 Device linking handshake: Step 2" broadcastApiData conn sessionDID @@ -274,7 +274,7 @@ produce signingSK baseURL = do secure conn () \(rsaConn@Secure.Connection {key = (_, sk)} :: Secure.Connection m (RSA.PublicKey, RSA.PrivateKey)) -> reattempt 10 do logDebug @Text "🤝 Device linking handshake: Step 2" - requestorTempDID@(DID _ tmpPK) <- listenRaw conn + requestorTempDID@(DID.Key tmpPK) <- listenRaw conn case tmpPK of Ed25519PublicKey _ -> diff --git a/fission-cli/library/Fission/CLI/Handler/User/Register.hs b/fission-cli/library/Fission/CLI/Handler/User/Register.hs index 5a6f84a41..d3ec0f9ef 100644 --- a/fission-cli/library/Fission/CLI/Handler/User/Register.hs +++ b/fission-cli/library/Fission/CLI/Handler/User/Register.hs @@ -20,7 +20,7 @@ import Fission.User.Username.Types import Fission.Web.Auth.Token.Types import Fission.Web.Client as Client -import Web.DID.Types +import Web.DID.Types as DID import qualified Web.UCAN.Types as UCAN import Fission.User.Email.Types @@ -124,7 +124,7 @@ createAccount maybeUsername maybeEmail = do exchangePK <- KeyStore.fetchPublic (Proxy @ExchangeKey) signingPK <- KeyStore.fetchPublic (Proxy @SigningKey) - _ <- WNFS.create (DID Key $ Key.Ed25519PublicKey signingPK) "/" + _ <- WNFS.create (DID.Key $ Key.Ed25519PublicKey signingPK) "/" let form = Registration diff --git a/fission-core/library/Fission/Internal/Orphanage/RSA2048.hs b/fission-core/library/Fission/Internal/Orphanage/RSA2048.hs deleted file mode 100644 index f3f6469ff..000000000 --- a/fission-core/library/Fission/Internal/Orphanage/RSA2048.hs +++ /dev/null @@ -1,11 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Fission.Internal.Orphanage.RSA2048 () where - -import qualified Crypto.PubKey.Ed25519 as Ed25519 -import qualified System.IO.Unsafe as Unsafe - -import Fission.Prelude - -instance Arbitrary Ed25519.SecretKey where - arbitrary = return . Unsafe.unsafePerformIO $ Ed25519.generateSecretKey diff --git a/fission-core/library/Fission/Web/Auth/Token/UCAN.hs b/fission-core/library/Fission/Web/Auth/Token/UCAN.hs index 985e1f425..01f463f5e 100644 --- a/fission-core/library/Fission/Web/Auth/Token/UCAN.hs +++ b/fission-core/library/Fission/Web/Auth/Token/UCAN.hs @@ -73,7 +73,7 @@ getRootDID :: -> m DID getRootDID fallbackPK = \case RootCredential -> - return $ DID Key fallbackPK + return $ DID.Key fallbackPK Nested _ ucan -> do UCAN {claims = Claims {sender}} <- ensureM $ getRoot ucan @@ -135,14 +135,8 @@ mkUCAN :: mkUCAN receiver senderSK nbf exp facts resource potency proof = UCAN {..} where sig = signEd25519 header claims senderSK - - sender = DID - { publicKey = Key.Ed25519PublicKey $ Ed25519.toPublic senderSK - , method = DID.Key - } - + sender = DID.Key $ Key.Ed25519PublicKey $ Ed25519.toPublic senderSK claims = Claims {..} - header = Header { typ = UCAN.Typ.JWT , alg = Key.Ed25519 diff --git a/fission-core/test/Fission/Test.hs b/fission-core/test/Fission/Test.hs index 04529f336..04312e22c 100644 --- a/fission-core/test/Fission/Test.hs +++ b/fission-core/test/Fission/Test.hs @@ -8,7 +8,6 @@ import qualified Fission.Test.Internal.Bool as Bool import qualified Fission.Test.Internal.UTF8 as UTF8 import qualified Fission.Test.Random as Random import qualified Fission.Test.URL as URL -import qualified Fission.Test.User.DID as DID import qualified Fission.Test.Web.Auth.Signature.Ed25519 as Ed import qualified Fission.Test.Web.Auth.Token.Bearer as Bearer import qualified Fission.Test.Web.Auth.Token.UCAN as UCAN @@ -20,7 +19,6 @@ spec :: Spec spec = describe "Fission" do Bool.spec - DID.spec DNS.spec Environment.spec Random.spec diff --git a/fission-core/test/Fission/Test/User/DID.hs b/fission-core/test/Fission/Test/User/DID.hs deleted file mode 100644 index a48f1aae4..000000000 --- a/fission-core/test/Fission/Test/User/DID.hs +++ /dev/null @@ -1,84 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-} - -module Fission.Test.User.DID (spec) where - -import qualified Crypto.PubKey.Ed25519 as Ed25519 -import qualified Data.Aeson as JSON - -import qualified RIO.ByteString.Lazy as Lazy -import Servant.API - -import Crypto.Key.Asymmetric as Key -import Web.DID.Oldstyle.Types -import Web.DID.Types - -import Fission.Test.Prelude - -spec :: Spec -spec = - describe "Serialization" do - context "RSA2048" do - it "serializes to a well-known value" - let - expected :: Lazy.ByteString - expected = "did:key:z13V3Sog2YaUKhdGCmgx9UZuW1o1ShFJYc6DvGYe7NTt689NoL2RtpVs65Zw899YrTN9WuxdEEDm54YxWuQHQvcKfkZwa8HTgokHxGDPEmNLhvh69zUMEP4zjuARQ3T8bMUumkSLGpxNe1bfQX624ef45GhWb3S9HM3gvAJ7Qftm8iqnDQVcxwKHjmkV4hveKMTix4bTRhieVHi1oqU4QCVy4QPWpAAympuCP9dAoJFxSP6TNBLY9vPKLazsg7XcFov6UuLWsEaxJ5SomCpDx181mEgW2qTug5oQbrJwExbD9CMgXHLVDE2QgLoQMmgsrPevX57dH715NXC2uY6vo2mYCzRY4KuDRUsrkuYCkewL8q2oK1BEDVvi3Sg8pbC9QYQ5mMiHf8uxiHxTAmPedv8" - in - encode (DID Key rsaKey) `shouldBe` "\"" <> expected <> "\"" - - context "Ed25519" do - it "serializes to a well-known value" - let - expected :: Text - expected = "did:key:z6MkgYGF3thn8k1Fv4p4dWXKtsXCnLH7q9yw4QgNPULDmDKB" - in - encode (DID Key edKey) `shouldBe` JSON.encode expected - - itsProp' "deserialize . serialize ~ id" \(ed25519pk :: Ed25519.PublicKey) -> - decode (encode . DID Key $ Ed25519PublicKey ed25519pk) `shouldBe` - Just (DID Key $ Ed25519PublicKey ed25519pk) - - itsProp' "lengths is always 56" \(ed25519pk :: Ed25519.PublicKey) -> - Lazy.length (encode . DID Key $ Ed25519PublicKey ed25519pk) `shouldBe` 56 + 2 -- extra 2 for quotes because JSON - - itsProp' "always starts with 'did:key:z6Mk'" \(ed25519pk :: Ed25519.PublicKey) -> - Lazy.take 13 (encode . DID Key $ Ed25519PublicKey ed25519pk) `shouldBe` "\"did:key:z6Mk" - - context "Legacy (AKA `Oldstyle`)" do - it "deserializes to a well-known value" $ - eitherDecodeStrict ("\"" <> encodeUtf8 oldstyle <> "\"") - `shouldBe` Right (DID Key edKey) - - it "can be manually set to display in the Oldstyle format" $ - textDisplay Oldstyle { did = DID Key edKey } `shouldBe` oldstyle - - context "W3C did:key Ed25519 test vectors" do - didKeyTestVectors |> foldMapM \(idx, bs) -> - it ("Deserializes vector #" <> show idx <> " to a valid DID") $ - eitherDecode (encode bs) `shouldSatisfy` isEd25519DidKey - - itsProp' "serialized is isomorphic to ADT" \(did :: DID) -> - JSON.decode (JSON.encode did) `shouldBe` Just did - - itsProp' "is a base58 encoded Key DID" \(did :: DID) -> - Lazy.isPrefixOf "\"did:key:z" (JSON.encode did) - -rsaKey :: Key.Public -Right rsaKey = parseUrlPiece "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB" - -edKey :: Key.Public -Right edKey = parseUrlPiece "Hv+AVRD2WUjUFOsSNbsmrp9fokuwrUnjBcr92f0kxw4=" - -isEd25519DidKey :: Either String DID -> Bool -isEd25519DidKey = \case - Right (DID Key (Ed25519PublicKey _)) -> True - _ -> False - -didKeyTestVectors :: [(Natural, Text)] -didKeyTestVectors = - [ (0, "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp") - , (1, "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG") - , (2, "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf") - ] - -oldstyle :: Text -oldstyle = "did:key:zStEZpzSMtTt9k2vszgvCwF4fLQQSyA15W5AQ4z3AR6Bx4eFJ5crJFbuGxKmbma4" diff --git a/fission-core/test/Fission/Test/Web/Auth/Signature/Ed25519.hs b/fission-core/test/Fission/Test/Web/Auth/Signature/Ed25519.hs index 2391a0809..d2a6a35a0 100644 --- a/fission-core/test/Fission/Test/Web/Auth/Signature/Ed25519.hs +++ b/fission-core/test/Fission/Test/Web/Auth/Signature/Ed25519.hs @@ -6,7 +6,7 @@ import qualified Crypto.Key.Asymmetric as Key import Crypto.Key.Asymmetric.Algorithm.Types import qualified Crypto.PubKey.Ed25519 as Ed25519 -import Web.DID.Types +import Web.DID.Types as DID import qualified Web.UCAN.Internal.Base64.URL as B64.URL import qualified Web.UCAN.RawContent as UCAN import Web.UCAN.Types @@ -26,10 +26,7 @@ spec = sig' = signEd25519 header' claims' sk rawContent = UCAN.RawContent $ B64.URL.encodeJWT header' claims' - did = DID - { publicKey = Key.Ed25519PublicKey pk - , method = Key - } + did = DID.Key $ Key.Ed25519PublicKey pk ucan' = ucan { header = header' diff --git a/fission-web-client/library/Fission/Web/Client.hs b/fission-web-client/library/Fission/Web/Client.hs index e8d20aa53..44d6e271a 100644 --- a/fission-web-client/library/Fission/Web/Client.hs +++ b/fission-web-client/library/Fission/Web/Client.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE AllowAmbiguousTypes #-} - module Fission.Web.Client ( sendRequestM , sendAuthedRequest diff --git a/fission-web-server/library/Fission/Web/Server/Auth/Token/UCAN.hs b/fission-web-server/library/Fission/Web/Server/Auth/Token/UCAN.hs index 3ef862000..8966e8fb4 100644 --- a/fission-web-server/library/Fission/Web/Server/Auth/Token/UCAN.hs +++ b/fission-web-server/library/Fission/Web/Server/Auth/Token/UCAN.hs @@ -1,6 +1,6 @@ module Fission.Web.Server.Auth.Token.UCAN (handler) where -import Web.DID.Types +import Web.DID.Types as DID import Web.UCAN.Resolver as UCAN import qualified Web.UCAN.Types as UCAN import qualified Web.UCAN.Validation as UCAN @@ -42,7 +42,7 @@ handler (Bearer.Token jwt rawContent) = do toAuthorization jwt toAuthorization :: - ( UCAN.Resolver m + ( UCAN.Resolver m , MonadThrow m , MonadLogger m , MonadDB t m @@ -56,7 +56,7 @@ toAuthorization jwt@UCAN.UCAN {claims = UCAN.Claims {..}} = do Left err -> throwM err - Right UCAN.UCAN {claims = UCAN.Claims {sender = DID {publicKey = pk}}} -> + Right UCAN.UCAN {claims = UCAN.Claims {sender = DID.Key pk}} -> runDB (User.getByPublicKey pk) >>= \case Nothing -> Web.Error.throw $ NotFound @User diff --git a/fission-web-server/library/Fission/Web/Server/Handler/User/Create.hs b/fission-web-server/library/Fission/Web/Server/Handler/User/Create.hs index ef972ac8c..465b45f8d 100644 --- a/fission-web-server/library/Fission/Web/Server/Handler/User/Create.hs +++ b/fission-web-server/library/Fission/Web/Server/Handler/User/Create.hs @@ -9,7 +9,7 @@ import Servant.Server.Generic import Fission.Prelude -import Web.DID.Types +import Web.DID.Types as DID import qualified Fission.Web.API.User.Create.Types as User.Create @@ -45,7 +45,7 @@ withDID :: , Challenge.Creator m ) => ServerT User.Create.WithDID m -withDID User.Registration {username, email} DID {..} = do +withDID User.Registration {username, email} (DID.Key publicKey) = do now <- currentTime userId <- Web.Err.ensureM $ User.create username publicKey email now challenge <- Web.Err.ensureM $ Challenge.create userId diff --git a/fission-web-server/library/Fission/Web/Server/Internal/Development.hs b/fission-web-server/library/Fission/Web/Server/Internal/Development.hs index 6d761470d..deddeabc6 100644 --- a/fission-web-server/library/Fission/Web/Server/Internal/Development.hs +++ b/fission-web-server/library/Fission/Web/Server/Internal/Development.hs @@ -27,7 +27,7 @@ import Fission.Prelude import Fission.Internal.Fixture.Key.Ed25519 as Fixture.Ed25519 import Fission.URL.Types -import Web.DID.Types +import Web.DID.Types as DID import Fission.Web.API.Host.Types import Fission.Web.API.Remote @@ -108,10 +108,7 @@ run logFunc dbPool processCtx httpManager tlsManager action = do herokuID = Hku.ID "HEROKU_ID" herokuPassword = Hku.Password "HEROKU_PASSWORD" - fissionDID = DID - { publicKey = Fixture.Ed25519.pk - , method = Key - } + fissionDID = DID.Key $ Fixture.Ed25519.pk environment = LocalDev @@ -185,10 +182,7 @@ mkConfig dbPool processCtx httpManager tlsManager logFunc linkRelayStoreVar mach herokuID = Hku.ID "HEROKU_ID" herokuPassword = Hku.Password "HEROKU_PASSWORD" - fissionDID = DID - { publicKey = Fixture.Ed25519.pk - , method = Key - } + fissionDID = DID.Key $ Fixture.Ed25519.pk ipfsPath = "/usr/local/bin/ipfs" ipfsURLs = Partial.fromJust $ nonEmpty [IPFS.URL $ BaseUrl Http "localhost" 5001 ""] diff --git a/fission-web-server/library/Fission/Web/Server/Internal/Production.hs b/fission-web-server/library/Fission/Web/Server/Internal/Production.hs index d97dbd7cc..99a927999 100644 --- a/fission-web-server/library/Fission/Web/Server/Internal/Production.hs +++ b/fission-web-server/library/Fission/Web/Server/Internal/Production.hs @@ -39,7 +39,7 @@ import Fission.Prelude import Fission.Internal.App import Fission.Time -import Web.DID.Types +import Web.DID.Types as DID import Fission.Web.API.Host.Types as Server import qualified Fission.Web.API.Types as Fission @@ -195,7 +195,7 @@ start middleware runner = do now <- currentTime cfg@Fission.Server.Config {..} <- ask - let DID _ serverPK = fissionDID + let DID.Key serverPK = fissionDID logDebug $ displayShow cfg diff --git a/fission-web-server/library/Fission/Web/Server/Mock/Config.hs b/fission-web-server/library/Fission/Web/Server/Mock/Config.hs index 827438dfc..e7c4889cb 100644 --- a/fission-web-server/library/Fission/Web/Server/Mock/Config.hs +++ b/fission-web-server/library/Fission/Web/Server/Mock/Config.hs @@ -11,7 +11,7 @@ import qualified Network.IPFS.Types as IPFS import Servant import Servant.Server.Experimental.Auth -import Web.DID.Types +import Web.DID.Types as DID import Fission.Prelude @@ -39,11 +39,7 @@ defaultConfig :: Config defaultConfig = Config { now = agesAgo , linkedPeers = pure $ IPFS.Peer "ipv4/fakepeeraddress" - , didVerifier = mkAuthHandler \_ -> - return $ DID - { publicKey = pk - , method = Key - } + , didVerifier = mkAuthHandler \_ -> pure $ DID.Key pk , userVerifier = mkAuthHandler \_ -> pure $ Fixture.entity Fixture.user , authVerifier = mkAuthHandler \_ -> authZ , herokuVerifier = BasicAuthCheck \_ -> pure . Authorized $ Heroku.Auth "FAKE HEROKU" @@ -68,13 +64,8 @@ defaultConfig = Config authZ :: Monad m => m Authorization authZ = return Authorization - { sender = Right did + { sender = Right $ DID.Key $ Fixture.Ed25519.pk , about = Fixture.entity Fixture.user , potency = Just AppendOnly , resource = Subset $ FissionFileSystem "/test/" } - where - did = DID - { publicKey = Fixture.Ed25519.pk - , method = Key - } diff --git a/fission-web-server/library/Fission/Web/Server/Types.hs b/fission-web-server/library/Fission/Web/Server/Types.hs index 6db288347..c1f9e329d 100644 --- a/fission-web-server/library/Fission/Web/Server/Types.hs +++ b/fission-web-server/library/Fission/Web/Server/Types.hs @@ -559,19 +559,19 @@ instance User.Modifier Server where runUserUpdate updatePK pkToText uID "_did" where updatePK = User.updatePublicKeyDB uID pk now - pkToText pk' = textDisplay (DID Key pk') + pkToText pk' = textDisplay (DID.Key pk') addExchangeKey uID key now = runUserUpdate addKey keysToText uID "_exchange" where addKey = User.addExchangeKeyDB uID key now - keysToText keys = Text.intercalate "," (textDisplay . DID Key . Key.RSAPublicKey <$> keys) + keysToText keys = Text.intercalate "," (textDisplay . DID.Key . Key.RSAPublicKey <$> keys) removeExchangeKey uID key now = runUserUpdate removeKey keysToText uID "_exchange" where removeKey = User.removeExchangeKeyDB uID key now - keysToText keys = Text.intercalate "," (textDisplay . DID Key . Key.RSAPublicKey <$> keys) + keysToText keys = Text.intercalate "," (textDisplay . DID.Key . Key.RSAPublicKey <$> keys) setData userId newCID now = do runDB (User.getById userId) >>= \case diff --git a/fission-web-server/test/Fission/Test/Web/Server/Auth.hs b/fission-web-server/test/Fission/Test/Web/Server/Auth.hs index bef1b043e..7330e8c67 100644 --- a/fission-web-server/test/Fission/Test/Web/Server/Auth.hs +++ b/fission-web-server/test/Fission/Test/Web/Server/Auth.hs @@ -20,7 +20,7 @@ import qualified Fission.Web.API.Heroku.Auth.Types as Heroku import Fission.Web.Server.Auth import Fission.Web.Server.Auth.Token.Basic.Class import Fission.Web.Server.Authorization.Types -import Web.DID.Types +import Web.DID.Types as DID import qualified Fission.Test.Web.Server.Auth.Token as Token @@ -41,7 +41,7 @@ spec = context "DID auth" do it "uses the encapsulated function" do - didResult `shouldBe` Right (DID Key Ed25519.pk) + didResult `shouldBe` Right (DID.Key Ed25519.pk) context "heroku auth" do it "uses the encapsulated function" do diff --git a/hs-ucan/library/Crypto/Key/Asymmetric/Public.hs b/hs-ucan/library/Crypto/Key/Asymmetric/Public.hs index 3eb6f07c2..e27c82a4c 100644 --- a/hs-ucan/library/Crypto/Key/Asymmetric/Public.hs +++ b/hs-ucan/library/Crypto/Key/Asymmetric/Public.hs @@ -1,10 +1,20 @@ -module Crypto.Key.Asymmetric.Public (genRSA2048) where +module Crypto.Key.Asymmetric.Public + ( genRSA2048 + , decodeASN1DERRSAPublicKey + , encodeASN1DERRSAPublicKey + ) where import qualified Crypto.PubKey.RSA as RSA + +import qualified Data.ASN1.BitArray as ASN1 +import qualified Data.ASN1.Types as ASN1 +import qualified Data.ByteString as BS +import qualified Data.X509 as X509 import qualified OpenSSL.RSA as OpenSSL import RIO import Web.UCAN.Internal.Orphanage.RSA2048.Private () + genRSA2048 :: MonadIO m => m RSA.PrivateKey genRSA2048 = do pair <- liftIO $ OpenSSL.generateRSAKey' 2048 65537 @@ -36,3 +46,43 @@ genRSA2048 = do Just private_qinv -> return RSA.PrivateKey {..} + + +decodeASN1DERRSAPublicKey :: ByteString -> Either String RSA.PublicKey +decodeASN1DERRSAPublicKey bs = + let + spki = + [ ASN1.Start ASN1.Sequence + , ASN1.Start ASN1.Sequence + , ASN1.OID [1,2,840,113549,1,1,1] + , ASN1.Null + , ASN1.End ASN1.Sequence + , ASN1.BitString (ASN1.toBitArray bs (BS.length bs * 8)) + , ASN1.End ASN1.Sequence + ] + in case ASN1.fromASN1 spki of + Right (X509.PubKeyRSA pk, _) -> + Right pk + + Right (pk, _) -> + Left $ "Couldn't parse RSAPublicKey (ASN1 DER encoded). Different format provided: " <> show pk + + Left err -> + Left $ show err + + +encodeASN1DERRSAPublicKey :: RSA.PublicKey -> ByteString +encodeASN1DERRSAPublicKey pk = + case ASN1.toASN1 (X509.PubKeyRSA pk) [] of + [ ASN1.Start ASN1.Sequence + , ASN1.Start ASN1.Sequence + , ASN1.OID [1,2,840,113549,1,1,1] + , ASN1.Null + , ASN1.End ASN1.Sequence + , ASN1.BitString bitArray + , ASN1.End ASN1.Sequence + ] -> + ASN1.bitArrayGetData bitArray + + _ -> + error "Unexpected ASN1 SubjectPublicKeyInfo encoding of RSA public keys" diff --git a/hs-ucan/library/Crypto/Key/Asymmetric/Public/Types.hs b/hs-ucan/library/Crypto/Key/Asymmetric/Public/Types.hs index 6f24e2e38..5264f1130 100644 --- a/hs-ucan/library/Crypto/Key/Asymmetric/Public/Types.hs +++ b/hs-ucan/library/Crypto/Key/Asymmetric/Public/Types.hs @@ -24,7 +24,9 @@ data Public deriving Eq instance Show Public where - show = Text.unpack . textDisplay + show = \case + Ed25519PublicKey ed -> Text.unpack $ textDisplay ed + RSAPublicKey pk -> show pk instance Display Public where textDisplay (Ed25519PublicKey pk) = textDisplay pk diff --git a/hs-ucan/library/Web/DID/Method/Types.hs b/hs-ucan/library/Web/DID/Method/Types.hs deleted file mode 100644 index a59d8d5a7..000000000 --- a/hs-ucan/library/Web/DID/Method/Types.hs +++ /dev/null @@ -1,23 +0,0 @@ -module Web.DID.Method.Types (Method (..)) where - -import Data.Aeson -import RIO -import Test.QuickCheck - -data Method - = Key - deriving (Show, Eq) - -instance Display Method where - display Key = "key" - -instance Arbitrary Method where - arbitrary = elements [Key] - -instance ToJSON Method where - toJSON Key = "key" - -instance FromJSON Method where - parseJSON = withText "DID.Method" \case - "key" -> return Key - other -> fail $ show other <> " is not an acceptable DID method" diff --git a/hs-ucan/library/Web/DID/Oldstyle/Types.hs b/hs-ucan/library/Web/DID/Oldstyle/Types.hs index f12127b81..cdc61dd6c 100644 --- a/hs-ucan/library/Web/DID/Oldstyle/Types.hs +++ b/hs-ucan/library/Web/DID/Oldstyle/Types.hs @@ -7,8 +7,7 @@ import qualified RIO.ByteString as BS import qualified Web.UCAN.Internal.UTF8 as UTF8 import Crypto.Key.Asymmetric as Key -import Web.DID.Method.Types -import Web.DID.Types +import Web.DID.Types as DID -- | DEPRECATED Encoding of oldstyle Ed25519 DIDs. Manual use only @@ -16,7 +15,7 @@ newtype Oldstyle = Oldstyle { did :: DID } deriving stock (Show, Eq) instance Display Oldstyle where - textDisplay Oldstyle {did = DID Key (Ed25519PublicKey ed)} = + textDisplay Oldstyle {did = DID.Key (Ed25519PublicKey ed)} = mconcat [ "did:key:z" , UTF8.toBase58Text $ BS.pack (0xed : 0x01 : BS.unpack (encodeUtf8 $ textDisplay ed)) diff --git a/hs-ucan/library/Web/DID/Types.hs b/hs-ucan/library/Web/DID/Types.hs index fed6302b5..6455b3625 100644 --- a/hs-ucan/library/Web/DID/Types.hs +++ b/hs-ucan/library/Web/DID/Types.hs @@ -1,35 +1,33 @@ module Web.DID.Types ( DID (..) - -- * Reexport - , module Web.DID.Method.Types ) where -import Data.Aeson as JSON -import qualified Data.Aeson.Types as JSON +import Data.Aeson as JSON +import qualified Data.Aeson.Types as JSON import Data.Swagger -import Control.Lens ((?~)) +import Control.Lens ((?~)) import Test.QuickCheck -import Data.Base58String.Bitcoin as BS58.BTC -import Data.Binary hiding (encode) -import qualified Data.ByteArray as BA -import qualified Data.ByteString.Base64 as BS64 -import Data.Hashable (Hashable (..)) +import Data.Base58String.Bitcoin as BS58.BTC +import Data.Binary hiding (encode) +import qualified Data.ByteArray as BA +import qualified Data.ByteString.Base64 as BS64 +import Data.Hashable (Hashable (..)) import Crypto.Error -import qualified Crypto.PubKey.Ed25519 as Ed25519 +import qualified Crypto.PubKey.Ed25519 as Ed25519 import RIO -import qualified RIO.ByteString as BS -import qualified RIO.Text as Text +import qualified RIO.ByteString as BS +import qualified RIO.Text as Text -import qualified Web.UCAN.Internal.UTF8 as UTF8 +import qualified Web.UCAN.Internal.UTF8 as UTF8 import Servant.API -import Crypto.Key.Asymmetric as Key (Public (..)) -import Web.DID.Method.Types +import Crypto.Key.Asymmetric as Key (Public (..)) +import qualified Crypto.Key.Asymmetric.Public as Public {- | A DID key, broken into its constituant parts @@ -67,29 +65,25 @@ More here: https://github.com/multiformats/unsigned-varint Ed25519 > eitherDecode (encode "did:key:z6MkgYGF3thn8k1Fv4p4dWXKtsXCnLH7q9yw4QgNPULDmDKB") :: Either String DID -Right (DID {method = Key, publicKey = Hv+AVRD2WUjUFOsSNbsmrp9fokuwrUnjBcr92f0kxw4=}) +Right (DID.Key Hv+AVRD2WUjUFOsSNbsmrp9fokuwrUnjBcr92f0kxw4=) RSA > eitherDecode "\"did:key:z1LBnwEktwYLrpPhuwFowdVwAFX5zpRo9rrVzwiRRBBiaBoCR7hNqgstW7VM3T9auNbqTmQW4vdHb61RhTMVgCp1BTxuaKU3anWoERFXpuZvfE39g8u7HS9BeD1QJN1fX6S8vvskaPhxFkwLtGr4ZffkUE57WZpNWM6U6BqdktZxmKzxC85zN4FC9Ws5LHuGfxaCuBLiUfA7qEYVSz1Qu1rZDpD6NyfUNrHJ1ErZduJun976nbFHrmDnU47RcSMFESbNKDi1467tRfubsMrDzebdCFKQ2m1AYysto2i6Wmcnucjt3tnwTqe7Bo8L1g8h8UGA946DF3WeGbPUGqznsUdLq6XLCmJzJkJmrNyeZkswdyPX2Vu6J9E4J12Sb3h9ds7atByamftitEZsf6hPJkLUXGThYpCnmRAArRRfPY2H6cKDc7AcnFPyNHGkab5ZFo4qvgBZytbK1K5oD3HfQ6E2ybLhyC2b8i5wo6DLtm9ufixSJNNTH7Uikk88CmertKR7s12CK1WLEM3Zu5YBZNphnjj7cp8QTodAehRPV9NG1CLEAMJVN79DvYe6SfiafJobcvfD8npfS6jcejcyouQbEpKDG7QAnKS48P4AvgBqDvfNUe54jMkk6r6CoX4LcYGHukZDEnea9kwkEoXkUYS4j1AfbKh44FzSuXbQqZnjjVpThxCNmmNn1E4qHmsFkvGoF3FN55CPkoGfDCvyQJgqmsFmpeTJSy9wzv4MvbqpuATxr7eyxsGeCWQkcDwub32inGpR3reTfzRJECCFZarnXdcC5Pidakb1Wu8L\"" -Right (DID {method = Key, publicKey = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB}) +Right (DID.Key MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB) -} -data DID = DID - { method :: Method - , publicKey :: Key.Public - } deriving (Show, Eq) +data DID + = Key Key.Public + -- More varieties here later + deriving (Show, Eq) -- For FromJSON instance Ord DID where a `compare` b = textDisplay a `compare` textDisplay b instance Arbitrary DID where - arbitrary = do - publicKey <- arbitrary - method <- arbitrary - - return DID {..} + arbitrary = Key <$> arbitrary instance Hashable DID where hashWithSalt salt did = hashWithSalt salt $ textDisplay did @@ -107,21 +101,17 @@ instance FromHttpApiData DID where Right val -> Right val instance Display DID where -- NOTE `pk` here is base2, not base58 - textDisplay (DID method pk) = header <> UTF8.toBase58Text (BS.pack multicodecW8) + textDisplay (Key pk) = "did:key:z" <> UTF8.toBase58Text (BS.pack multicodecW8) where - header :: Text - header = "did:" <> textDisplay method <> ":" <> "z" - multicodecW8 :: [Word8] multicodecW8 = case pk of - Ed25519PublicKey ed -> 0xed : 0x01 : BS.unpack (BA.convert ed) - RSAPublicKey rsa -> 0x00 : 0xF5 : 0x02 : BS.unpack (BS64.decodeLenient . encodeUtf8 $ textDisplay rsa) - {- ^ ^ ^ - | | | - | "expect 373 Bytes", encoded in the mixed-endian format - "raw" - -} + Ed25519PublicKey ed -> + 0xed : 0x01 : BS.unpack (BA.convert ed) + + RSAPublicKey rsa -> + -- breaking change, but spec-adhering format: 0x85 : 0x24 : BS.unpack (Public.encodeASN1DERRSAPublicKey rsa) + 0x00 : 0xF5 : 0x02 : BS.unpack (BS64.decodeLenient . encodeUtf8 $ textDisplay rsa) instance ToJSON DID where toJSON = String . textDisplay @@ -157,10 +147,16 @@ parseText txt = CryptoFailed cryptoError -> fail $ "Unable to parse Ed25519 key: " <> show cryptoError CryptoPassed edPK -> return $ Ed25519PublicKey edPK + (0x85 : 0x24 : rsaASNPublicKeyW8s) -> + case Public.decodeASN1DERRSAPublicKey $ BS.pack rsaASNPublicKeyW8s of + Right pk -> pure $ RSAPublicKey pk + Left err -> fail err + + -- Backwards compatibility (0x00 : 0xF5 : 0x02 : rsaKeyW8s) -> RSAPublicKey <$> parseKeyW8s (BS64.encode $ BS.pack rsaKeyW8s) nope -> fail . show . BS64.encode $ BS.pack nope <> " is not an acceptable did:key" - return $ DID Key pk + return $ Key pk diff --git a/hs-ucan/library/Web/DID/Verification.hs b/hs-ucan/library/Web/DID/Verification.hs new file mode 100644 index 000000000..2e5817dc2 --- /dev/null +++ b/hs-ucan/library/Web/DID/Verification.hs @@ -0,0 +1,27 @@ +module Web.DID.Verification + ( verifySignature + ) where + +import Crypto.Hash.Algorithms +import qualified Crypto.Key.Asymmetric as Key +import qualified Crypto.PubKey.Ed25519 +import qualified Crypto.PubKey.RSA.PKCS15 +import RIO + +import Web.DID.Types as DID +import qualified Web.UCAN.Signature.Error as Signature +import qualified Web.UCAN.Signature.RS256.Types as RSA +import Web.UCAN.Signature.Types as Signature + + +verifySignature :: DID -> ByteString -> Signature -> Either Signature.Error Bool +verifySignature (DID.Key publicKey) signedData signature = + case (publicKey, signature) of + (Key.Ed25519PublicKey pk, Signature.Ed25519 sig) -> + Right $ Crypto.PubKey.Ed25519.verify pk signedData sig + + (Key.RSAPublicKey pk, Signature.RS256 (RSA.Signature sig)) -> do + Right $ Crypto.PubKey.RSA.PKCS15.verify (Just SHA256) pk signedData sig + + (_, _) -> + Left Signature.SignatureDoesNotMatch diff --git a/hs-ucan/library/Web/UCAN/Types.hs b/hs-ucan/library/Web/UCAN/Types.hs index 284b40d0b..2ba8f6651 100644 --- a/hs-ucan/library/Web/UCAN/Types.hs +++ b/hs-ucan/library/Web/UCAN/Types.hs @@ -42,7 +42,7 @@ import Test.QuickCheck import Crypto.Key.Asymmetric as Key import qualified Crypto.Key.Asymmetric.Algorithm.Types as Algorithm -import Web.DID.Types +import Web.DID.Types as DID import Web.UCAN.Header.Types (Header (..)) import qualified Web.UCAN.RawContent as UCAN @@ -93,7 +93,7 @@ instance claims' <- arbitrary let - claims = claims' {sender = DID Key pk} + claims = claims' {sender = DID.Key pk} sig' = case sk of Left rsaSK -> Unsafe.unsafePerformIO $ signRS256 header claims rsaSK @@ -197,11 +197,7 @@ instance nbf <- arbitrary pk <- arbitrary - let - receiver = DID - { publicKey = pk - , method = Key - } + let receiver = DID.Key pk return Claims {..} diff --git a/hs-ucan/library/Web/UCAN/Validation.hs b/hs-ucan/library/Web/UCAN/Validation.hs index d8716b057..0985ce1e8 100644 --- a/hs-ucan/library/Web/UCAN/Validation.hs +++ b/hs-ucan/library/Web/UCAN/Validation.hs @@ -20,7 +20,7 @@ import Data.Aeson import Control.Monad.Time import Crypto.Key.Asymmetric as Key -import Web.DID.Types as User +import Web.DID.Types as DID import Web.SemVer.Types import Web.UCAN.Resolver as Proof @@ -161,7 +161,7 @@ checkRSA2048Signature (UCAN.RawContent raw) ucan@UCAN {..} (RS256.Signature inne where content = encodeUtf8 raw - Claims {sender = User.DID {publicKey}} = claims + Claims {sender = DID.Key publicKey} = claims checkEd25519Signature :: UCAN.RawContent -> UCAN fct rsc ptc -> Either UCAN.Error (UCAN fct rsc ptc) checkEd25519Signature (UCAN.RawContent raw) ucan@UCAN {..} = @@ -175,4 +175,4 @@ checkEd25519Signature (UCAN.RawContent raw) ucan@UCAN {..} = Left $ UCAN.SignatureError InvalidPublicKey where - Claims {sender = User.DID {publicKey}} = claims + Claims {sender = DID.Key publicKey} = claims diff --git a/hs-ucan/test/Main.hs b/hs-ucan/test/Main.hs index 4dcbb83cb..17d453a2f 100644 --- a/hs-ucan/test/Main.hs +++ b/hs-ucan/test/Main.hs @@ -2,10 +2,13 @@ module Main (main) where import Test.Web.UCAN.Prelude -import qualified Test.Web.UCAN as UCAN +import qualified Test.Web.DID as DID +import qualified Test.Web.UCAN as UCAN main :: IO () main = do - spec <- testSpecs $ parallel UCAN.spec + spec <- testSpecs $ parallel $ describe "Web" do + UCAN.spec + DID.spec defaultMain $ testGroup "Tests" spec diff --git a/hs-ucan/test/Test/Web/DID.hs b/hs-ucan/test/Test/Web/DID.hs new file mode 100644 index 000000000..d796cd183 --- /dev/null +++ b/hs-ucan/test/Test/Web/DID.hs @@ -0,0 +1,188 @@ +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} +module Test.Web.DID (spec, rsaKey) where + +import Data.Aeson as JSON +import qualified Data.Aeson.Types as JSON + +import Servant.API + +import qualified RIO.ByteString.Lazy as Lazy + +import Crypto.Key.Asymmetric as Key +import qualified Crypto.Key.Asymmetric.Algorithm.Types as Alg +import qualified Crypto.PubKey.Ed25519 as Ed25519 + +import Test.Web.UCAN.Prelude + +import Web.DID.Oldstyle.Types +import Web.DID.Types as DID +import qualified Web.DID.Verification as DID +import qualified Web.UCAN.Signature as Signature +import qualified Web.UCAN.Signature.Error as Signature + + +spec :: Spec +spec = + describe "DID" do + describe "serialisation" do + describe "RSA2048" do + it "serializes to a well-known value" + let + expected :: Lazy.ByteString + expected = "did:key:z13V3Sog2YaUKhdGCmgx9UZuW1o1ShFJYc6DvGYe7NTt689NoL2RtpVs65Zw899YrTN9WuxdEEDm54YxWuQHQvcKfkZwa8HTgokHxGDPEmNLhvh69zUMEP4zjuARQ3T8bMUumkSLGpxNe1bfQX624ef45GhWb3S9HM3gvAJ7Qftm8iqnDQVcxwKHjmkV4hveKMTix4bTRhieVHi1oqU4QCVy4QPWpAAympuCP9dAoJFxSP6TNBLY9vPKLazsg7XcFov6UuLWsEaxJ5SomCpDx181mEgW2qTug5oQbrJwExbD9CMgXHLVDE2QgLoQMmgsrPevX57dH715NXC2uY6vo2mYCzRY4KuDRUsrkuYCkewL8q2oK1BEDVvi3Sg8pbC9QYQ5mMiHf8uxiHxTAmPedv8" + -- new format: expected = "did:key:z4MXj1wBzi9jUstyNvmiK5WLRRL4rr9UvzPxhry1CudCLKWLyMbP1WoTwDfttBTpxDKf5hAJEjqNbeYx2EEvrJmSWHAu7TJRPTrE3QodbMfRvRNRDyYvaN1FSQus2ziS1rWXwAi5Gpc16bY3JwjyLCPJLfdRWHZhRXiay5FWEkfoSKy6aftnzAvqNkKBg2AxgzGMinR6d1WiH4w5mEXFtUeZkeo4uwtRTd8rD9BoVaHVkGwJkksDybE23CsBNXiNfbweFVRcwfTMhcQsTsYhUWDcSC6QE3zt9h4Rsrj7XRYdwYSK5bc1qFRsg5HULKBp2uZ1gcayiW2FqHFcMRjBieC4LnSMSD1AZB1WUncVRbPpVkn1UGhCU" + in + encode (DID.Key rsaKey) `shouldBe` "\"" <> expected <> "\"" + + describe "Ed25519" do + it "serializes to a well-known value" + let + expected :: Text + expected = "did:key:z6MkgYGF3thn8k1Fv4p4dWXKtsXCnLH7q9yw4QgNPULDmDKB" + in + encode (DID.Key edKey) `shouldBe` JSON.encode expected + + itsProp' "deserialize . serialize ~ id" \(ed25519pk :: Ed25519.PublicKey) -> + decode (encode . DID.Key $ Ed25519PublicKey ed25519pk) `shouldBe` + Just (DID.Key $ Ed25519PublicKey ed25519pk) + + itsProp' "lengths is always 56" \(ed25519pk :: Ed25519.PublicKey) -> + Lazy.length (encode . DID.Key $ Ed25519PublicKey ed25519pk) `shouldBe` 56 + 2 -- extra 2 for quotes because JSON + + itsProp' "always starts with 'did:key:z6Mk'" \(ed25519pk :: Ed25519.PublicKey) -> + Lazy.take 13 (encode . DID.Key $ Ed25519PublicKey ed25519pk) `shouldBe` "\"did:key:z6Mk" + + describe "Legacy (AKA `Oldstyle`)" do + it "deserializes to a well-known value" $ + eitherDecodeStrict ("\"" <> encodeUtf8 oldstyleEdKey <> "\"") + `shouldBe` Right (DID.Key edKey) + + it "can be manually set to display in the Oldstyle format" $ + textDisplay Oldstyle { did = DID.Key edKey } `shouldBe` oldstyleEdKey + + itsProp' "is a base58 encoded Key DID" \(did :: DID) -> + Lazy.isPrefixOf "\"did:key:z" (JSON.encode did) + + itsProp' "serialized is isomorphic to ADT" \(did :: DID) -> + JSON.eitherDecode (JSON.encode did) `shouldBe` Right did + + describe "fixtures" do + + describe "Ed25519" do + + it "verifies alice's signature" do + runSignatureTest Alg.Ed25519 + aliceDIDEncoded + helloWorld + aliceHelloWorldSignature + `shouldBe` + Success (Right True) + + it "verifies bob's signature" do + runSignatureTest Alg.Ed25519 + bobDIDEncoded + helloWorld + bobHelloWorldSignature + `shouldBe` + Success (Right True) + + it "rejects alice imitating bob" do + runSignatureTest Alg.Ed25519 + bobDIDEncoded + helloWorld + aliceHelloWorldSignature + `shouldBe` + Success (Right False) + + it "rejects changed signed data" do + runSignatureTest Alg.Ed25519 + aliceDIDEncoded + somethingElse + aliceHelloWorldSignature + `shouldBe` + Success (Right False) + + describe "W3C did:key test vectors" do + testVectorsW3CEdKey & foldMapM \(idx, bs) -> + it ("Deserializes vector #" <> show idx <> " to a valid DID") $ + eitherDecode (encode bs) `shouldSatisfy` isEd25519DidKey + + describe "RSA" do + it "parses a ts-ucan-generated RSA DID" $ + JSON.fromJSON @DID (JSON.String rsaDIDEncoded) `shouldSatisfy` \case + Success _ -> True + Error _ -> False + + describe "W3C did:key test vectors" do + testVectorsW3CRSA & foldMapM \(idx, bs) -> + it ("Deserializes vector #" <> show idx <> " to a valid DID") $ + eitherDecode (encode bs) `shouldSatisfy` isRSADidKey + + +runSignatureTest :: Alg.Algorithm -> Text -> ByteString -> Text -> JSON.Result (Either Signature.Error Bool) +runSignatureTest alg didEncoded signedData sigEncoded = + fmap + (\(did, signature) -> DID.verifySignature did signedData signature) + (parseDIDAndSignature alg didEncoded sigEncoded) + + +parseDIDAndSignature :: Alg.Algorithm -> Text -> Text -> JSON.Result (DID, Signature.Signature) +parseDIDAndSignature alg didEncoded signatureEncoded = do + did <- JSON.fromJSON @DID $ JSON.String didEncoded + sig <- JSON.parse (Signature.parse alg) $ JSON.String signatureEncoded + return (did, sig) + + +helloWorld :: ByteString +helloWorld = "Hello, World!" + +somethingElse :: ByteString +somethingElse = "Something else" + +aliceDIDEncoded :: Text +aliceDIDEncoded = "did:key:z6Mkk89bC3JrVqKie71YEcc5M1SMVxuCgNx6zLZ8SYJsxALi" + +aliceHelloWorldSignature :: Text +aliceHelloWorldSignature = "tzgopgnzTfvRbaOP+COtqvQJVOfNSKHjlwXz9iMtY/fSWutcCqoeIZqNZwQygu+ntMhsVAst/Rte6I6vAv80AA" + +bobDIDEncoded :: Text +bobDIDEncoded = "did:key:z6MkffDZCkCTWreg8868fG1FGFogcJj5X6PY93pPcWDn9bob" + +bobHelloWorldSignature :: Text +bobHelloWorldSignature = "PV9pjuzha64gVXCUHghTN3aa6r1lKFOdmH4OJPshDBEgqATA06jWojW377RQQClOyWqRmaNkjQ93UyJvteP9Cg" + +rsaDIDEncoded :: Text +rsaDIDEncoded = "did:key:z4MXj1wBzi9jUstyNvmiK5WLRRL4rr9UvzPxhry1CudCLKWLyMbP1WoTwDfttBTpxDKf5hAJEjqNbeYx2EEvrJmSWHAu7TJRPTrE3QodbMfRvRNRDyYvaN1FSQus2ziS1rWXwAi5Gpc16bY3JwjyLCPJLfdRWHZhRXiay5FWEkfoSKy6aftnzAvqNkKBg2AxgzGMinR6d1WiH4w5mEXFtUeZkeo4uwtRTd8rD9BoVaHVkGwJkksDybE23CsBNXiNfbweFVRcwfTMhcQsTsYhUWDcSC6QE3zt9h4Rsrj7XRYdwYSK5bc1qFRsg5HULKBp2uZ1gcayiW2FqHFcMRjBieC4LnSMSD1AZB1WUncVRbPpVkn1UGhCU" + + +rsaKey :: Key.Public +Right rsaKey = parseUrlPiece "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB" + +edKey :: Key.Public +Right edKey = parseUrlPiece "Hv+AVRD2WUjUFOsSNbsmrp9fokuwrUnjBcr92f0kxw4=" + +isEd25519DidKey :: Either String DID -> Bool +isEd25519DidKey = \case + Right (DID.Key (Ed25519PublicKey _)) -> True + _ -> False + +isRSADidKey :: Either String DID -> Bool +isRSADidKey = \case + Right (DID.Key (RSAPublicKey _)) -> True + _ -> False + +testVectorsW3CEdKey :: [(Natural, Text)] +testVectorsW3CEdKey = + [ (0, "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp") + , (1, "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG") + , (2, "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf") + ] + +testVectorsW3CRSA :: [(Natural, Text)] +testVectorsW3CRSA = + [ (0, "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i" ) + , (1, "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2" ) + ] + +oldstyleEdKey :: Text +oldstyleEdKey = "did:key:zStEZpzSMtTt9k2vszgvCwF4fLQQSyA15W5AQ4z3AR6Bx4eFJ5crJFbuGxKmbma4" diff --git a/hs-ucan/test/Test/Web/UCAN.hs b/hs-ucan/test/Test/Web/UCAN.hs index e8802641d..fab79ad56 100644 --- a/hs-ucan/test/Test/Web/UCAN.hs +++ b/hs-ucan/test/Test/Web/UCAN.hs @@ -14,7 +14,7 @@ import Test.Web.UCAN.Example spec :: Spec spec = - describe "Web.UCAN" do + describe "UCAN" do describe "serialization" do itsProp' "serialized is isomorphic to ADT" \(ucan :: UCAN () Resource Potency) -> JSON.eitherDecode (JSON.encode ucan) `shouldBe` Right ucan diff --git a/hs-ucan/test/Test/Web/UCAN/Example.hs b/hs-ucan/test/Test/Web/UCAN/Example.hs index a69e1537e..a5127991e 100644 --- a/hs-ucan/test/Test/Web/UCAN/Example.hs +++ b/hs-ucan/test/Test/Web/UCAN/Example.hs @@ -4,7 +4,7 @@ module Test.Web.UCAN.Example , Potency(..) ) where -import qualified RIO.Text as Text +import qualified RIO.Text as Text import Test.Web.UCAN.Prelude import Web.UCAN.Proof.Class diff --git a/shell.nix b/shell.nix index 9aa4ac435..1c6c8a240 100644 --- a/shell.nix +++ b/shell.nix @@ -22,7 +22,6 @@ deps = { common = [ - pkgs.gnumake unstable.niv ]; @@ -43,8 +42,6 @@ ]; haskell = [ - unstable.ghc - unstable.haskellPackages.implicit-hie unstable.haskell-language-server unstable.stack unstable.stylish-haskell