diff --git a/build.sbt b/build.sbt index 7372bf4a..4a0ddca3 100644 --- a/build.sbt +++ b/build.sbt @@ -24,6 +24,7 @@ lazy val V = new { // val zioJson = "0.4.2" // val zioMunitTest = "0.1.1" val zioHttp = "0.0.5" + val zioConfig = "4.0.0-RC16" // val zioPrelude = "1.0.0-RC19" // // https://mvnrepository.com/artifact/io.github.cquiroz/scala-java-time @@ -61,7 +62,10 @@ lazy val D = new { // val zio = Def.setting("dev.zio" %%% "zio" % V.zio) // val zioStreams = Def.setting("dev.zio" %%% "zio-streams" % V.zio) // val zioJson = Def.setting("dev.zio" %%% "zio-json" % V.zioJson) - val ziohttp = Def.setting("dev.zio" %% "zio-http" % V.zioHttp) + val zioHttp = Def.setting("dev.zio" %% "zio-http" % V.zioHttp) + val zioConfig = Def.setting("dev.zio" %% "zio-config" % V.zioConfig) + val zioConfigMagnolia = Def.setting("dev.zio" %% "zio-config-magnolia" % V.zioConfig) // For deriveConfig + val zioConfigTypesafe = Def.setting("dev.zio" %% "zio-config-typesafe" % V.zioConfig) // For HOCON // val zioPrelude = Def.setting("dev.zio" %%% "zio-prelude" % V.zioPrelude) // // val zioTest = Def.setting("dev.zio" %%% "zio-test" % V.zio % Test) // // val zioTestSBT = Def.setting("dev.zio" %%% "zio-test-sbt" % V.zio % Test) @@ -171,7 +175,7 @@ lazy val httpUtils = crossProject(JSPlatform, JVMPlatform) // project libraryDependencies += D.scalaDID.value, ) .jvmSettings( - libraryDependencies += D.ziohttp.value, + libraryDependencies += D.zioHttp.value, ) lazy val mediator = project @@ -180,7 +184,8 @@ lazy val mediator = project .settings( libraryDependencies += D.scalaDID_imp.value, libraryDependencies += D.scalaDID_peer.value, - libraryDependencies += D.ziohttp.value, + libraryDependencies += D.zioHttp.value, + libraryDependencies ++= Seq(D.zioConfig.value, D.zioConfigMagnolia.value, D.zioConfigTypesafe.value), ) .settings( Compile / mainClass := Some("fmgp.did.demo.MediatorStandalone"), diff --git a/did-mediator/src/main/resources/application.conf b/did-mediator/src/main/resources/application.conf new file mode 100644 index 00000000..b6a4c251 --- /dev/null +++ b/did-mediator/src/main/resources/application.conf @@ -0,0 +1,24 @@ +mediator = { + identity = { + keyAgreement = { + kty = "OKP" + crv = "X25519" + d = "Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c" + d = ${?KEY_AGREEMENT_D} + x = "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw" + x = ${?KEY_AGREEMENT_X} + } + keyAuthentication = { + kty = "OKP" + crv = "Ed25519" + d = "INXCnxFEl0atLIIQYruHzGd5sUivMRyQOzu87qVerug" + d = ${?KEY_AUTHENTICATION_D} + x = "MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA" + x = ${?KEY_AUTHENTICATION_X} + } + endpoint = "https://k8s-int.atalaprism.io/mediator" + endpoint = ${?SERVICE_ENDPOINT} + } + server.http.port = 8080 + # server.http.port = ${?PORT} +} diff --git a/did-mediator/src/main/scala/fmgp/did/comm/mediator/MediatorStandalone.scala b/did-mediator/src/main/scala/fmgp/did/comm/mediator/MediatorStandalone.scala index c3f1bb8e..5cb33019 100644 --- a/did-mediator/src/main/scala/fmgp/did/comm/mediator/MediatorStandalone.scala +++ b/did-mediator/src/main/scala/fmgp/did/comm/mediator/MediatorStandalone.scala @@ -9,6 +9,9 @@ import zio.http.socket._ import zio.http.ZClient.ClientLive import zio.http.Http.Empty import zio.http.Http.Static +import zio.config._ +import zio.config.magnolia._ +import zio.config.typesafe._ import scala.io.Source @@ -19,7 +22,15 @@ import fmgp.did._ import fmgp.did.comm._ import fmgp.did.comm.mediator._ import fmgp.did.comm.protocol._ -import fmgp.did.method.peer.DidPeerResolver +import fmgp.did.method.peer._ + +case class MediatorConfig(endpoint: java.net.URI, keyAgreement: OKPPrivateKey, keyAuthentication: OKPPrivateKey) { + val did = DIDPeer2.makeAgent( + Seq(keyAgreement, keyAuthentication), + Seq(DIDPeerServiceEncoded(s = endpoint.toString())) + ) + val agentLayer = ZLayer(MediatorAgent.make(id = did.id, keyStore = did.keyStore)) +} object MediatorStandalone extends ZIOAppDefault { @@ -31,40 +42,6 @@ object MediatorStandalone extends ZIOAppDefault { .collectZIO[Request] { case Method.GET -> !! / "hello" => ZIO.succeed(Response.text("Hello World! DID Comm Mediator APP")).debug } - - val mediatorAgentLayer = ZLayer( - MediatorAgent.make( // https://k8s-int.atalaprism.io/ - DIDSubject( - "did:peer:2" - + ".Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y" - + ".Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd" - + ".SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9rOHMtaW50LmF0YWxhcHJpc20uaW8vbWVkaWF0b3IiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19" - ), - KeyStore( - Set( - OKPPrivateKey( - kty = KTY.OKP, - crv = Curve.X25519, - d = "Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c", - x = "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw", - kid = Some( - "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9rOHMtaW50LmF0YWxhcHJpc20uaW8vbWVkaWF0b3IiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19#6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y" - ) - ), // keyAgreement - OKPPrivateKey( - kty = KTY.OKP, - crv = Curve.Ed25519, - d = "INXCnxFEl0atLIIQYruHzGd5sUivMRyQOzu87qVerug", - x = "MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA", - kid = Some( - "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9rOHMtaW50LmF0YWxhcHJpc20uaW8vbWVkaWF0b3IiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19#6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd" - ) - ) - ) - ), - ) - ) - override val run = for { _ <- Console.printLine( // https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Mediator """███╗ ███╗███████╗██████╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ @@ -76,23 +53,18 @@ object MediatorStandalone extends ZIOAppDefault { |Yet another server simpler Mediator server DID Comm v2. |Vist: https://github.com/input-output-hk/atala-prism-mediator""".stripMargin ) + configs = ConfigProvider.fromResourcePath() + mediatorConfig <- configs.nested("identity").nested("mediator").load(deriveConfig[MediatorConfig]) _ <- ZIO.log(s"Mediator APP. See https://github.com/input-output-hk/atala-prism-mediator") + _ <- ZIO.log(s"MediatorConfig: $mediatorConfig") + _ <- ZIO.log(s"DID: ${mediatorConfig.did.id.string}") myHub <- Hub.sliding[String](5) _ <- ZStream.fromHub(myHub).run(ZSink.foreach((str: String) => ZIO.logInfo("HUB: " + str))).fork - // pord <- System - // .property("PORD") - // .flatMap { - // case None => System.property("pord") - // case Some(value) => ZIO.succeed(Some(value)) - // } - // .map(_.flatMap(_.toBooleanOption).getOrElse(false)) - port <- System - .property("PORT") - .flatMap { - case None => System.property("port") - case Some(value) => ZIO.succeed(Some(value)) - } - .map(_.flatMap(_.toIntOption).getOrElse(8080)) + port <- configs + .nested("http") + .nested("server") + .nested("mediator") + .load(Config.int("port")) _ <- ZIO.log(s"Starting server on port: $port") server = { val config = ServerConfig(address = new java.net.InetSocketAddress(port)) @@ -114,7 +86,7 @@ object MediatorStandalone extends ZIOAppDefault { ) ) .provideSomeLayer(DidPeerResolver.layerDidPeerResolver) - .provideSomeLayer(mediatorAgentLayer) // .provideSomeLayer(AgentByHost.layer) + .provideSomeLayer(mediatorConfig.agentLayer) // .provideSomeLayer(AgentByHost.layer) .provideSomeLayer(Operations.layerDefault) .provideSomeLayer(client >>> MessageDispatcherJVM.layer) .provideSomeLayer(ZLayer.fromZIO(Ref.make[MediatorDB](MediatorDB.empty))) // TODO move into AgentByHost diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..c0db94e4 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,38 @@ +version: '3.9' + +services: + # mongo: + # image: mongo:5.0 + # ports: + # - 27017:27017 + # #volumes: + # # - ./tmp/mongo:/data/db + # environment: + # - MONGO_INITDB_ROOT_USERNAME=admin + # - MONGO_INITDB_ROOT_PASSWORD=admin + + atalaprism-mediator: + image: ghcr.io/input-output-hk/mediator:0.1.1-SNAPSHOT + ports: + - 8080:8080 + network_mode: host + environment: + # Creates the identity: "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9rOHMtaW50LmF0YWxhcHJpc20uaW8vbWVkaWF0b3IiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19" + - KEY_AGREEMENT_D=Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c + - KEY_AGREEMENT_X=Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw + - KEY_AUTHENTICATION_D=INXCnxFEl0atLIIQYruHzGd5sUivMRyQOzu87qVerug + - KEY_AUTHENTICATION_X=MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA + - SERVICE_ENDPOINT=https://k8s-int.atalaprism.io/mediator + # Config storage + #- DB_URL=mongodb://admin:admin@mongo:27017 + #- MONGODB_USER=admin + #- MONGODB_PASSWORD=admin + # depends_on: + # - "mongo" + + +# RUN +# docker-compose up -d +# docker-compose ps +# docker-compose exec mongo /bin/sh +# docker exec -it mongo-1 bash \ No newline at end of file diff --git a/make-trust-ping-example.md b/make-trust-ping-example.md new file mode 100644 index 00000000..c8f1a2aa --- /dev/null +++ b/make-trust-ping-example.md @@ -0,0 +1,89 @@ + +# Test Mediator (Trust Ping) + +## Compile and Run the Mediator on docker + +```shell +docker:publishLocal # Compile and create the mediator image +docker-compose up #docker run -p 8080:8080 ghcr.io/input-output-hk/mediator:0.1.0-SNAPSHOT +``` + +## Run the client + +```shell +scala-cli repl \ + --dependency app.fmgp::did::0.1.0-M2 \ + --dependency app.fmgp::did-imp::0.1.0-M2 \ + --dependency app.fmgp::did-method-peer::0.1.0-M2 \ + --repo https://oss.sonatype.org/content/repositories/releases +``` + + +```scala +import zio._ +import zio.json._ +import fmgp.crypto._ +import fmgp.did._ +import fmgp.did.comm._ +import fmgp.did.comm.protocol.trustping2._ +import fmgp.did.method.peer._ + +val agent = DIDPeer2.makeAgent( + Seq( + OKPPrivateKey(// keyAgreement + kty = KTY.OKP, + crv = Curve.X25519, + d = "Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c", + x = "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw", + kid = None + ), + OKPPrivateKey(//keyAuthentication + kty = KTY.OKP, + crv = Curve.Ed25519, + d = "INXCnxFEl0atLIIQYruHzGd5sUivMRyQOzu87qVerug", + x = "MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA", + kid = None + ) + ), + Seq(DIDPeerServiceEncoded(s = "http://localhost:5000/")) +) + +val mediator = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9rOHMtaW50LmF0YWxhcHJpc20uaW8vbWVkaWF0b3IiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19" + +val ping = TrustPingWithRequestedResponse(from = agent.id, to = TO(mediator)) + +val program = for { + msg <- Operations.authEncrypt(ping.toPlaintextMessage) + _ <- Console.printLine(msg.toJson) +} yield () + +Unsafe.unsafe { implicit unsafe => // Run side efect + Runtime.default.unsafe + .run(program.provide(Operations.layerDefault ++ DidPeerResolver.layer ++ ZLayer.succeed(agent))) + .getOrThrowFiberFailure() +} +``` + +To send the trust ping you can do a request with curl `curl -X POST http://localhost:8080 -H 'content-type: application/didcomm-encrypted+json' -d'...'` (replace `...` with the encrypted DID Comm message you get from the code above). + +To receive the reply you can have netcat running like `nc -nl -p 5000`. + +Then you can decrypt reply with the follow code: + +```scala + +val reply = """{"ciphertext":"863YmjDPR5mYlJtTEfTUFW11wpCKxsVKpDeEWtQPAqbPlECVIDKaCkh0j_tjpSQomgQL93MjrtjuTo3OnIU2EfHDX8t2mkf84muUSmeQtfMnN_ZmRYPxKw3WG_XUGrE25m8zAcce3RClm13TK9Y3XHeM4vrrExfOBYFcIIUoT9yWPIWb6Y39fRNyvlbNuYcEbsPR3RfvecOcBQJ3YaTlpzVjdgXaICZ4oC_5aVRX3EXsIhmEfjt6lBOGmstGT3zPAsLoP8mjb9GbwOIwpbj4u2BczjMDkUsezuzHsyCeilMGziG5Zr7D64Cjbt8Pg2F11vx4sLW11U3hfjRJ0gnShyBzSAOZmrR_TVBWGaP2KOELRtY4Inp8SzP4-YG89Ie2Cu9ZJesZr4eHYe5qFbI7IZg5lALazTtKodoehFuUDk9YklW8xip5_4-yIWKvj0_j79VBoTf3NiEyRz6CmPiW3Mtx5QJwt56Gb1DkLIkgyO5p_N6mUO3wntOa-bwW4ukYCG8eGnM05ENkIOpVW6DU2b5kvfuQarlG_QRXeUPy5hSjZjDa6WM6HwrbiXCFPc6-s3iMKs7IfrezVt7lN-hUynInR9duWNQLJEbyr_-Ete7r7YJDoORLemF6VGAfihO4ut1ceYIeYpF30saz9n6avVasaDSO_mErx8HvucXpZ8CzwyIO3PBImFi_EFP3VI1JGCE3b5EwUDXbkmUKIj1QbOrt_5kpmojgGP5h9Bi5Fzca9DEJrXsB2MTrXCNMhc1vUH5btMQy3guCmERrzcKIPPFhC8LMZshUzDRU1TDFKBUsUzcPitbi2wxz1zWV9ENnpLWil6pxu0LyNfjfz83GBA","protected":"eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6InE1dW1XUnQ0dE5YeWM1eUlWOG84R1Jaekc5YUZmWlhkeHYxMlIwaktNRDgifSwiYXB2IjoidWRmdmFEZFpKNTFEdFJPZm8tOW1nMXRCOTRhM3ZxRFFhaFdpZXNNZjd3VSIsInNraWQiOiJkaWQ6cGVlcjoyLkV6NkxTZ2h3U0U0Mzd3bkRFMXB0M1g2aFZEVVF6U2pzSHppbnBYM1hGdk1qUkFtN3kuVno2TWtoaDFlNUNFWVlxNkpCVWNUWjZDcDJyYW5DV1JydjdZYXgzTGU0TjU5UjZkZC5TZXlKMElqb2laRzBpTENKeklqb2lhSFIwY0hNNkx5OXJPSE10YVc1MExtRjBZV3hoY0hKcGMyMHVhVzh2YldWa2FXRjBiM0lpTENKeUlqcGJYU3dpWVNJNld5SmthV1JqYjIxdEwzWXlJbDE5IzZMU2dod1NFNDM3d25ERTFwdDNYNmhWRFVRelNqc0h6aW5wWDNYRnZNalJBbTd5IiwiYXB1IjoiWkdsa09uQmxaWEk2TWk1RmVqWk1VMmRvZDFORk5ETTNkMjVFUlRGd2RETllObWhXUkZWUmVsTnFjMGg2YVc1d1dETllSblpOYWxKQmJUZDVMbFo2TmsxcmFHZ3haVFZEUlZsWmNUWktRbFZqVkZvMlEzQXljbUZ1UTFkU2NuWTNXV0Y0TTB4bE5FNDFPVkkyWkdRdVUyVjVTakJKYW05cFdrY3dhVXhEU25wSmFtOXBZVWhTTUdOSVRUWk1lVGx5VDBoTmRHRlhOVEJNYlVZd1dWZDRhR05JU25Cak1qQjFZVmM0ZG1KWFZtdGhWMFl3WWpOSmFVeERTbmxKYW5CaVdGTjNhVmxUU1RaWGVVcHJZVmRTYW1JeU1YUk1NMWw1U1d3eE9TTTJURk5uYUhkVFJUUXpOM2R1UkVVeGNIUXpXRFpvVmtSVlVYcFRhbk5JZW1sdWNGZ3pXRVoyVFdwU1FXMDNlUSIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24iLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiRUNESC0xUFUrQTI1NktXIn0","recipients":[{"encrypted_key":"yhqQA2n2QRm5e8m15hVsRWoL4BcEooLBlROwAYenUIOOwc90jK-9uDO2qAiV8eamgU9GQZ0dNYFM2oyaRQaewnIbCIyM6nwg","header":{"kid":"did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwLyIsInIiOltdLCJhIjpbImRpZGNvbW0vdjIiXX0#6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y"}}],"tag":"Xm_uDGCkZVjgWc8d0dr_N2L5gN8ll_rPvLyexiiImlM","iv":"46H81iydYSwZColseMrf-g"}""" + .fromJson[EncryptedMessage] + .getOrElse(???) + +val program2 = for { + msg <- Operations.anonDecrypt(reply) + _ <- Console.printLine(msg.toJson) +} yield () + +Unsafe.unsafe { implicit unsafe => // Run side efect + Runtime.default.unsafe + .run(program2.provide(Operations.layerDefault ++ DidPeerResolver.layer ++ ZLayer.succeed(agent))) + .getOrThrowFiberFailure() +} +```