Skip to content

Commit

Permalink
feat(agent): implement issue credential protocol
Browse files Browse the repository at this point in the history
Fixes ATL-2499
  • Loading branch information
goncalo-frade-iohk committed Nov 27, 2022
1 parent c9cbad4 commit 5f48513
Show file tree
Hide file tree
Showing 21 changed files with 899 additions and 46 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ opt_in_rules:
- xct_specific_matcher
- yoda_condition
excluded:
- .build
- build
- Pods
- protobuf
Expand Down
22 changes: 2 additions & 20 deletions Castor/Sources/Helpers/Base64+DIDMethodID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,10 @@ import Foundation

extension Base64Utils {
func encodeMethodID(data: Data) -> String {
encode(data)
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "+", with: "-")
.trimingTrailing(while: CharacterSet(charactersIn: "="))
data.base64UrlEncodedString()
}

func decodeMethodID(str: String) -> Data? {
let expectedLength = (str.count + 3) / 4 * 4
return decode(str
.replacingOccurrences(of: "_", with: "/")
.replacingOccurrences(of: "-", with: "+")
.appending(String(repeating: .init("="), count: expectedLength))
)
}
}

private extension String {
func trimingTrailing(while characterSet: CharacterSet) -> String {
guard
let index = lastIndex(where: { !CharacterSet(charactersIn: String($0)).isSubset(of: characterSet) })
else { return self }

return String(self[...index])
return Data(fromBase64URL: str)
}
}
20 changes: 0 additions & 20 deletions Core/Sources/Base64Utils.swift

This file was deleted.

44 changes: 44 additions & 0 deletions Core/Sources/Helpers/Base64Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Foundation

public struct Base64Utils {
public init() {}

public func encode(_ data: Data) -> String {
String(data.base64EncodedString()
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "+", with: "-")
.trimingTrailing(while: CharacterSet(charactersIn: "=")))
}

public func decode(_ src: String) -> Data? {
let expectedLength = (src.count + 3) / 4 * 4
let base64Encoded = src
.replacingOccurrences(of: "_", with: "/")
.replacingOccurrences(of: "-", with: "+")
.appending(String(repeating: .init("="), count: expectedLength))
return Data(base64Encoded: base64Encoded)
}
}

public extension Data {
func base64UrlEncodedString() -> String {
Base64Utils().encode(self)
}

init?(fromBase64URL: String) {
guard let data = Base64Utils().decode(fromBase64URL) else {
return nil
}
self = data
}
}

