Skip to content

Commit

Permalink
[fix] holder presentation and kbjwt
Browse files Browse the repository at this point in the history
  • Loading branch information
dtsiflit committed Oct 14, 2024
1 parent 4c57907 commit 8daa421
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 73 deletions.
29 changes: 19 additions & 10 deletions Sources/Claim/ClaimsExtractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@
*/
import SwiftyJSON

public typealias ClaimExtractorResult = (digestsFoundOnPayload: [DigestType], recreatedClaims: JSON)
public typealias ClaimExtractorResult = (
digestsFoundOnPayload: [DigestType],
recreatedClaims: JSON,
disclosuresPerClaim: DisclosuresPerClaim?
)

public class ClaimExtractor {

// MARK: - Properties

var digestsOfDisclosuresDict: [DisclosureDigest: Disclosure]
var digestsOfDisclosures: [DisclosureDigest: Disclosure]

// MARK: - Lifecycle

public init(digestsOfDisclosuresDict: [DisclosureDigest: Disclosure]) {
self.digestsOfDisclosuresDict = digestsOfDisclosuresDict
self.digestsOfDisclosures = digestsOfDisclosuresDict
}

// MARK: - Methods
Expand All @@ -46,9 +50,9 @@ public class ClaimExtractor {
var sdArray = sdArray.compactMap(\.string)
// try to find matching digests in order to be replaced with the value
while true {
let (updatedSdArray, foundDigest) = sdArray.findAndRemoveFirst(from: digestsOfDisclosuresDict.compactMap({$0.key}))
let (updatedSdArray, foundDigest) = sdArray.findAndRemoveFirst(from: digestsOfDisclosures.compactMap({$0.key}))
if let foundDigest,
let foundDisclosure = digestsOfDisclosuresDict[foundDigest]?.base64URLDecode()?.objectProperty {
let foundDisclosure = digestsOfDisclosures[foundDigest]?.base64URLDecode()?.objectProperty {
json[Keys.sd.rawValue].arrayObject = updatedSdArray

guard !json[foundDisclosure.key].exists() else {
Expand All @@ -57,10 +61,15 @@ public class ClaimExtractor {

json[foundDisclosure.key] = foundDisclosure.value

if let d = digestsOfDisclosuresDict[foundDigest] {
if let disclosure = digestsOfDisclosures[foundDigest] {
let currentJsonPointer = "/" + (currentPath + [foundDisclosure.key]).joined(separator: "/")
// visitor?.call(key: foundDisclosure.key, disclosure: foundDisclosure.value.stringValue + " " + foundDigest + " " + d + " " + currentJsonPointer)
visitor?.call(key: currentJsonPointer, disclosure: d)
visitor?.call(
pointer: .init(
pointer: currentJsonPointer
),
disclosure: disclosure
)
}
foundDigests.append(.object(foundDigest))

Expand Down Expand Up @@ -89,7 +98,7 @@ public class ClaimExtractor {
for (index, object) in subJson.arrayValue.enumerated() {
let newPath = currentPath + [key, "\(index)"] // Update the path for array elements
if object[Keys.dots.rawValue].exists() {
if let foundDisclosedArrayElement = digestsOfDisclosuresDict[object[Keys.dots].stringValue]?
if let foundDisclosedArrayElement = digestsOfDisclosures[object[Keys.dots].stringValue]?
.base64URLDecode()?
.arrayProperty {

Expand All @@ -105,7 +114,7 @@ public class ClaimExtractor {
currentPath: newPath // Pass the updated path for the nested JSON

),
!ifHasNested.digestsFoundOnPayload.isEmpty {
!ifHasNested.digestsFoundOnPayload.isEmpty {
foundDigests += ifHasNested.digestsFoundOnPayload
json[key].arrayObject?[index] = ifHasNested.recreatedClaims
}
Expand All @@ -114,6 +123,6 @@ public class ClaimExtractor {
}
}
}
return (foundDigests, json)
return (foundDigests, json, visitor?.disclosuresPerClaim)
}
}
3 changes: 1 addition & 2 deletions Sources/Issuer/SDJWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public struct SDJWT {
}
}

func recreateClaims() throws -> ClaimExtractorResult {
func recreateClaims(visitor: Visitor? = nil) throws -> ClaimExtractorResult {
let digestCreator = try extractDigestCreator()
var digestsOfDisclosuresDict = [DisclosureDigest: Disclosure]()
for disclosure in self.disclosures {
Expand All @@ -66,7 +66,6 @@ public struct SDJWT {
}
}

let visitor = Visitor()
return try ClaimExtractor(
digestsOfDisclosuresDict: digestsOfDisclosuresDict
).findDigests(
Expand Down
54 changes: 24 additions & 30 deletions Sources/Issuer/SignedSDJWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import SwiftyJSON
import JSONWebSignature
import JSONWebKey

typealias DisclosuresPerClaim = Dictionary<JSONPointer, [Disclosure]>
public typealias DisclosuresPerClaim = Dictionary<JSONPointer, [Disclosure]>

public struct SignedSDJWT {

Expand Down Expand Up @@ -157,8 +157,11 @@ public extension SignedSDJWT {
serialiser(self).serialised
}

func recreateClaims() throws -> ClaimExtractorResult {
return try self.toSDJWT().recreateClaims()
func recreateClaims(visitor: Visitor? = nil) throws -> ClaimExtractorResult {
return try self.toSDJWT()
.recreateClaims(
visitor: visitor
)
}

func asJwsJsonObject(
Expand All @@ -176,18 +179,25 @@ public extension SignedSDJWT {
)
}

func present(query: Set<JSONPointer>) async throws -> SignedSDJWT? {
func present(
query: Set<JSONPointer>,
visitor: Visitor? = Visitor()
) async throws -> SignedSDJWT? {
return try await present(
query: { jsonPointer in
return query.contains(jsonPointer)
}
},
visitor: visitor
)
}

func present(
query: (JSONPointer) -> Bool
private func present(
query: (JSONPointer) -> Bool,
visitor: Visitor?
) async throws -> SignedSDJWT? {
let (_, disclosuresPerClaim) = try recreateClaimsAndDisclosuresPerClaim()
let (_, disclosuresPerClaim) = try recreateClaimsAndDisclosuresPerClaim(
visitor: visitor
)
let keys = disclosuresPerClaim.keys.filter(query)
if keys.isEmpty {
return nil
Expand All @@ -209,30 +219,14 @@ public extension SignedSDJWT {
}

private extension SignedSDJWT {
func recreateClaimsAndDisclosuresPerClaim() throws -> (JSON, DisclosuresPerClaim) {
func recreateClaimsAndDisclosuresPerClaim(visitor: Visitor?) throws -> (JSON, DisclosuresPerClaim) {

let claims = try recreateClaims()
let claims = try recreateClaims(visitor: visitor)
print(claims)

return (JSON.empty, [:])
}
}

public protocol ClaimVisitor {
func call(pointer: JSONPointer, disclosure: Disclosure?)
func call(key: String, disclosure: Disclosure?)
}

public class Visitor: ClaimVisitor {

public init() {
}

public func call(pointer: JSONPointer, disclosure: Disclosure?) {
print("Visitor")
}

public func call(key: String, disclosure: Disclosure?) {
print("Visitor: \(key) \(disclosure ?? "N/A")")
return (
claims.recreatedClaims,
claims.disclosuresPerClaim ?? [:]
)
}
}
5 changes: 4 additions & 1 deletion Sources/Utilities/ClaimVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ public final class Visitor: ClaimVisitor {
public init() {
}

public func call(pointer: JSONPointer, disclosure: Disclosure) {
public func call(
pointer: JSONPointer,
disclosure: Disclosure
) {
// Ensure that the path (pointer) does not already exist in disclosuresPerClaim
guard disclosuresPerClaim[pointer] == nil else {
fatalError("Disclosures for \(pointer.pointer) have already been calculated.")
Expand Down
4 changes: 4 additions & 0 deletions Sources/Verifier/KeyBindingVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class KeyBindingVerifier: VerifierProtocol {

try verifyIat(iatOffset: iatOffset, iat: Date(timeIntervalSince1970: TimeInterval(timeInterval)))
try verifyAud(aud: aud, expectedAudience: expectedAudience)

try verify()
}

public func verify(
Expand All @@ -70,6 +72,8 @@ public class KeyBindingVerifier: VerifierProtocol {
}

self.signatureVerifier = try SignatureVerifier(signedJWT: challenge, publicKey: extractedKey)

try verify()
}

@discardableResult
Expand Down
2 changes: 1 addition & 1 deletion Tests/Helpers/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func validateObjectResults(factoryResult result: Result<ClaimSet, Error>, expect
}
XCTAssert(expectedDigests + numberOfDecoys <= expectedDigests + decoysLimit)
return (json, disclosures)
case .failure(let err):
case .failure:
XCTFail("Failed to Create SDJWT")
return(.empty, [])
}
Expand Down
6 changes: 2 additions & 4 deletions Tests/Issuance/BuilderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ final class BuilderTest: XCTestCase {
let unsignedJwt = factory.createSDJWTPayload(sdJwtObject: sdObject.asObject)

switch unsignedJwt {
case .success((let json, let disclosures)):
case .success:
XCTAssert(true)
case .failure(let err):
case .failure:
XCTFail("Failed to Create SDJWT")
}

Expand Down Expand Up @@ -112,8 +112,6 @@ final class BuilderTest: XCTestCase {
FlatDisclosedClaim("array", ["GR", "DE"])
}

let json = plainJWT.asJSON

@SDJWTBuilder
var objects: SdElement {
FlatDisclosedClaim("Flat Object", plainJWT.asJSON)
Expand Down
88 changes: 63 additions & 25 deletions Tests/Presentation/PresentationTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,13 @@ final class PresentationTest: XCTestCase {
try await super.tearDown()
}

func test() async throws {


let issuersKey = issuersKeyPair.public
let issuerJwk = try issuersKey.jwk
func testSDJWTPresentationWithSelectiveDisclosures() async throws {

// Given
let visitor = Visitor()
let holdersKey = holdersKeyPair.public
let holdersJwk = try holdersKey.jwk

let jsonObject: JSON = [
"issuer": "https://example.com/issuer",
"jwks": [
"keys": [
[
"crv": "P-256",
"kid": "Ao50Swzv_uWu805LcuaTTysu_6GwoqnvJh9rnc44U48",
"kty": "EC",
"x": issuerJwk.x?.base64URLEncode(),
"y": issuerJwk.y?.base64URLEncode()
]
]
]
]
var verifier: KeyBindingVerifier = KeyBindingVerifier()

let issuerSignedSDJWT = try SDJWTIssuer.issue(
issuersPrivateKey: issuersKeyPair.private,
Expand Down Expand Up @@ -87,22 +71,76 @@ final class PresentationTest: XCTestCase {
PlainClaim("crv", "P-256")
}
}
RecursiveObject("test_recursive") {
FlatDisclosedClaim("recursive_address", "東京都港区芝公園4丁目2−8")
}
}

// When
let query: Set<JSONPointer> = Set(
["/address/region", "/address/country"]
["/address/region", "/address/country", "/dimitri_recursive/recursive_address"]
.compactMap {
JSONPointer(pointer: $0)
}
)


let presentedSdJwt = try await issuerSignedSDJWT.present(
query: query
query: query,
visitor: visitor
)

// po CompactSerialiser(signedSDJWT: presentedSdJwt!).serialised
// print(presentedSdJwt)
guard let presentedSdJwt = presentedSdJwt else {
XCTFail("Expected presentedSdJwt value to be non-nil but it was nil")
return
}

let sdHash = DigestCreator()
.hashAndBase64Encode(
input: CompactSerialiser(
signedSDJWT: presentedSdJwt
).serialised
)!

var holderPresentation: SignedSDJWT?
XCTAssertNoThrow(
holderPresentation = try SDJWTIssuer
.presentation(
holdersPrivateKey: holdersKeyPair.private,
signedSDJWT: issuerSignedSDJWT,
disclosuresToPresent: presentedSdJwt.disclosures,
keyBindingJWT: KBJWT(
header: DefaultJWSHeaderImpl(algorithm: .ES256),
kbJwtPayload: .init([
Keys.nonce.rawValue: "123456789",
Keys.aud.rawValue: "example.com",
Keys.iat.rawValue: 1694600000,
Keys.sdHash.rawValue: sdHash
])
)
)
)

let kbJwt = holderPresentation?.kbJwt

// Then
XCTAssertNoThrow(
try verifier.verify(
iatOffset: .init(
startTime: Date(timeIntervalSince1970: 1694600000 - 1000),
endTime: Date(timeIntervalSince1970: 1694600000)
)!,
expectedAudience: "example.com",
challenge: kbJwt!,
extractedKey: holdersJwk
)
)

XCTAssertNotNil(kbJwt)
XCTAssertEqual(presentedSdJwt.disclosures.count, 4)

let presentedDisclosures = Set(presentedSdJwt.disclosures)
let visitedDisclosures = Set(visitor.disclosures)
XCTAssertTrue(presentedDisclosures.isSubset(of: visitedDisclosures))
}
}

0 comments on commit 8daa421

Please sign in to comment.