Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aries RFC 0434: Out-of-Band Protocol 1.1 #22

Merged
merged 9 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion mercury/prism-mediator/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def didScalaAUX =

lazy val V = new {
val munit = "1.0.0-M6" // "0.7.29"
val munitZio = "0.1.1"

// https://mvnrepository.com/artifact/dev.zio/zio
val zio = "2.0.0"
Expand All @@ -42,6 +43,9 @@ lazy val D = new {

// For munit https://scalameta.org/munit/docs/getting-started.html#scalajs-setup
val munit = Def.setting("org.scalameta" %% "munit" % V.munit % Test)
// For munit zio https://github.com/poslegm/munit-zio
val munitZio = Def.setting("com.github.poslegm" %% "munit-zio" % V.munitZio % Test)

}

// #########################
Expand Down Expand Up @@ -74,7 +78,15 @@ lazy val protocolInvitation = project
.in(file("protocol-invitation"))
.settings(name := "mercury-protocol-invitation", version := VERSION)
.settings(libraryDependencies += D.zio.value)
.settings(libraryDependencies ++= Seq(D.circeCore.value, D.circeGeneric.value, D.circeParser.value))
.settings(
libraryDependencies ++= Seq(
D.circeCore.value,
D.circeGeneric.value,
D.circeParser.value,
D.munit.value,
D.munitZio.value
)
)
.dependsOn(models)

lazy val protocolConnection = project
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*
package io.iohk.atala.mercury.mediator

import io.circe.generic.auto.*
Expand Down Expand Up @@ -102,3 +103,4 @@ object Endpoints {
sendMessageServerEndpoint
)
}
*/
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*
package io.iohk.atala.mercury.mediator

import cats.syntax.all._
Expand Down Expand Up @@ -63,3 +64,4 @@ object Mediator extends ZIOAppDefault {
.provide(MediatorDidComm.mediator ++ MailStorage.layer)
.exitCode
}
*/
50 changes: 21 additions & 29 deletions mercury/prism-mediator/protocol-invitation/Invitation-Protocol.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,49 @@
# Invitation Protocol

This Protocol is parte of the DIDComm Messaging Specification.
This Protocol is parte of the **DIDComm Messaging Specification** but also **Aries RFC 0434: Out-of-Band Protocol 1.1**

Its a out-of-band style protocol.

The out-of-band protocol is used when you wish to engage with another agent and you don't have a DIDComm connection to use for the interaction.