private extension String {
func trimingTrailing(while characterSet: CharacterSet) -> String {
guard
let index = lastIndex(where: { !CharacterSet(charactersIn: String($0)).isSubset(of: characterSet) })
else { return self }

return String(self[...index])
}
}
4 changes: 2 additions & 2 deletions Domain/Sources/Models/MessageAttachment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public struct AttachmentJsonData: AttachmentData {

public struct AttachmentDescriptor {
public let id: String
public let mediaType: [String]?
public let mediaType: String?
public let data: AttachmentData
public let filename: [String]?
public let lastmodTime: Date?
Expand All @@ -69,7 +69,7 @@ public struct AttachmentDescriptor {

public init(
id: String,
mediaType: [String]? = nil,
mediaType: String? = nil,
data: AttachmentData,
filename: [String]? = nil,
lastmodTime: Date? = nil,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>PrismPluto.xcdatamodel</string>
</dict>
</plist>
3 changes: 0 additions & 3 deletions PrismAgent/Sources/Error.swift

This file was deleted.

4 changes: 4 additions & 0 deletions PrismAgent/Sources/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ public enum PrismAgentError: Error {
case unknownPrismOnboardingTypeError
case failedToOnboardError
case invalidPickupDeliveryMessageError
case invalidOfferCredentialMessageError
case invalidProposedCredentialMessageError
case invalidIssueCredentialMessageError
case invalidRequestCredentialMessageError
}
18 changes: 18 additions & 0 deletions PrismAgent/Sources/Helpers/AttachmentDescriptor+Builder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Core
import Domain
import Foundation

extension AttachmentDescriptor {
static func build<T: Encodable>(
id: String = UUID().uuidString,
payload: T,
mediaType: String? = "application/json"
) throws -> AttachmentDescriptor {
let encoded = try JSONEncoder().encode(payload).base64UrlEncodedString()
return AttachmentDescriptor(
id: id,
mediaType: mediaType,
data: AttachmentBase64(base64: encoded)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Domain
import Foundation

// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2#preview-credential
public struct CredentialPreview: Codable, Equatable {
public struct Attribute: Codable, Equatable {
public let name: String
public let value: String
public let mimeType: String?
}

public let type: String
public let attributes: [Attribute]

public init(attributes: [Attribute]) {
self.type = ProtocolTypes.didcommCredentialPreview.rawValue
self.attributes = attributes
}
}

struct CredentialFormat: Codable, Equatable {
// know Format:
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2#propose-attachment-registry
// - dif/[email protected]
// - aries/[email protected]
// - hlindy/[email protected]
//
let attachId: String
let format: String
}
134 changes: 134 additions & 0 deletions PrismAgent/Sources/Protocols/IssueCredential/IssueCredential.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import Domain
import Foundation

// ALL parameters are DIDCOMMV2 format and naming conventions and follows the protocol
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2
public struct IssueCredential {
struct Body: Codable, Equatable {
let goalCode: String?
let comment: String?
let replacementId: String?
let moreAvailable: String?
let formats: [CredentialFormat]

init(
goalCode: String? = nil,
comment: String? = nil,
replacementId: String? = nil,
moreAvailable: String? = nil,
formats: [CredentialFormat]
) {
self.goalCode = goalCode
self.comment = comment
self.replacementId = replacementId
self.moreAvailable = moreAvailable
self.formats = formats
}
}

public let id: String
public let type = ProtocolTypes.didcommIssueCredential.rawValue
let body: Body
let attachments: [AttachmentDescriptor]
public let thid: String?
public let from: DID
// swiftlint:disable identifier_name
public let to: DID
// swiftlint:enable identifier_name

init(
id: String = UUID().uuidString,
body: Body,
attachments: [AttachmentDescriptor],
thid: String?,
from: DID,
// swiftlint:disable identifier_name
to: DID
// swiftlint:enable identifier_name
) {
self.id = id
self.body = body
self.attachments = attachments
self.thid = thid
self.from = from
self.to = to
}

public init(fromMessage: Message) throws {
guard
fromMessage.piuri == ProtocolTypes.didcommIssueCredential.rawValue,
let fromDID = fromMessage.from,
let toDID = fromMessage.to
else { throw PrismAgentError.invalidIssueCredentialMessageError }

let body = try JSONDecoder().decode(Body.self, from: fromMessage.body)
self.init(
id: fromMessage.id,
body: body,
attachments: fromMessage.attachments,
thid: fromMessage.thid,
from: fromDID,
to: toDID
)
}

public func makeMessage() throws -> Message {
.init(
id: id,
piuri: type,
from: from,
to: to,
body: try JSONEncoder().encode(body),
attachments: attachments,
thid: thid
)
}

public static func makeIssueFromRequestCredential(msg: Message) throws -> IssueCredential {
let request = try RequestCredential(fromMessage: msg)

return IssueCredential(
body: Body(
goalCode: request.body.goalCode,
comment: request.body.comment,
formats: request.body.formats
),
attachments: request.attachments,
thid: msg.id,
from: request.to,
to: request.from)
}

static func build<T: Encodable>(
fromDID: DID,
toDID: DID,
thid: String?,
credentialPreview: CredentialPreview,
credentials: [String: T] = [:]
) throws -> IssueCredential {
let aux = try credentials.map { key, value in
let attachment = try AttachmentDescriptor.build(payload: value)
let format = CredentialFormat(attachId: attachment.id, format: key)
return (format, attachment)
}
return IssueCredential(
body: Body(
formats: aux.map { $0.0 }
),
attachments: aux.map { $0.1 },
thid: thid,
from: fromDID,
to: toDID
)
}
}

extension IssueCredential: Equatable {
public static func == (lhs: IssueCredential, rhs: IssueCredential) -> Bool {
lhs.id == rhs.id &&
lhs.type == rhs.type &&
lhs.from == rhs.from &&
lhs.to == rhs.to &&
lhs.body == rhs.body
}
}
Loading

0 comments on commit 5f48513

Please sign in to comment.