From 1458cfe5c7bf5ff30f3f9349f27885de6651a6fb Mon Sep 17 00:00:00 2001 From: Thomas Diesler Date: Wed, 21 Dec 2022 08:31:25 +0100 Subject: [PATCH] [#5] Add support for DIDComm Out Of Band Messages --- .../org/nessus/didcomm/model/MessageParser.kt | 30 +++++++ .../org/nessus/didcomm/model/MessageWriter.kt | 31 +++++++ .../didcomm/model/OutOfBandInvitationV2.kt | 87 +++++++++++++++++++ .../org/nessus/didcomm/test/model/Fixtures.kt | 30 +++++++ .../test/model/OutOfBandInvitationTest.kt | 34 ++++++++ 5 files changed, 212 insertions(+) create mode 100644 core/src/main/kotlin/org/nessus/didcomm/model/MessageParser.kt create mode 100644 core/src/main/kotlin/org/nessus/didcomm/model/MessageWriter.kt create mode 100644 core/src/main/kotlin/org/nessus/didcomm/model/OutOfBandInvitationV2.kt create mode 100644 core/src/test/kotlin/org/nessus/didcomm/test/model/Fixtures.kt create mode 100644 core/src/test/kotlin/org/nessus/didcomm/test/model/OutOfBandInvitationTest.kt diff --git a/core/src/main/kotlin/org/nessus/didcomm/model/MessageParser.kt b/core/src/main/kotlin/org/nessus/didcomm/model/MessageParser.kt new file mode 100644 index 00000000..9549f3dd --- /dev/null +++ b/core/src/main/kotlin/org/nessus/didcomm/model/MessageParser.kt @@ -0,0 +1,30 @@ +package org.nessus.didcomm.model + +import com.google.gson.Gson +import org.didcommx.didcomm.message.Message + +/** + * Parses a JSON string to a DIDComm Message + */ +class MessageParser { + + companion object { + private val gson = Gson() + + fun fromJson(json: String) : Message { + val jsonMap: MutableMap = mutableMapOf() + gson.fromJson(json, Map::class.java).forEach { en -> + val enval = en.value!! + when(val key: String = en.key.toString()) { + "created_time" -> jsonMap[key] = (enval as Double).toLong() + "expires_time" -> jsonMap[key] = (enval as Double).toLong() + "custom_headers" -> if ((enval as Map).isNotEmpty()) { + jsonMap[key] = enval + } + else -> jsonMap[key] = enval + } + } + return Message.parse(jsonMap) + } + } +} diff --git a/core/src/main/kotlin/org/nessus/didcomm/model/MessageWriter.kt b/core/src/main/kotlin/org/nessus/didcomm/model/MessageWriter.kt new file mode 100644 index 00000000..5be3b366 --- /dev/null +++ b/core/src/main/kotlin/org/nessus/didcomm/model/MessageWriter.kt @@ -0,0 +1,31 @@ +package org.nessus.didcomm.model + +import com.google.gson.FieldNamingPolicy +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import org.didcommx.didcomm.message.Message + +/** + * Serializes a DIDComm Message to JSON + */ +class MessageWriter { + + companion object { + private val gson: Gson = GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create() + private val prettyGson: Gson = GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .setPrettyPrinting() + .create() + + fun toJson(msg: Message, pretty: Boolean = false) : String { + return toJson(msg as Any, pretty) + } + + fun toJson(obj: Any, pretty: Boolean = false) : String { + val gson = if (pretty) prettyGson else gson + return gson.toJson(obj) + } + } +} diff --git a/core/src/main/kotlin/org/nessus/didcomm/model/OutOfBandInvitationV2.kt b/core/src/main/kotlin/org/nessus/didcomm/model/OutOfBandInvitationV2.kt new file mode 100644 index 00000000..c457553b --- /dev/null +++ b/core/src/main/kotlin/org/nessus/didcomm/model/OutOfBandInvitationV2.kt @@ -0,0 +1,87 @@ +package org.nessus.didcomm.model + +import com.google.gson.Gson + +/** + * [Out of Band Invitation]https://identity.foundation/didcomm-messaging/spec/#out-of-band-messages + * + * { + * "type": "https://didcomm.org/out-of-band/2.0/invitation", + * "id": "", + * "from":"", + * "body": { + * "goal_code": "issue-vc", + * "goal": "To issue a Faber College Graduate credential", + * "accept": [ + * "didcomm/v2", + * "didcomm/aip2;env=rfc587" + * ], + * }, + * "attachments": [ + * { + * "id": "request-0", + * "mime_type": "application/json", + * "data": { + * "json": "" + * } + * } + * ] + * } + */ +data class OutOfBandInvitationV2( + + /** + * Message ID. The id attribute value MUST be unique to the sender, across all messages they send. + * This value MUST be used as the parent thread ID (pthid) for the response message that follows. + * REQUIRED + */ + val id: String, + + /** + * The DID representing the sender to be used by recipients for future interactions. + * REQUIRED + */ + val from: String, + + /** + * A self-attested code the receiver may want to display to the user or use in automatically deciding what to do with the out-of-band message. + * OPTIONAL + */ + val goalCode: String?, + + /** + * A self-attested string that the receiver may want to display to the user about the context-specific goal of the out-of-band message. + * OPTIONAL + */ + val goal: String?, + + /** + * An array of media types in the order of preference for sending a message to the endpoint. + * These identify a profile of DIDComm Messaging that the endpoint supports. + * OPTIONAL + */ + val accept: List?, + + /** + * An array of attachments that will contain the invitation messages in order of preference that the receiver can use in responding to the message. + * Each message in the array is a rough equivalent of the others, and all are in pursuit of the stated goal and goal_code. + * Only one of the messages should be chosen and acted upon. + * OPTIONAL + */ + val attachments: List?, +) { + + companion object { + + /** + * The header conveying the DIDComm MTURI. + * REQUIRED + */ + const val type: String = "https://didcomm.org/out-of-band/2.0/invitation" + + fun fromBody(body: Map): OutOfBandInvitationV2 { + val gson = Gson() + return gson.fromJson(gson.toJson(body), OutOfBandInvitationV2::class.java) + } + } +} diff --git a/core/src/test/kotlin/org/nessus/didcomm/test/model/Fixtures.kt b/core/src/test/kotlin/org/nessus/didcomm/test/model/Fixtures.kt new file mode 100644 index 00000000..75ea5cd6 --- /dev/null +++ b/core/src/test/kotlin/org/nessus/didcomm/test/model/Fixtures.kt @@ -0,0 +1,30 @@ +package org.nessus.didcomm.test.model + +import org.didcommx.didcomm.message.Attachment +import org.didcommx.didcomm.message.Message +import org.nessus.didcomm.model.OutOfBandInvitationV2 + +class OutOfBand { + + companion object { + + const val ALICE_DID = "did:example:alice" + const val FABER_DID = "did:example:faber" + + private const val ID = "1234567890" + private const val TYPE = OutOfBandInvitationV2.type + + val OUT_OF_BAND_INVITATION = Message.builder(ID, mapOf( + "goal_code" to "issue-vc", + "goal" to "To issue a Faber College Graduate credential", + "accept" to listOf("didcomm/v2", "didcomm/aip2;env=rfc587")), + TYPE) + .from(FABER_DID) + .createdTime(1516269022) + .expiresTime(1516385931) + .attachments(listOf( + Attachment.builder( + "request-0", Attachment.Data.parse(mapOf("base64" to "qwerty"))).build())) + .build() + } +} diff --git a/core/src/test/kotlin/org/nessus/didcomm/test/model/OutOfBandInvitationTest.kt b/core/src/test/kotlin/org/nessus/didcomm/test/model/OutOfBandInvitationTest.kt new file mode 100644 index 00000000..366bf8e3 --- /dev/null +++ b/core/src/test/kotlin/org/nessus/didcomm/test/model/OutOfBandInvitationTest.kt @@ -0,0 +1,34 @@ +package org.nessus.didcomm.test.model + +import mu.KotlinLogging +import org.didcommx.didcomm.message.Message +import org.junit.jupiter.api.Test +import org.nessus.didcomm.model.MessageParser +import org.nessus.didcomm.model.MessageWriter +import org.nessus.didcomm.model.OutOfBandInvitationV2 +import kotlin.test.Ignore +import kotlin.test.assertEquals + +@Ignore +class OutOfBandInvitationTest { + + private val log = KotlinLogging.logger {} + + @Test + fun testOutOfBandInvitation() { + + val exp: Message = OutOfBand.OUT_OF_BAND_INVITATION + val expBody = OutOfBandInvitationV2.fromBody(exp.body) + val expJson: String = MessageWriter.toJson(exp) + log.info("exp: {}", expJson) + + val was = MessageParser.fromJson(expJson) + log.info("was: {}", MessageWriter.toJson(was)) + assertEquals(exp, was) + + val wasBody = OutOfBandInvitationV2.fromBody(was.body) + log.info("body: {}", MessageWriter.toJson(wasBody)) + assertEquals(expBody, wasBody) + } + +}