See [https://identity.foundation/didcomm-messaging/spec/#invitation]
See [https://github.com/hyperledger/aries-rfcs/blob/main/features/0434-outofband/README.md]

## PIURI

`https://didcomm.org/out-of-band/2.0/invitation`
Version 1.0: `https://didcomm.org/out-of-band/1.0/invitation`

Version 2.0: `https://didcomm.org/out-of-band/2.0/invitation`

### Roles

- Invitee
- Sender
- Will create the message `https://didcomm.org/out-of-band/2.0/invitation`
- Inviter
- Receiver
- Will accept the invitation

### Notes

- Invitation has expiry date

### Invitee create invitation message

```mermaid
stateDiagram-v2
[*] --> [*]
```

### Inviter accepting invitation (Flow Diagram)
### Sender create invitation message (Flow Diagram)

```mermaid
stateDiagram-v2
[*] --> Invited:Send Invitation
Invited --> Requested:Recieve connection request
Requested --> Responded:Send Connection Response
Responded --> Invited: Send Connection Error Retry if possible stay in same state
Responded --> Completed:Recieve Acknowledgement
Completed --> Responded:Recieve Acknowledgement Error Retry if possible stay in same state
Completed --> [*]
[*] --> Initial
Initial --> await_response:Send out-of-band invitation message
await_response --> done:receive DIDCOMM response message(single use)
await_response --> await_response:recieve DIDCOMM response message(multi use message)
await_response --> error:recieve problem report response
done --> [*]
```

---

### Invitee Confirming (Flow Diagram)
### Receiver accepting invitation (Flow Diagram)

```mermaid
stateDiagram-v2
[*] --> Invited:Recieve Invitation
Invited --> Requested: Send Connection Request
Requested --> Responded: Recieve Connection Response
Responded --> Invited: Send Connection Error Response Retry if possible stay in same state
Responded --> Completed: Send Acknowledgement
Completed --> Responded: Recieve Acknowledgement Error Retry if possible stay in same sate
Completed --> [*]
[*] --> Initial
Initial --> prepare_response:recieve out-of-band invitation message
prepare_response --> done:send DIDCOMM response message
done --> [*]
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.iohk.atala.mercury.protocol.invitation
import java.util.{Base64 => JBase64}
import io.circe.Encoder
import io.circe.generic.semiauto._
import io.circe.syntax._

sealed trait AttachmentData

final case class Base64(base64: String) extends AttachmentData

/** @see
* https://github.com/hyperledger/aries-rfcs/tree/main/concepts/0017-attachments
* @param `@id`
* @param `mime-type`
* @param data
* @param filename
* @param lastmod_time
* @param byte_count
* @param description
*/
final case class AttachmentDescriptor(
`@id`: Option[String] = None,
`mime-type`: Option[String] = None,
data: Base64 = Base64(""),
filename: Option[String] = None,
lastmod_time: Option[String] = None,
byte_count: Option[Int] = None,
description: Option[String] = None
)

object AttachmentDescriptor {
def buildAttachment[A: Encoder](
id: Option[String] = None,
payload: A,
mimeType: Option[String] = Some("application/json")
): AttachmentDescriptor = {
val encoded = JBase64.getUrlEncoder.encodeToString(payload.asJson.noSpaces.getBytes)
AttachmentDescriptor(id, mimeType, Base64(encoded))
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.iohk.atala.mercury.protocol.invitation
import cats.implicits._
import io.circe.syntax._
import io.circe.generic.semiauto._
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder, HCursor, Json}
import io.iohk.atala.mercury.protocol.invitation.v1.{Invitation => InvitationV1}
import io.iohk.atala.mercury.protocol.invitation.v2.{Invitation => InvitationV2}

object InvitationCodec {

implicit val serviceEncoder: Encoder[Service] = deriveEncoder[Service]
implicit val serviceDecoder: Decoder[Service] = deriveDecoder[Service]

implicit val didEncoder: Encoder[Did] = (a: Did) => Json.fromString(a.did)
implicit val didDecoder: Decoder[Did] = (c: HCursor) => c.value.as[String].map(did => Did(did))

implicit val serviceTypeEncoder: Encoder[ServiceType] = Encoder.instance {
case service @ Service(_, _, _, _, _) => service.asJson
case did @ Did(_) => did.asJson
}

implicit val serviceTypeDecoder: Decoder[ServiceType] =
List[Decoder[ServiceType]](
Decoder[Service].widen,
Decoder[Did].widen
).reduceLeft(_ or _)

implicit val base64Encoder: Encoder[Base64] = deriveEncoder[Base64]
implicit val base64eDecoder: Decoder[Base64] = deriveDecoder[Base64]

implicit val attachmentDescriptorEncoder: Encoder[AttachmentDescriptor] = deriveEncoder[AttachmentDescriptor]
implicit val attachmentDescriptorDecoder: Decoder[AttachmentDescriptor] = deriveDecoder[AttachmentDescriptor]

implicit val invitationEncoderV1: Encoder[InvitationV1] = (entity: InvitationV1) =>
Json.obj(
"@id" -> Json.fromString(entity.`@id`),
"@type" -> Json.fromString(entity.`@type`),
"label" -> Json.fromString(entity.label),
"goal" -> Json.fromString(entity.goal),
"goal_code" -> Json.fromString(entity.goal_code),
"accept" -> entity.accept.asJson,
"handshake_protocols" -> entity.handshake_protocols.asJson,
"requests~attach" -> entity.`requests~attach`.asJson,
"services" -> entity.services.asJson
)
implicit val invitationDecoderV1: Decoder[InvitationV1] = deriveDecoder[InvitationV1]

implicit val invitationEncoderV2: Encoder[InvitationV2] = deriveEncoder[InvitationV2]
implicit val invitationDecoderV2: Decoder[InvitationV2] = deriveDecoder[InvitationV2]
}
Original file line number Diff line number Diff line change
@@ -1,52 +1,25 @@
package io.iohk.atala.mercury.protocol.invitation

import io.circe.syntax.EncoderOps
import io.circe.generic.auto._
import java.util.Base64

// TODO split into files

case class CreateInvitation(goal: String, goal_code: String)

case class CreateInvitationResponse(alias: String, invitation: Invitation, invitationUrl: String)

case class Body(goal: String, goal_code: String, accept: Seq[String])

sealed trait ServiceType

case class Service(
id: String,
serviceEndpoint: String,
`type`: String,
recipientKeys: Seq[String],
routingKeys: Seq[String]
) extends ServiceType

case class Did(did: String) extends ServiceType

object CreateInvitationResponse {
val accepts = Seq("didcomm/v2")

def apply(goal: String, goal_code: String): CreateInvitationResponse = {
val body = Body(goal, goal_code, accepts)
val service = Service(
id = "did:prism:PR6vs6GEZ8rHaVgjg2WodM#did-communication",
serviceEndpoint = "http://localhost:8080/create-connection",
`type` = "did-communication",
recipientKeys = Seq("did:prism:PR6vs6GEZ8rHaVgjg2WodM"),
routingKeys = Seq("did:prism:PR6vs6GEZ8rHaVgjg2WodM")
)
val invitation = Invitation(
id = "f3375429-b116-4224-b55f-563d7ef461f1",
`@type` = "https://didcomm.org/out-of-band/2.0/invitation",
label = "Mediator Invitation",
body = body,
handshake_protocols = Seq("https://didcomm.org/didexchange/1.0"),
service = Seq(service)
)
import java.net.URL
import java.{util => ju}
import io.iohk.atala.mercury.protocol.invitation.v2._
import io.iohk.atala.mercury.protocol.invitation.InvitationCodec._
import io.circe._
import io.circe.parser._

object OutOfBand {

def parseLink(url: String): Option[String] = parseLink(new URL(url))
def parseLink(url: URL): Option[String] = (url.getQuery() match {
case str if str.startsWith("_oob=") => Some(str.drop(5))
case _ => None
}).map { e =>
val decoder = ju.Base64.getUrlDecoder()
String(decoder.decode(e))
}

val encodedString = Base64.getUrlEncoder.encodeToString(invitation.asJson.noSpaces.getBytes)
val invitationUrl = s"http://localhost:8080/invitation?_oob=$encodedString"
CreateInvitationResponse(alias = "Mediator", invitation, invitationUrl)
def parseInvitation(url: String): Option[Invitation] = {
parseLink(url).map(e => parse(e).getOrElse(???).as[Invitation].getOrElse(???))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.iohk.atala.mercury.protocol.invitation
import cats.implicits._
import io.circe.syntax._
import io.circe.generic.semiauto._
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder, HCursor, Json}

sealed trait ServiceType

/** Service block
* @see
* https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband
* @param id
* @param `type`
* @param recipientKeys
* @param routingKeys
* @param serviceEndpoint
*/
case class Service(
id: String,
`type`: String,
recipientKeys: Seq[String],
routingKeys: Option[Seq[String]],
serviceEndpoint: String,
) extends ServiceType

case class Did(did: String) extends ServiceType
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.iohk.atala.mercury.protocol

import java.util.UUID

package object invitation {

/** provides new msg id
* @return
*/
def getNewMsgId: String = UUID.randomUUID().toString
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.iohk.atala.mercury.protocol.invitation.v1
import cats.implicits._
import io.circe.syntax._
import io.circe.generic.semiauto._
import io.circe.{Encoder, Json}
import io.iohk.atala.mercury.model.PIURI

import scala.annotation.targetName
import io.iohk.atala.mercury.protocol.invitation.AttachmentDescriptor
import io.iohk.atala.mercury.protocol.invitation.ServiceType

/** Out-Of-Band invitation Example
* @see
* https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband
*
* @param `id`
* @param label
* @param goal
* @param goal_code
* @param handshake_protocols
* @param `request~attach`
* @param services
*/
final case class Invitation(
`@id`: String = io.iohk.atala.mercury.protocol.invitation.getNewMsgId,
label: String,
goal: String,
goal_code: String,
accept: Seq[String],
handshake_protocols: Seq[String],
`requests~attach`: Seq[AttachmentDescriptor],
services: Seq[ServiceType]
) {
val `@type`: PIURI = "https://didcomm.org/out-of-band/2.0/invitation"
}
Loading