Skip to content

Commit

Permalink
feat(edgeagent): adds support for connectionless issuance and present…
Browse files Browse the repository at this point in the history
…ation

Signed-off-by: goncalo-frade-iohk <[email protected]>
  • Loading branch information
goncalo-frade-iohk committed Sep 26, 2024
1 parent 65ff99d commit 7a5398e
Show file tree
Hide file tree
Showing 27 changed files with 263 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,22 +113,22 @@ struct LongFormPrismDIDResolver: DIDResolverDomain {
)
}

let decodedPublicKeys = publicKeys.map {
let decodedPublicKeys = publicKeys.enumerated().map {
let didUrl = DIDUrl(
did: did,
fragment: $0.id
fragment: $0.element.usage.id(index: $0.offset - 1)
)

let method = DIDDocument.VerificationMethod(
id: didUrl,
controller: did,
type: $0.keyData.getProperty(.curve) ?? "",
publicKeyMultibase: $0.keyData.raw.base64EncodedString()
type: $0.element.keyData.getProperty(.curve) ?? "",
publicKeyMultibase: $0.element.keyData.raw.base64EncodedString()
)

return PublicKeyDecoded(
id: didUrl.string,
keyType: .init(usage: $0.usage),
keyType: .init(usage: $0.element.usage),
method: method
)
}
Expand Down
12 changes: 6 additions & 6 deletions EdgeAgentSDK/Castor/Sources/Resolvers/PeerDIDResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,15 @@ extension DIDCore.DIDDocument.VerificationMethod {
}

extension DIDCore.DIDDocument.Service {
init(from: AnyCodable) throws {
init(from: DIDCore.AnyCodable) throws {
guard
let dic = from.value as? [String: Any],
let id = dic["id"] as? String,
let type = dic["type"] as? String,
let serviceEndpoint = dic["serviceEndpoint"]
else { throw CommonError.invalidCoding(message: "Could not decode service") }
switch serviceEndpoint {
case let value as AnyCodable:
case let value as DIDCore.AnyCodable:
self = .init(
id: id,
type: type,
Expand All @@ -221,26 +221,26 @@ extension DIDCore.DIDDocument.Service {
self = .init(
id: id,
type: type,
serviceEndpoint: AnyCodable(value)
serviceEndpoint: DIDCore.AnyCodable(value)
)
case let value as [String: Any]:
self = .init(
id: id,
type: type,
serviceEndpoint: AnyCodable(value)
serviceEndpoint: DIDCore.AnyCodable(value)
)
case let value as [String]:
self = .init(
id: id,
type: type,
serviceEndpoint: AnyCodable(value)
serviceEndpoint: DIDCore.AnyCodable(value)
)
default:
throw CommonError.invalidCoding(message: "Could not decode service")
}
}

func toAnyCodable() -> AnyCodable {
func toAnyCodable() -> DIDCore.AnyCodable {
AnyCodable(dictionaryLiteral:
("id", self.id),
("type", self.type),
Expand Down
2 changes: 2 additions & 0 deletions EdgeAgentSDK/Castor/Tests/DIDParserTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Apollo
@testable import Castor
@testable import Domain
import XCTest

Expand Down
36 changes: 18 additions & 18 deletions EdgeAgentSDK/Domain/Sources/AnyCodable.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import Foundation

struct AnyCodable {
let value: Any
public struct AnyCodable {
public let value: Any

init<T: Codable>(_ value: T) {
public init<T: Codable>(_ value: T) {
self.value = value
}

init(_ value: Any) {
self.value = value
}

func get<T>() -> T? {
public func get<T>() -> T? {
value as? T
}

func get() -> Any {
public func get() -> Any {
value
}
}

extension AnyCodable: Codable {
init(from decoder: Decoder) throws {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

if container.decodeNil() {
Expand All @@ -48,7 +48,7 @@ extension AnyCodable: Codable {
}
}

func encode(to encoder: Encoder) throws {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()

switch self.value {
Expand Down Expand Up @@ -103,7 +103,7 @@ extension AnyCodable: Codable {
}

extension AnyCodable: Equatable {
static func ==(lhs: AnyCodable, rhs: AnyCodable) -> Bool {
public static func ==(lhs: AnyCodable, rhs: AnyCodable) -> Bool {
switch (lhs.value, rhs.value) {
case is (Void, Void):
return true
Expand Down Expand Up @@ -146,7 +146,7 @@ extension AnyCodable: Equatable {
}

extension AnyCodable: CustomStringConvertible {
var description: String {
public var description: String {
switch value {
case is Void:
return String(describing: nil as Any?)
Expand All @@ -159,7 +159,7 @@ extension AnyCodable: CustomStringConvertible {
}

extension AnyCodable: CustomDebugStringConvertible {
var debugDescription: String {
public var debugDescription: String {
switch value {
case let value as CustomDebugStringConvertible:
return "AnyCodable(\(value.debugDescription))"
Expand All @@ -171,35 +171,35 @@ extension AnyCodable: CustomDebugStringConvertible {

extension AnyCodable: ExpressibleByNilLiteral, ExpressibleByBooleanLiteral, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {

init(nilLiteral: ()) {
public init(nilLiteral: ()) {
self.init(nil ?? ())
}

init(booleanLiteral value: Bool) {
public init(booleanLiteral value: Bool) {
self.init(value)
}

init(integerLiteral value: Int) {
public init(integerLiteral value: Int) {
self.init(value)
}

init(floatLiteral value: Double) {
public init(floatLiteral value: Double) {
self.init(value)
}

init(extendedGraphemeClusterLiteral value: String) {
public init(extendedGraphemeClusterLiteral value: String) {
self.init(value)
}

init(stringLiteral value: String) {
public init(stringLiteral value: String) {
self.init(value)
}

init(arrayLiteral elements: Any...) {
public init(arrayLiteral elements: Any...) {
self.init(elements)
}

init(dictionaryLiteral elements: (AnyHashable, Any)...) {
public init(dictionaryLiteral elements: (AnyHashable, Any)...) {
self.init(Dictionary<AnyHashable, Any>(elements, uniquingKeysWith: { (first, _) in first }))
}
}
10 changes: 8 additions & 2 deletions EdgeAgentSDK/Domain/Sources/Models/Message+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extension Message: Codable {
enum CodingKeys: String, CodingKey {
case id
case piuri
case type
case from
case to
case fromPrior
Expand All @@ -21,7 +22,7 @@ extension Message: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(piuri, forKey: .piuri)
try container.encode(piuri, forKey: .type)
if let dic = try? JSONSerialization.jsonObject(with: body) as? [String: Any?] {
var filteredDictionary = dic
for (key, value) in dic {
Expand Down Expand Up @@ -49,7 +50,12 @@ extension Message: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let id = try container.decode(String.self, forKey: .id)
let piuri = try container.decode(String.self, forKey: .piuri)
let piuri: String
if let type = try container.decodeIfPresent(String.self, forKey: .piuri) {
piuri = type
} else {
piuri = try container.decode(String.self, forKey: .type)
}
let body: Data?
if
let bodyCodable = try? container.decodeIfPresent(AnyCodable.self, forKey: .body),
Expand Down
6 changes: 3 additions & 3 deletions EdgeAgentSDK/Domain/Sources/Models/MessageAttachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ public struct AttachmentLinkData: AttachmentData {
/// The `AttachmentJsonData` struct represents a DIDComm attachment containing JSON data.
public struct AttachmentJsonData: AttachmentData {
/// The JSON data associated with the attachment.
public let data: Data
public let json: AnyCodable

/// Initializes a new `AttachmentJsonData` object with the specified properties.
/// - Parameter data: The JSON data associated with the attachment.
public init(data: Data) {
self.data = data
public init(json: AnyCodable) {
self.json = json
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public extension DIDCommAgent {
}
jsonData = decoded
case let attchedData as AttachmentJsonData:
jsonData = attchedData.data
jsonData = try JSONEncoder.didComm().encode(attchedData.json)
default:
throw EdgeAgentError.invalidAttachmentFormat(nil)
}
Expand Down Expand Up @@ -141,7 +141,7 @@ public extension DIDCommAgent {
}
jsonData = decoded
case let attchedData as AttachmentJsonData:
jsonData = attchedData.data
jsonData = try JSONEncoder.didComm().encode(attchedData.json)
default:
throw EdgeAgentError.invalidAttachmentFormat(nil)
}
Expand Down Expand Up @@ -210,7 +210,7 @@ public extension DIDCommAgent {
}
jsonData = decoded
case let attchedData as AttachmentJsonData:
jsonData = attchedData.data
jsonData = try JSONEncoder.didComm().encode(attchedData.json)
default:
throw EdgeAgentError.invalidAttachmentFormat(nil)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,28 @@ public extension DIDCommAgent {
case onboardingPrism(PrismOnboarding)
/// Case representing a DIDComm Out-of-Band invitation
case onboardingDIDComm(OutOfBandInvitation)
/// Case representing a DIDComm Connectionless Presentation
case connectionlessPresentation(RequestPresentation)
/// Case representing a DIDComm Connectionless Issuance
case connectionlessIssuance(OfferCredential3_0)
}

/// Parses the given string as an Out-of-Band invitation
/// - Parameter url: The string to parse
/// - Returns: The parsed Out-of-Band invitation
/// - Throws: `EdgeAgentError` if the string is not a valid URL
func parseOOBInvitation(url: String) throws -> OutOfBandInvitation {
guard let url = URL(string: url) else { throw CommonError.invalidURLError(url: url) }
return try parseOOBInvitation(url: url)
guard let messageData = Data(fromBase64URL: url) else {
guard let url = URL(string: url) else {
throw CommonError.invalidURLError(url: url)
}
return try parseOOBInvitation(url: url)
}
let message = try JSONDecoder.didComm().decode(OutOfBandInvitation.self, from: messageData)
guard message.type == ProtocolTypes.didcomminvitation.rawValue else {
throw EdgeAgentError.unknownInvitationTypeError
}
return message
}

/// Parses the given URL as an Out-of-Band invitation
Expand Down Expand Up @@ -71,8 +84,26 @@ public extension DIDCommAgent {
func parseInvitation(str: String) async throws -> InvitationType {
if let prismOnboarding = try? await parsePrismInvitation(str: str) {
return .onboardingPrism(prismOnboarding)
} else if let message = try? parseOOBInvitation(url: str) {
return .onboardingDIDComm(message)
} else if let oobMessage = try? parseOOBInvitation(url: str) {
if let attachment = oobMessage.attachments?.first {
let invitationType = try await parseAttachmentConnectionlessMessage(oob: oobMessage, attachment: attachment)
switch invitationType {
case .connectionlessPresentation(let message):
try await pluto.storeMessage(
message: message.makeMessage(),
direction: .received
).first().await()
case .connectionlessIssuance(let message):
try await pluto.storeMessage(
message: message.makeMessage(),
direction: .received
).first().await()
default:
break
}
return invitationType
}
return .onboardingDIDComm(oobMessage)
}
throw EdgeAgentError.unknownInvitationTypeError
}
Expand Down Expand Up @@ -127,4 +158,35 @@ public extension DIDCommAgent {
).run()
try await connectionManager.addConnection(pair)
}

private func parseAttachmentConnectionlessMessage(
oob: OutOfBandInvitation,
attachment: AttachmentDescriptor
) async throws -> InvitationType {
let newDID = try await createNewPeerDID(updateMediator: true)
switch attachment.data {
case let value as AttachmentJsonData:
let normalizeJson = try JSONEncoder.didComm().encode(value.json)
let message = try JSONDecoder.didComm().decode(Message.self, from: normalizeJson)
if let request = try? RequestPresentation(fromMessage: message, toDID: newDID) {
return .connectionlessPresentation(request)
}
else if let offer = try? OfferCredential3_0(fromMessage: message, toDID: newDID){
return .connectionlessIssuance(offer)
}
return .onboardingDIDComm(oob)

case let value as AttachmentBase64:
let message = try JSONDecoder.didComm().decode(Message.self, from: try value.decoded())
if let request = try? RequestPresentation(fromMessage: message, toDID: newDID) {
return .connectionlessPresentation(request)
}
else if let offer = try? OfferCredential3_0(fromMessage: message, toDID: newDID){
return .connectionlessIssuance(offer)
}
return .onboardingDIDComm(oob)
default:
return .onboardingDIDComm(oob)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public struct OutOfBandInvitation: Decodable {
public let type = ProtocolTypes.didcomminvitation.rawValue
public let from: String
public let body: Body
public let attachments: [AttachmentDescriptor]?

/**
Creates a new `OutOfBandInvitation` object.
Expand All @@ -42,11 +43,13 @@ public struct OutOfBandInvitation: Decodable {
init(
id: String = UUID().uuidString,
body: Body,
from: DID
from: DID,
attachments: [AttachmentDescriptor]? = nil
) {
self.id = id
self.body = body
self.from = from.string
self.attachments = attachments
}
}

Loading

0 comments on commit 7a5398e

Please sign in to comment.