Skip to content

Commit

Permalink
Aries RFC 0434: Out-of-Band Protocol 1.1 (#22)
Browse files Browse the repository at this point in the history
* [WIP] Aries RFC 0434: Out-of-Band Protocol 1.1

* Aries RFC 0434: Out-of-Band Protocol 1.1

* added serviceType

* Add OutOfBand Message Invitation parser method

* Minimizing git diffs

* Minimizing git diffs

* Clean up

* Minor update to Documentation

Co-authored-by: FabioPinheiro <[email protected]>
  • Loading branch information
mineme0110 and FabioPinheiro authored Sep 16, 2022
1 parent 45a18c9 commit 57ed209
Show file tree
Hide file tree
Showing 14 changed files with 348 additions and 94 deletions.
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

0 comments on commit 57ed209

Please sign in to comment.