Skip to content
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
11 changes: 11 additions & 0 deletions Core/Sources/Helpers/String+URL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

public extension String {
func parseIfUrl() throws -> String {
guard self.contains("data:application/"), let url = URL(string: self) else {
return self
}
let data = try Data(contentsOf: url)
return String(data: data, encoding: .utf8) ?? self
}
}
4 changes: 2 additions & 2 deletions E2E/Tests/Source/Resolvers/PrismShortFormResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class PrismShortFormResolver: DIDResolverDomain {
}
return DIDDocument.Service(
id: service.id,
type: type,
serviceEndpoint: try convertServiceEndpoints(from: service.serviceEndpoint)
type: .many(type),
serviceEndpoint: .many(try convertServiceEndpoints(from: service.serviceEndpoint))
)
} ?? []
)
Expand Down
9 changes: 7 additions & 2 deletions E2E/Tests/Source/Workflows/EdgeAgentWorkflow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class EdgeAgentWorkflow {
) { sdk in
let message = sdk.issueCredentialStack.removeFirst()
let issuedCredential = try IssueCredential3_0(fromMessage: message)
_ = try await sdk.didcommAgent.processIssuedCredentialMessage(message: issuedCredential)
let credential = try await sdk.didcommAgent.processIssuedCredentialMessage(message: issuedCredential)
try await edgeAgent.remember(key: recordId, value: message.id)
}
}
Expand All @@ -164,11 +164,16 @@ class EdgeAgentWorkflow {
) { sdk in
let credentials = sdk.didcommAgent.edgeAgent.verifiableCredentials()
let credential = try await credentials.map { $0.first }.first().await()

guard let credential else {
throw ValidationError.error(message: "No credential available to present")
}

let message = sdk.proofOfRequestStack.removeFirst()
let requestPresentationMessage = try RequestPresentation(fromMessage: message)
let sendProofMessage = try await sdk.didcommAgent.createPresentationForRequestProof(
request: requestPresentationMessage,
credential: credential!,
credential: credential,
options: [.disclosingClaims(claims: ["automation-required"])]
).makeMessage()
_ = try await sdk.didcommAgent.sendMessage(message: sendProofMessage)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Domain
import Foundation

extension JWTCredential: ExportableCredential {
public var exporting: Data {
(try? jwtString.tryToData()) ?? Data()
}

public var restorationType: String { "jwt" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ extension JWTCredential: StorableCredential {
}

public var recoveryId: String {
"jwt+vc"
"jwt+credential"
}

public var credentialData: Data {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct EnvelopedVerfiablePresentation: RawCodable {
enum CodingKeys: String, CodingKey {
case context = "@context"
case type
case verifiableCredential
case id
}

/// The JSON‑LD `@context` values for the enveloped presentation entry (one‑or‑many).
Expand Down Expand Up @@ -62,7 +62,7 @@ public struct EnvelopedVerfiablePresentation: RawCodable {
public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.context = try container.decodeIfPresent(OneOrMany<String>.self, forKey: .context) ?? .many([])
self.id = try container.decode(String.self, forKey: .context)
self.id = try container.decode(String.self, forKey: .id)
self.type = try container.decodeIfPresent(OneOrMany<String>.self, forKey: .type) ?? .many([])
self.raw = try AnyCodable(from: decoder)
}
Expand All @@ -71,7 +71,7 @@ public struct EnvelopedVerfiablePresentation: RawCodable {
guard let raw else {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(context, forKey: .context)
try container.encode(id, forKey: .context)
try container.encode(id, forKey: .id)
try container.encode(type, forKey: .type)
return
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Core
import Domain
import Foundation

public struct JWTEnvelopedVerifiableCredential<Credential: Codable>: Codable {
public struct JWTEnvelopedVerifiableCredential<Credential: Codable>: RawCodable {
public let iss: String?
public let sub: String?
public let nbf: Date?
Expand All @@ -10,6 +11,7 @@ public struct JWTEnvelopedVerifiableCredential<Credential: Codable>: Codable {
public let jti: String?
public let aud: [String]?
public let vc: Credential
public let raw: AnyCodable?

init(
iss: String? = nil,
Expand All @@ -19,7 +21,8 @@ public struct JWTEnvelopedVerifiableCredential<Credential: Codable>: Codable {
iat: Date? = nil,
jti: String? = nil,
aud: [String]? = nil,
vc: Credential
vc: Credential,
raw: AnyCodable? = nil
) {
self.iss = iss
self.sub = sub
Expand All @@ -29,6 +32,7 @@ public struct JWTEnvelopedVerifiableCredential<Credential: Codable>: Codable {
self.jti = jti
self.aud = aud
self.vc = vc
self.raw = raw
}

enum CodingKeys: CodingKey {
Expand Down Expand Up @@ -63,5 +67,22 @@ public struct JWTEnvelopedVerifiableCredential<Credential: Codable>: Codable {
} else {
self.vc = try Credential(from: decoder)
}
self.raw = try AnyCodable(from: decoder)
}

public func encode(to encoder: any Encoder) throws {
guard let raw else {
var container = encoder.container(keyedBy: JWTEnvelopedVerifiableCredential<Credential>.CodingKeys.self)
try container.encodeIfPresent(self.iss, forKey: .iss)
try container.encodeIfPresent(self.sub, forKey: .sub)
try container.encodeIfPresent(self.nbf, forKey: .nbf)
try container.encodeIfPresent(self.exp, forKey: .exp)
try container.encodeIfPresent(self.iat, forKey: .iat)
try container.encodeIfPresent(self.jti, forKey: .jti)
try container.encodeIfPresent(self.aud, forKey: .aud)
try container.encode(self.vc, forKey: .vc)
return
}
try raw.encode(to: encoder)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ struct JWTCreatePresentation {
throw PolluxError.credentialIsNotOfPresentationDefinitionRequiredAlgorithm
}

let credentialSubject = try JSONEncoder().encode(credential.defaultEnvelop.vc)
let credentialSubject = try JSONEncoder().encode(credential.defaultEnvelop)

try presentationRequest.presentationDefinition.inputDescriptors.forEach {
try $0.constraints.fields.forEach {
Expand All @@ -92,7 +92,7 @@ struct JWTCreatePresentation {
format: "jwt",
pathNested: .init(
id: $0.id,
path: "$.vp.verifiableCredential[0]",
path: "$.vp.verifiableCredential[0].id",
format: "jwt"
)
)
Expand Down Expand Up @@ -126,7 +126,7 @@ struct JWTCreatePresentation {
credential: JWTCredential,
request: Data,
did: DID
) throws -> JWTEnvelopedVerifiablePresentation<VerifiablePresentation<EnvelopedVerfiablePresentation>> {
) throws -> JWTEnvelopedVerifiablePresentation<VerifiablePresentation<OneOrMany<EnvelopedVerfiablePresentation>>> {
let jsonObject = try JSONSerialization.jsonObject(with: request)
guard
let domain = findValue(forKey: "domain", in: jsonObject),
Expand All @@ -140,17 +140,17 @@ struct JWTCreatePresentation {
vp: VerifiablePresentation(
context: .one(W3CRegisteredConstants.verifiableCredential2_0Context),
type: .one(W3CRegisteredConstants.verifiablePresentationType),
verifiableCredential: EnvelopedVerfiablePresentation(
verifiableCredential: .many([EnvelopedVerfiablePresentation(
context: .one(W3CRegisteredConstants.verifiableCredential2_0Context),
id: "data:application/vc+jwt,\(credential.jwtString)",
type: .one(W3CRegisteredConstants.envelopedVerifiableCredentialType)
)
)])
)
)
}

private func vcPresentationJWTString(
payload: JWTEnvelopedVerifiablePresentation<VerifiablePresentation<EnvelopedVerfiablePresentation>>,
private func vcPresentationJWTString<Payload: Codable>(
payload: Payload,
exportableKey: ExportableKey
) throws -> String {
let keyJWK = exportableKey.jwk
Expand Down
2 changes: 2 additions & 0 deletions EdgeAgentSDK/Pollux/Sources/Operation/JWT/VerifyJWT.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Core
import Domain
import Foundation
import JSONWebAlgorithms
Expand All @@ -7,6 +8,7 @@ struct VerifyJWT {
let castor: Castor

func verifyJWT(jwtString: String) async throws -> Bool {
let jwtString = try jwtString.parseIfUrl()
try await verifyJWTCredentialRevocation(jwtString: jwtString)
let payload: DefaultJWTClaimsImpl = try JWT.getPayload(jwtString: jwtString)
guard let issuer = payload.iss else {
Expand Down
8 changes: 6 additions & 2 deletions EdgeAgentSDK/Pollux/Sources/PolluxImpl+Public.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ extension PolluxImpl: Pollux {
switch restorationIdentifier {
case "sd-jwt+credential":
return try SDJWTCredential(sdjwtString: credentialData.tryToString())
case "jwt+credential":
return try JSONDecoder().decode(LegacyJWTCredential.self, from: credentialData)
case "jwt+credential", "jwt+vc":
do {
return try JWTCredential(jwtString: credentialData.tryToString())
} catch {
return try JSONDecoder().decode(LegacyJWTCredential.self, from: credentialData)
}
case "w3c+credential":
return try JSONDecoder().decode(LegacyW3CVerifiableCredential.self, from: credentialData)
case "anon+credential":
Expand Down