Skip to content

Commit

Permalink
[fix] sdjwt vc verifier, init sdjwt from json
Browse files Browse the repository at this point in the history
  • Loading branch information
dtsiflit committed Oct 1, 2024
1 parent b645eeb commit 75fcd47
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 57 deletions.
8 changes: 8 additions & 0 deletions Sources/Issuer/SDJWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ public struct SignedSDJWT {
self.kbJwt = try? JWS(jwsString: serializedKbJwt ?? "")
}

init?(json: JSON) throws {
let triple = try JwsJsonSupport.parseJWSJson(unverifiedSdJwt: json)
self.jwt = triple.jwt
self.disclosures = triple.disclosures
self.kbJwt = triple.kbJwt
}

private init?<KeyType>(sdJwt: SDJWT, issuersPrivateKey: KeyType) {
// Create a Signed SDJWT with no key binding
guard let signedJwt = try? SignedSDJWT.createSignedJWT(key: issuersPrivateKey, jwt: sdJwt.jwt) else {
Expand Down Expand Up @@ -191,6 +198,7 @@ public extension SignedSDJWT {
return try self.toSDJWT().recreateClaims()
}


func asJwsJsonObject(
option: JwsJsonSupportOption = .flattened,
kbJwt: JWTString?,
Expand Down
8 changes: 5 additions & 3 deletions Sources/Parser/CompactParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ public class CompactParser: ParserProtocol {
}

// Ensure that all components are properly assigned
guard let unwrappedHeader = header,
let unwrappedPayload = payload,
let unwrappedSignature = signature else {
guard
let unwrappedHeader = header,
let unwrappedPayload = payload,
let unwrappedSignature = signature
else {
throw SDJWTVerifierError.parsingError
}

Expand Down
16 changes: 16 additions & 0 deletions Sources/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ public enum SDJWTError: Error {
case macAsAlgorithm
}

public enum SDJWTVerifierError: Error {
case parsingError
case invalidJwt
case invalidIssuer
case keyBindingFailed(description: String)
case invalidDisclosure(disclosures: [Disclosure])
case missingOrUnknownHashingAlgorithm
case nonUniqueDisclosures
case nonUniqueDisclosureDigests
case missingDigests(disclosures: [Disclosure])
case noAlgorithmProvided
case failedToCreateVerifier
case expiredJwt
case notValidYetJwt
}

/// Static Keys Used by the JWT
enum Keys: String {
case sd = "_sd"
Expand Down
100 changes: 83 additions & 17 deletions Sources/Utilities/JwsJsonSupportOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
*/
import Foundation
import SwiftyJSON
import JSONWebSignature

fileprivate let JWS_JSON_HEADER = "header"
fileprivate let JWS_JSON_DISCLOSURES = "disclosures"
fileprivate let JWS_JSON_KB_JWT = "kb_jwt"
fileprivate let JWS_JSON_PROTECTED = "protected"
fileprivate let JWS_JSON_SIGNATURE = "signature"
fileprivate let JWS_JSON_SIGNATURES = "signatures"
fileprivate let JWS_JSON_PAYLOAD = "payload"

public enum JwsJsonSupportOption {

case general, flattened

private static let JWS_JSON_HEADER = "header"
private static let JWS_JSON_DISCLOSURES = "disclosures"
private static let JWS_JSON_KB_JWT = "kb_jwt"
private static let JWS_JSON_PROTECTED = "protected"
private static let JWS_JSON_SIGNATURE = "signature"
private static let JWS_JSON_SIGNATURES = "signatures"
private static let JWS_JSON_PAYLOAD = "payload"
}

internal extension JwsJsonSupportOption {
Expand All @@ -40,38 +40,104 @@ internal extension JwsJsonSupportOption {
) -> JSON {
let headersAndSignature = JSONObject {
[
Self.JWS_JSON_HEADER: JSONObject {
JWS_JSON_HEADER: JSONObject {
[
Self.JWS_JSON_DISCLOSURES: JSONArray {
JWS_JSON_DISCLOSURES: JSONArray {
disclosures.map { JSON($0) }
},
Self.JWS_JSON_KB_JWT: kbJwt == nil ? nil : JSON(kbJwt!)
JWS_JSON_KB_JWT: kbJwt == nil ? nil : JSON(kbJwt!)
]
},
Self.JWS_JSON_PROTECTED: JSON(protected),
Self.JWS_JSON_SIGNATURE: JSON(signature)
JWS_JSON_PROTECTED: JSON(protected),
JWS_JSON_SIGNATURE: JSON(signature)
]
}

switch self {
case .general:
return JSONObject {
[
Self.JWS_JSON_PAYLOAD: JSON(payload),
Self.JWS_JSON_SIGNATURES: JSONArray {
JWS_JSON_PAYLOAD: JSON(payload),
JWS_JSON_SIGNATURES: JSONArray {
[headersAndSignature]
}
]
}
case .flattened:
return JSONObject {
[
Self.JWS_JSON_PAYLOAD: JSON(payload),
JWS_JSON_PAYLOAD: JSON(payload),
]
headersAndSignature
}
}
}
}

internal class JwsJsonSupport {

static func parseJWSJson(unverifiedSdJwt: JSON) throws -> (jwt: JWS, disclosures: [String], kbJwt: JWS?) {

let signatureContainer: JSON = unverifiedSdJwt[JWS_JSON_SIGNATURES]
.array?
.first ?? unverifiedSdJwt

let unverifiedJwt = try createUnverifiedJwt(
signatureContainer: signatureContainer,
unverifiedSdJwt: unverifiedSdJwt
)

let unprotectedHeader = extractUnprotectedHeader(from: signatureContainer)

return try extractUnverifiedValues(
unprotectedHeader: unprotectedHeader,
unverifiedJwt: unverifiedJwt
)
}

static private func createUnverifiedJwt(signatureContainer: JSON, unverifiedSdJwt: JSON) throws -> String {
guard let protected = signatureContainer[JWS_JSON_PROTECTED].string else {
throw SDJWTVerifierError.invalidJwt
}

guard let signature = signatureContainer[JWS_JSON_SIGNATURE].string else {
throw SDJWTVerifierError.invalidJwt
}

guard let payload = unverifiedSdJwt[JWS_JSON_PAYLOAD].string else {
throw SDJWTVerifierError.invalidJwt
}

return "\(protected).\(payload).\(signature)"
}

static private func extractUnprotectedHeader(from signatureContainer: JSON) -> JSON? {
if let jsonObject = signatureContainer[JWS_JSON_HEADER].dictionary {
return JSON(jsonObject)
}
return nil
}

static func extractUnverifiedValues(unprotectedHeader: JSON?, unverifiedJwt: String) throws -> (JWS, [String], JWS?) {

let unverifiedDisclosures: [String] = unprotectedHeader?[JWS_JSON_DISCLOSURES]
.array?
.compactMap { element in
return element.string
} ?? []

let jws: JWS? = if let unverifiedKBJwt = unprotectedHeader?[JWS_JSON_KB_JWT].string {
try JWS(jwsString: unverifiedKBJwt)
} else {
nil
}

return (
try JWS(jwsString: unverifiedJwt),
unverifiedDisclosures,
jws
)
}
}


Loading

0 comments on commit 75fcd47

Please sign in to comment.