diff --git a/Package.resolved b/Package.resolved index ac1615e..7ddd407 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,12 +1,30 @@ { "pins" : [ { - "identity" : "joseswift", + "identity" : "cryptoswift", "kind" : "remoteSourceControl", - "location" : "https://github.com/niscy-eudiw/JOSESwift.git", + "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", "state" : { - "revision" : "518cedba79ef18867191811b161471298b6cb7c8", - "version" : "2.4.1-gcm" + "revision" : "c9c3df6ab812de32bae61fc0cd1bf6d45170ebf0", + "version" : "1.8.2" + } + }, + { + "identity" : "jose-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/beatt83/jose-swift.git", + "state" : { + "revision" : "682002aae8d841d47bd9f641b8b85a233460d140", + "version" : "3.1.0" + } + }, + { + "identity" : "secp256k1.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/GigaBitcoin/secp256k1.swift.git", + "state" : { + "revision" : "4c77c7384768acf1093d66ccaacf298d322b10b7", + "version" : "0.15.0" } }, { @@ -14,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SwiftyJSON/SwiftyJSON.git", "state" : { - "revision" : "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07", - "version" : "5.0.1" + "revision" : "af76cf3ef710b6ca5f8c05f3a31307d44a3c5828", + "version" : "5.0.2" } } ], diff --git a/Package.swift b/Package.swift index cc726b5..4955728 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 5.8.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -23,16 +23,16 @@ let package = Package( from: "5.0.1" ), .package( - url: "https://github.com/niscy-eudiw/JOSESwift.git", - exact: "2.4.1-gcm" - ), + url: "https://github.com/beatt83/jose-swift.git", + from: "3.1.0" + ) ], targets: [ .target( name: "eudi-lib-sdjwt-swift", dependencies: [ - .product(name: "SwiftyJSON", package: "swiftyjson"), - .product(name: "JOSESwift", package: "JOSESwift") + "jose-swift", + .product(name: "SwiftyJSON", package: "swiftyjson") ], path: "Sources", plugins: [ diff --git a/Sources/Factory/SDJWTFactory.swift b/Sources/Factory/SDJWTFactory.swift index 031db58..b2d5a14 100644 --- a/Sources/Factory/SDJWTFactory.swift +++ b/Sources/Factory/SDJWTFactory.swift @@ -15,7 +15,6 @@ */ import Foundation import SwiftyJSON -import JOSESwift typealias ClaimSet = (value: JSON, disclosures: [Disclosure]) diff --git a/Sources/Issuer/JWSController.swift b/Sources/Issuer/JWSController.swift deleted file mode 100644 index bc628ae..0000000 --- a/Sources/Issuer/JWSController.swift +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023 European Commission - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import JOSESwift -import CryptoKit -import Security - -class JWSController { - - // MARK: - Properties - - var signatureAlgorithm: SignatureAlgorithm - // SecKey Should be Data (HMAC) Or SecKey (RSA, EC) - let signer: Signer - - // MARK: - Lifecycle - - init(signingAlgorithm: SignatureAlgorithm, privateKey: SecKey) throws { - self.signatureAlgorithm = signingAlgorithm - guard let signer = Signer(signingAlgorithm: signingAlgorithm, key: privateKey) else { - throw JOSESwiftError.signingFailed(description: "Failed To Create Signing Algorith \(signingAlgorithm) with key \(privateKey)") - } - - self.signer = signer - } -} diff --git a/Sources/Issuer/JWT.swift b/Sources/Issuer/JWT.swift index b535c5c..fde0d30 100644 --- a/Sources/Issuer/JWT.swift +++ b/Sources/Issuer/JWT.swift @@ -14,24 +14,25 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebAlgorithms +import JSONWebSignature import SwiftyJSON public struct JWT: JWTRepresentable { // MARK: - Properties - var header: JWSHeader + var header: JWSRegisteredFieldsHeader var payload: JSON // MARK: - Lifecycle - public init(header: JWSHeader, payload: JSON) throws { + public init(header: JWSRegisteredFieldsHeader, payload: JSON) throws { guard header.algorithm?.rawValue != Keys.none.rawValue else { throw SDJWTError.noneAsAlgorithm } - guard SignatureAlgorithm.allCases.map({$0.rawValue}).contains(header.algorithm?.rawValue) else { + guard SigningAlgorithm.allCases.map({$0.rawValue}).contains(header.algorithm?.rawValue) else { throw SDJWTError.macAsAlgorithm } @@ -39,12 +40,12 @@ public struct JWT: JWTRepresentable { self.payload = payload } - public init(header: JWSHeader, kbJwtPayload: JSON) throws { + public init(header: JWSRegisteredFieldsHeader, kbJwtPayload: JSON) throws { guard header.algorithm?.rawValue != Keys.none.rawValue else { throw SDJWTError.noneAsAlgorithm } - guard SignatureAlgorithm.allCases.map({$0.rawValue}).contains(header.algorithm?.rawValue) else { + guard SigningAlgorithm.allCases.map({$0.rawValue}).contains(header.algorithm?.rawValue) else { throw SDJWTError.macAsAlgorithm } self.header = header @@ -54,13 +55,13 @@ public struct JWT: JWTRepresentable { // MARK: - Methods - func sign(signer: Signer) throws -> JWS { + func sign(key: KeyType) throws -> JWS { let unsignedJWT = try self.asUnsignedJWT() - return try JWS(header: unsignedJWT.header, payload: unsignedJWT.payload, signer: signer) + return try JWS.init(payload: unsignedJWT.payload, protectedHeader: unsignedJWT.header, key: key) } mutating func addKBTyp() { - self.header.typ = "kb+jwt" + self.header.type = "kb+jwt" } } diff --git a/Sources/Issuer/JWTRepresentable.swift b/Sources/Issuer/JWTRepresentable.swift index 779b4e7..003d621 100644 --- a/Sources/Issuer/JWTRepresentable.swift +++ b/Sources/Issuer/JWTRepresentable.swift @@ -14,26 +14,25 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebSignature import SwiftyJSON typealias Base64String = String -typealias UnsignedJWT = (header: JWSHeader, payload: Payload) +typealias UnsignedJWT = (header: JWSRegisteredFieldsHeader, payload: Data) protocol JWTRepresentable { - var header: JWSHeader { get } + var header: JWSRegisteredFieldsHeader { get } var payload: JSON { get } func asUnsignedJWT() throws -> UnsignedJWT - func sign(signer: Signer) throws -> JWS + func sign(key: KeyType) throws -> JWS - init(header: JWSHeader, payload: JSON) throws + init(header: JWSRegisteredFieldsHeader, payload: JSON) throws } extension JWTRepresentable { func asUnsignedJWT() throws -> UnsignedJWT { - let payload = Payload(try payload.rawData()) - return(header, payload) + return(header, try payload.rawData()) } } diff --git a/Sources/Issuer/SDJWT.swift b/Sources/Issuer/SDJWT.swift index c8c3a19..d866aba 100644 --- a/Sources/Issuer/SDJWT.swift +++ b/Sources/Issuer/SDJWT.swift @@ -14,8 +14,10 @@ * limitations under the License. */ import Foundation +import JSONWebKey +import JSONWebSignature +import JSONWebToken import SwiftyJSON -import JOSESwift public typealias KBJWT = JWT @@ -75,13 +77,13 @@ public struct SignedSDJWT { var delineatedCompactSerialisation: String { let separator = "~" - let input = ([jwt.compactSerializedString] + disclosures).reduce("") { $0.isEmpty ? $1 : $0 + separator + $1 } + separator + let input = ([jwt.compactSerialization] + disclosures).reduce("") { $0.isEmpty ? $1 : $0 + separator + $1 } + separator return DigestCreator() .hashAndBase64Encode( input: input ) ?? "" } - + // MARK: - Lifecycle init( @@ -89,16 +91,14 @@ public struct SignedSDJWT { disclosures: [Disclosure], serializedKbJwt: String? ) throws { - self.jwt = try JWS(compactSerialization: serializedJwt) + self.jwt = try JWS(jwsString: serializedJwt) self.disclosures = disclosures - self.kbJwt = try? JWS(compactSerialization: serializedKbJwt ?? "") + self.kbJwt = try? JWS(jwsString: serializedKbJwt ?? "") } private init?(sdJwt: SDJWT, issuersPrivateKey: KeyType) { // Create a Signed SDJWT with no key binding - guard let signingAlgorithm = sdJwt.jwt.header.algorithm, - let signedJwt = try? SignedSDJWT.createSignedJWT(jwsController: .init(signingAlgorithm: signingAlgorithm, privateKey: issuersPrivateKey), jwt: sdJwt.jwt) - else { + guard let signedJwt = try? SignedSDJWT.createSignedJWT(key: issuersPrivateKey, jwt: sdJwt.jwt) else { return nil } @@ -114,12 +114,7 @@ public struct SignedSDJWT { self.jwt = signedSDJWT.jwt self.disclosures = signedSDJWT.disclosures - - guard let signingAlgorithm = kbJWT.header.algorithm, - let signedKBJwt = try? SignedSDJWT.createSignedJWT(jwsController: .init(signingAlgorithm: signingAlgorithm, privateKey: holdersPrivateKey), jwt: kbJWT) - else { - return nil - } + let signedKBJwt = try? SignedSDJWT.createSignedJWT(key: holdersPrivateKey, jwt: kbJWT) self.kbJwt = signedKBJwt } @@ -140,8 +135,8 @@ public struct SignedSDJWT { }() } - private static func createSignedJWT(jwsController: JWSController, jwt: JWT) throws -> JWS { - try jwt.sign(signer: jwsController.signer) + private static func createSignedJWT(key: KeyType, jwt: JWT) throws -> JWS { + try jwt.sign(key: key) } func disclosuresToPresent(disclosures: [Disclosure]) -> Self { @@ -151,16 +146,16 @@ public struct SignedSDJWT { } func toSDJWT() throws -> SDJWT { - if let kbJwtHeader = kbJwt?.header, + if let kbJwtHeader = kbJwt?.protectedHeader, let kbJWtPayload = try? kbJwt?.payloadJSON() { return try SDJWT( - jwt: JWT(header: jwt.header, payload: jwt.payloadJSON()), + jwt: JWT(header: jwt.protectedHeader, payload: jwt.payloadJSON()), disclosures: disclosures, kbJWT: JWT(header: kbJwtHeader, kbJwtPayload: kbJWtPayload)) } return try SDJWT( - jwt: JWT(header: jwt.header, payload: jwt.payloadJSON()), + jwt: JWT(header: jwt.protectedHeader, payload: jwt.payloadJSON()), disclosures: disclosures, kbJWT: nil) } @@ -173,22 +168,11 @@ public struct SignedSDJWT { throw SDJWTVerifierError.keyBindingFailed(description: "Failled to find holders public key") } - guard let keyType = JWKKeyType(rawValue: jwk["kty"].stringValue) else { + guard let jwkObject = try? JSONDecoder.jwt.decode(JWK.self, from: jwk.rawData()) else { throw SDJWTVerifierError.keyBindingFailed(description: "failled to extract key type") } - switch keyType { - case .EC: - guard let crvType = ECCurveType(rawValue: jwk["crv"].stringValue) else { - throw SDJWTVerifierError.keyBindingFailed(description: "failled to extract curve type") - } - return ECPublicKey(crv: crvType, x: jwk["x"].stringValue, y: jwk["y"].stringValue) - case .RSA: - return RSAPublicKey(modulus: jwk["n"].stringValue, exponent: jwk["e"].stringValue) - case .OCT: - return try SymmetricKey(key: jwk["k"].rawData()) - } - + return jwkObject } } diff --git a/Sources/Issuer/SDJWTIssuer.swift b/Sources/Issuer/SDJWTIssuer.swift index 4ee5aa7..c6e7c1e 100644 --- a/Sources/Issuer/SDJWTIssuer.swift +++ b/Sources/Issuer/SDJWTIssuer.swift @@ -14,7 +14,7 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebSignature import SwiftyJSON public class SDJWTIssuer { @@ -22,7 +22,7 @@ public class SDJWTIssuer { /// Enum to represent the purpose of the JWT. enum Purpose { /// Used for JWT issuance. - case issuance(JWSHeader, ClaimSet) + case issuance(JWSRegisteredFieldsHeader, ClaimSet) /// Used for JWT presentation. case presentation(SignedSDJWT, [Disclosure], KBJWT?) } @@ -44,11 +44,12 @@ public class SDJWTIssuer { /// - Returns: The signed SDJWT. /// - Throws: An error if there's an issue with JWT creation or signing. /// - public static func issue(issuersPrivateKey: KeyType, - header: JWSHeader, - decoys: Int = 0, - @SDJWTBuilder buildSDJWT: () throws -> SdElement) throws -> SignedSDJWT { - + public static func issue( + issuersPrivateKey: KeyType, + header: JWSRegisteredFieldsHeader, + decoys: Int = 0, + @SDJWTBuilder buildSDJWT: () throws -> SdElement + ) throws -> SignedSDJWT { let factory = SDJWTFactory(decoysLimit: decoys) let claimSet = try factory.createSDJWTPayload(sdJwtObject: SDJWTBuilder.build(builder: buildSDJWT)).get() let signedSDJWT = try self.createSDJWT(purpose: .issuance(header, claimSet), signingKey: issuersPrivateKey) @@ -71,7 +72,7 @@ public class SDJWTIssuer { ) throws -> SignedSDJWT { try createSDJWT( purpose: .presentation( - signedSDJWT, + signedSDJWT, disclosuresToPresent, keyBindingJWT ), @@ -85,8 +86,10 @@ public class SDJWTIssuer { /// - signedSDJWT: The signed SDJWT to present. /// - disclosuresToPresent: The disclosures to include in the presentation. /// - public static func presentation(signedSDJWT: SignedSDJWT, - disclosuresToPresent: [Disclosure]) throws -> SignedSDJWT { + public static func presentation( + signedSDJWT: SignedSDJWT, + disclosuresToPresent: [Disclosure] + ) throws -> SignedSDJWT { return try createSDJWT(purpose: .presentation(signedSDJWT, disclosuresToPresent, nil), signingKey: Void.self) } @@ -102,7 +105,14 @@ public class SDJWTIssuer { static func createSDJWT(purpose: Purpose, signingKey: KeyType) throws -> SignedSDJWT { switch purpose { case .issuance(let JWSHeader, let claimSet): - let ungsingedSDJWT = try SDJWT(jwt: JWT(header: JWSHeader, payload: claimSet.value), disclosures: claimSet.disclosures, kbJWT: nil) + let ungsingedSDJWT = try SDJWT( + jwt: JWT( + header: JWSHeader, + payload: claimSet.value + ), + disclosures: claimSet.disclosures, + kbJWT: nil + ) return try createSignedSDJWT(sdJwt: ungsingedSDJWT, issuersPrivateKey: signingKey) // .......... case .presentation(let signedJWT, let selectedDisclosures, let KBJWT): @@ -137,8 +147,16 @@ public class SDJWTIssuer { /// - Returns: The key-bonded signed SDJWT. /// - Throws: An error if there's an issue with JWT signing or key binding. /// - private static func createKeyBondedSDJWT(signedSDJWT: SignedSDJWT, kbJWT: JWT, holdersPrivateKey: KeyType) throws -> SignedSDJWT { - try SignedSDJWT.keyBondedSDJWT(signedSDJWT: signedSDJWT, kbJWT: kbJWT, holdersPrivateKey: holdersPrivateKey) + private static func createKeyBondedSDJWT( + signedSDJWT: SignedSDJWT, + kbJWT: JWT, + holdersPrivateKey: KeyType + ) throws -> SignedSDJWT { + try SignedSDJWT.keyBondedSDJWT( + signedSDJWT: signedSDJWT, + kbJWT: kbJWT, + holdersPrivateKey: holdersPrivateKey + ) } } diff --git a/Sources/Parser/CompactParser.swift b/Sources/Parser/CompactParser.swift index 26ff847..274e2f1 100644 --- a/Sources/Parser/CompactParser.swift +++ b/Sources/Parser/CompactParser.swift @@ -14,7 +14,6 @@ * limitations under the License. */ import Foundation -import JOSESwift import SwiftyJSON public enum SerialisationFormat { diff --git a/Sources/Serializer/CompactSerialiser.swift b/Sources/Serializer/CompactSerialiser.swift index 5308893..52f5294 100644 --- a/Sources/Serializer/CompactSerialiser.swift +++ b/Sources/Serializer/CompactSerialiser.swift @@ -14,7 +14,6 @@ * limitations under the License. */ import Foundation -import JOSESwift public class CompactSerialiser: SerialiserProtocol { @@ -41,11 +40,11 @@ public class CompactSerialiser: SerialiserProtocol { public extension SerialisationFormat { func serialise(signedSDJWT: SignedSDJWT) -> String { var output = "" - output += signedSDJWT.jwt.compactSerializedString + output += signedSDJWT.jwt.compactSerialization output += signedSDJWT.disclosures.reduce(into: "~", { partialResult, disclosure in partialResult += disclosure + "~" }) - output += signedSDJWT.kbJwt?.compactSerializedString ?? "" + output += signedSDJWT.kbJwt?.compactSerialization ?? "" return output } } diff --git a/Sources/Serializer/EnvelopedSerialiser.swift b/Sources/Serializer/EnvelopedSerialiser.swift index 9fdc4bf..e5d5971 100644 --- a/Sources/Serializer/EnvelopedSerialiser.swift +++ b/Sources/Serializer/EnvelopedSerialiser.swift @@ -15,7 +15,6 @@ */ import Foundation import SwiftyJSON -import JOSESwift public class EnvelopedSerialiser: SerialiserProtocol { @@ -34,11 +33,11 @@ public class EnvelopedSerialiser: SerialiserProtocol { // MARK: - Lifecycle - public init(SDJWT: SignedSDJWT, jwTpayload: Payload, options opt: JSONSerialization.ReadingOptions = []) throws { + public init(SDJWT: SignedSDJWT, jwTpayload: Data, options opt: JSONSerialization.ReadingOptions = []) throws { var updatedSDJWT = SDJWT updatedSDJWT.kbJwt = nil - payload = try JSON(data: jwTpayload.data()) + payload = try JSON(data: jwTpayload) let compactSerialiser = CompactSerialiser(signedSDJWT: updatedSDJWT) payload[Keys.sdJwt].string = compactSerialiser.serialised } diff --git a/Sources/Utilities/Extensions/JWS+Extension.swift b/Sources/Utilities/Extensions/JWS+Extension.swift index adc9725..1c19dfb 100644 --- a/Sources/Utilities/Extensions/JWS+Extension.swift +++ b/Sources/Utilities/Extensions/JWS+Extension.swift @@ -14,12 +14,12 @@ * limitations under the License. */ -import JOSESwift +import JSONWebSignature import SwiftyJSON extension JWS { func payloadJSON() throws -> JSON { - try JSON(data: self.payload.data()) + try JSON(data: self.payload) } func iat() throws -> Int? { diff --git a/Sources/Utilities/Extensions/Payload+Extension.swift b/Sources/Utilities/Extensions/Payload+Extension.swift deleted file mode 100644 index de646a5..0000000 --- a/Sources/Utilities/Extensions/Payload+Extension.swift +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023 European Commission - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import JOSESwift -import Foundation - -extension Data { - var payload: Payload { - Payload(self) - } -} diff --git a/Sources/Utilities/Extensions/SignatureAlgorithm+Extension.swift b/Sources/Utilities/Extensions/SignatureAlgorithm+Extension.swift index ac35dd3..96ce1cf 100644 --- a/Sources/Utilities/Extensions/SignatureAlgorithm+Extension.swift +++ b/Sources/Utilities/Extensions/SignatureAlgorithm+Extension.swift @@ -14,14 +14,10 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebAlgorithms -extension SignatureAlgorithm: CaseIterable { - public static var allCases: [SignatureAlgorithm] { - if #available(iOS 11, *) { - return [.HS256, .HS384, .HS512, .RS256, .RS384, .RS512, .ES256, .ES384, .ES512, .PS256, .PS384, .PS512] - } else { - return [.HS256, .HS384, .HS512, .RS256, .RS384, .RS512, .ES256, .ES384, .ES512] - } +extension SigningAlgorithm: CaseIterable { + public static var allCases: [SigningAlgorithm] { + return [.HS256, .HS384, .HS512, .RS256, .RS384, .RS512, .ES256, .ES384, .ES512, .ES256K, .PS256, .PS384, .PS512, .EdDSA] } } diff --git a/Sources/Utilities/TimeRange.swift b/Sources/Utilities/TimeRange.swift index ce18d31..99bf047 100644 --- a/Sources/Utilities/TimeRange.swift +++ b/Sources/Utilities/TimeRange.swift @@ -18,7 +18,7 @@ import Foundation public struct TimeRange { let startTime: Date let endTime: Date - + public init?(startTime: Date, endTime: Date) { guard startTime < endTime else { return nil // Ensure that the start time is earlier than the end time @@ -26,7 +26,7 @@ public struct TimeRange { self.startTime = startTime self.endTime = endTime } - + func contains(date: Date) -> Bool { return date >= startTime && date <= endTime } diff --git a/Sources/Verifier/KeyBindingVerifier.swift b/Sources/Verifier/KeyBindingVerifier.swift index 68f42e2..8b21457 100644 --- a/Sources/Verifier/KeyBindingVerifier.swift +++ b/Sources/Verifier/KeyBindingVerifier.swift @@ -14,7 +14,8 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebKey +import JSONWebSignature import SwiftyJSON public class KeyBindingVerifier: VerifierProtocol { @@ -26,7 +27,7 @@ public class KeyBindingVerifier: VerifierProtocol { challenge: JWS, extractedKey: JWK) throws { - guard challenge.header.typ == "kb+jwt" else { + guard challenge.protectedHeader.type == "kb+jwt" else { throw SDJWTVerifierError.keyBindingFailed(description: "no kb+jwt as typ claim") } @@ -42,23 +43,7 @@ public class KeyBindingVerifier: VerifierProtocol { throw SDJWTVerifierError.keyBindingFailed(description: "No Nonce Provided") } - switch extractedKey.keyType { - case .EC: - guard let secKey = try? (extractedKey as? ECPublicKey)?.converted(to: SecKey.self) else { - throw SDJWTVerifierError.keyBindingFailed(description: "Key Type Missmatch") - } - self.signatureVerifier = try SignatureVerifier(signedJWT: challenge, publicKey: secKey) - case .RSA: - guard let secKey = try? (extractedKey as? RSAPublicKey)?.converted(to: SecKey.self) else { - throw SDJWTVerifierError.keyBindingFailed(description: "Key Type Missmatch") - } - self.signatureVerifier = try SignatureVerifier(signedJWT: challenge, publicKey: secKey) - case .OCT: - guard let secKey = try? (extractedKey as? SymmetricKey)?.converted(to: Data.self) else { - throw SDJWTVerifierError.keyBindingFailed(description: "Key Type Missmatch") - } - self.signatureVerifier = try SignatureVerifier(signedJWT: challenge, publicKey: secKey) - } + self.signatureVerifier = try SignatureVerifier(signedJWT: challenge, publicKey: extractedKey) try verifyIat(iatOffset: iatOffset, iat: Date(timeIntervalSince1970: TimeInterval(timeInterval))) try verifyAud(aud: aud, expectedAudience: expectedAudience) @@ -87,6 +72,6 @@ public class KeyBindingVerifier: VerifierProtocol { @discardableResult public func verify() throws -> JWS { - return try signatureVerifier.verify() + try signatureVerifier.verify() } } diff --git a/Sources/Verifier/SDJWTVerifier.swift b/Sources/Verifier/SDJWTVerifier.swift index 6b305d8..557c9dc 100644 --- a/Sources/Verifier/SDJWTVerifier.swift +++ b/Sources/Verifier/SDJWTVerifier.swift @@ -14,7 +14,8 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebKey +import JSONWebSignature public protocol VerifierProtocol { associatedtype ReturnType @@ -107,7 +108,7 @@ public class SDJWTVerifier { } let extractedKey = try sdjwt.extractHoldersPublicKey() try keyBindingVerifier(kbJwt, extractedKey).verify() - + if let sdHash = try? kbJwt.payloadJSON()["sd_hash"].stringValue { if sdHash != sdjwt.delineatedCompactSerialisation { throw SDJWTVerifierError.keyBindingFailed(description: "No KB provided") diff --git a/Sources/Verifier/SignatureVerifier.swift b/Sources/Verifier/SignatureVerifier.swift index fab359c..3909eff 100644 --- a/Sources/Verifier/SignatureVerifier.swift +++ b/Sources/Verifier/SignatureVerifier.swift @@ -14,7 +14,8 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebKey +import JSONWebSignature // To Constraint What can be passed as a key // JOSE Supports SecKey for RSA and EC and Data for HMAC @@ -22,33 +23,30 @@ public protocol KeyExpressible {} extension SecKey: KeyExpressible {} extension Data: KeyExpressible {} +extension JWK: KeyExpressible {} public class SignatureVerifier: VerifierProtocol { // MARK: - Properties - - let verifier: Verifier let jws: JWS + let key: KeyExpressible // MARK: - Lifecycle public init(signedJWT: JWS, publicKey: Key) throws { - guard let algorithm = signedJWT.header.algorithm else { + guard signedJWT.protectedHeader.algorithm != nil else { throw SDJWTVerifierError.noAlgorithmProvided } - - guard let verifier = Verifier(verifyingAlgorithm: algorithm, key: publicKey) else { - throw SDJWTVerifierError.failedToCreateVerifier - } - - self.verifier = verifier self.jws = signedJWT + self.key = publicKey } // MARK: - Methods @discardableResult public func verify() throws -> JWS { - let verifiedJws = try jws.validate(using: verifier) - return verifiedJws + guard try jws.verify(key: key) else { + throw SDJWTVerifierError.invalidJwt + } + return jws } } diff --git a/Tests/Issuance/DecoyTest.swift b/Tests/Issuance/DecoyTest.swift index 09862d8..052c0b0 100644 --- a/Tests/Issuance/DecoyTest.swift +++ b/Tests/Issuance/DecoyTest.swift @@ -46,10 +46,10 @@ final class DecoyTest: XCTestCase { } } } - + let asJSON = alrtSdObject.asJSON let asObject = alrtSdObject.asObject - + let jwtFactory = SDJWTFactory(decoysLimit: decoysLimit) let unsignedJwt = jwtFactory.createSDJWTPayload(sdJwtObject: sdObject.asObject) @@ -69,9 +69,9 @@ final class DecoyTest: XCTestCase { FlatDisclosedClaim("adress", "Al. Mich") } } - + let asJSON = sdObject.asObject - + let jwtFactory = SDJWTFactory() let payload = try! jwtFactory.createSDJWTPayload(sdJwtObject: sdObject.asObject).get() diff --git a/Tests/Issuance/IssuerTests.swift b/Tests/Issuance/IssuerTests.swift index 74ef960..cd83691 100644 --- a/Tests/Issuance/IssuerTests.swift +++ b/Tests/Issuance/IssuerTests.swift @@ -16,7 +16,8 @@ import Foundation import XCTest -import JOSESwift +import JSONWebKey +import JSONWebSignature import SwiftyJSON @testable import eudi_lib_sdjwt_swift @@ -24,8 +25,9 @@ import SwiftyJSON final class IssuerTest: XCTestCase { func testIssuer_ForIssuance_WhenProvidedWithAsetOfClaimsAndIssuersPrivateKey() throws -> SignedSDJWT { - let signedSDJWT = try SDJWTIssuer.issue(issuersPrivateKey: issuersKeyPair.private, - header: .init(algorithm: .ES256)) { + let signedSDJWT = try SDJWTIssuer.issue( + issuersPrivateKey: issuersKeyPair.private, + header: DefaultJWSHeaderImpl(algorithm: .ES256)) { ConstantClaims.iat(time: Date()) ConstantClaims.sub(subject: "Test Subject") PlainClaim("name", "plain name") @@ -42,8 +44,8 @@ final class IssuerTest: XCTestCase { let jwtString = compactSerializer.serialised.components(separatedBy: "~").first! - let jws = try JWS(compactSerialization: jwtString) - try jws.validate(using: Verifier(verifyingAlgorithm: .ES256, key: issuersKeyPair.public)!) + let jws = try JWS(jwsString: jwtString) + XCTAssertTrue(try jws.verify(key: issuersKeyPair.public)) } func testEnvelopedFormatSerializeation_WhenProvidedWithABuiltSDJWT() throws { @@ -55,15 +57,15 @@ final class IssuerTest: XCTestCase { // "nonce": "iRnRdKuu1AtLM4ltc16by2XF0accSeutUescRw6BWC14" // ])) - let payload = try Payload(JSON([ - "aud": "https://verifier.example.com", - "iat": 1580000000, - "nonce": "iRnRdKuu1AtLM4ltc16by2XF0accSeutUescRw6BWC14"]).rawData()) + let payload = try JSON([ + "aud": "https://verifier.example.com", + "iat": 1580000000, + "nonce": "iRnRdKuu1AtLM4ltc16by2XF0accSeutUescRw6BWC14"]).rawData() let envelopedFormat = try EnvelopedSerialiser(SDJWT: sdjwt, jwTpayload: payload) - let jwt = try JWT(header: .init(algorithm: .ES256), payload: JSON(envelopedFormat.data)) + let jwt = try JWT(header: DefaultJWSHeaderImpl(algorithm: .ES256), payload: JSON(envelopedFormat.data)) print(jwt.payload) } } diff --git a/Tests/Issuance/KeyBindingTest.swift b/Tests/Issuance/KeyBindingTest.swift index bbbc19b..ba6585c 100644 --- a/Tests/Issuance/KeyBindingTest.swift +++ b/Tests/Issuance/KeyBindingTest.swift @@ -14,9 +14,10 @@ * limitations under the License. */ import Foundation -import XCTest -import JOSESwift +import JSONWebKey +import JSONWebSignature import SwiftyJSON +import XCTest @testable import eudi_lib_sdjwt_swift @@ -51,20 +52,19 @@ final class KeyBindingTest: XCTestCase { func testKeyBinding() throws { let factory = SDJWTFactory(saltProvider: DefaultSaltProvider()) - let pk = try ECPublicKey(publicKey: issuersKeyPair.public) + let pk = try issuersKeyPair.public.jwk let jwk: JSON = try - ["jwk": JSON(data: pk.jsonData()!)] + ["jwk": JSON(data: try JSONEncoder.jwt.encode(pk))] let keyBindingJwt = factory.createSDJWTPayload(sdjwtObject: claims.asObject, holdersPublicKey: jwk) } func testcCreateKeyBindingJWT_whenPassedECPublicKey() throws { let json = JSON(parseJSON: jwk) - let ecPk = try ECPublicKey(data: json.rawData()) - print(try ecPk.converted(to: SecKey.self)) + let ecPk = try JSONDecoder.jwt.decode(JWK.self, from: jwk.tryToData()) - let kbJws = try JWS(compactSerialization: kbJwt) - let verifier = try SignatureVerifier(signedJWT: kbJws, publicKey: ecPk.converted(to: SecKey.self)) + let kbJws = try JWS(jwsString: kbJwt) + let verifier = try SignatureVerifier(signedJWT: kbJws, publicKey: ecPk) try XCTAssertNoThrow(verifier.verify()) } @@ -72,21 +72,23 @@ final class KeyBindingTest: XCTestCase { let factory = SDJWTFactory(saltProvider: DefaultSaltProvider()) - let holdersECPK = try ECPublicKey(publicKey: holdersKeyPair.public) + let holdersECPK = try holdersKeyPair.public.jwk let jwk: JSON = try - ["jwk": JSON(data: holdersECPK.jsonData()!)] + ["jwk": JSON(data: try JSONEncoder.jwt.encode(holdersECPK))] let claims = try factory.createSDJWTPayload(sdjwtObject: claims.asObject, holdersPublicKey: jwk).get() - let issuance = try SDJWTIssuer.createSDJWT(purpose: .issuance(.init(algorithm: .ES256), claims), - signingKey: issuersKeyPair.private) + let issuance = try SDJWTIssuer.createSDJWT( + purpose: .issuance(DefaultJWSHeaderImpl(algorithm: .ES256), claims), + signingKey: issuersKeyPair.private + ) let compactSerializer = CompactSerialiser(signedSDJWT: issuance) let jwtString = compactSerializer.serialised - + let digestCreator = DigestCreator() let out = digestCreator.hashAndBase64Encode(input: jwtString) ?? "" - + let kbjwtPayload: ClaimSet = (JSON( [ Keys.aud.rawValue: "https://example.com/verifier", @@ -98,10 +100,10 @@ final class KeyBindingTest: XCTestCase { let presentation = try SDJWTIssuer.createSDJWT( purpose: .presentation( - issuance, + issuance, issuance.disclosures, KBJWT( - header: .init(algorithm: .ES256), + header: DefaultJWSHeaderImpl(algorithm: .ES256), payload: kbjwtPayload.value ) ), @@ -111,7 +113,7 @@ final class KeyBindingTest: XCTestCase { try SignatureVerifier(signedJWT: issuance.jwt, publicKey: issuersKeyPair.public).verify() try SignatureVerifier(signedJWT: presentation.kbJwt!, publicKey: holdersKeyPair.public).verify() try SignatureVerifier(signedJWT: presentation.jwt, publicKey: issuersKeyPair.public).verify() - + return(issuance, presentation) } diff --git a/Tests/Issuance/SignedJwtTest.swift b/Tests/Issuance/SignedJwtTest.swift index 4fc890b..fb4b1d1 100644 --- a/Tests/Issuance/SignedJwtTest.swift +++ b/Tests/Issuance/SignedJwtTest.swift @@ -14,7 +14,7 @@ * limitations under the License. */ import Foundation -import JOSESwift +import JSONWebSignature import SwiftyJSON import XCTest @@ -27,7 +27,7 @@ final class SignedJwtTest: XCTestCase { let keyPair = generateES256KeyPair() let signedJWT = try SDJWTIssuer.issue( issuersPrivateKey: keyPair.private, - header: .init( + header: DefaultJWSHeaderImpl( algorithm: .ES256 ) ) { diff --git a/Tests/SpecExamples.swift b/Tests/SpecExamples.swift index 10d7d40..b9e5376 100644 --- a/Tests/SpecExamples.swift +++ b/Tests/SpecExamples.swift @@ -14,6 +14,8 @@ * limitations under the License. */ import Foundation +import JSONWebKey +import JSONWebSignature import SwiftyJSON import XCTest @@ -48,7 +50,7 @@ final class SpecExamples: XCTestCase { let keyPair = generateES256KeyPair() - let sdjwt = try SDJWTIssuer.createSDJWT(purpose: .issuance(.init(algorithm: .ES256), output.get()), signingKey: keyPair.private) + let sdjwt = try SDJWTIssuer.createSDJWT(purpose: .issuance(DefaultJWSHeaderImpl(algorithm: .ES256), output.get()), signingKey: keyPair.private) let string = CompactSerialiser(signedSDJWT: sdjwt).serialised diff --git a/Tests/Verification/SerializerTest.swift b/Tests/Verification/SerializerTest.swift index 0b8aea6..233dd79 100644 --- a/Tests/Verification/SerializerTest.swift +++ b/Tests/Verification/SerializerTest.swift @@ -15,7 +15,6 @@ */ import Foundation import XCTest -import JOSESwift @testable import eudi_lib_sdjwt_swift @@ -43,8 +42,9 @@ final class SerialiserTest: XCTestCase { func testSerialiseWhenChosingEnvelopeFormat_AppylingNoKeyBinding_ThenExpectACorrectJWT() throws { let compactParser = try CompactParser(serialisedString: testSerializerWhenSerializedFormatIsSelected_ThenExpectSerialisedFormattedSignedSDJWT()) - let envelopeSerializer = try EnvelopedSerialiser(SDJWT: compactParser.getSignedSdJwt(), - jwTpayload: JWTBody(nonce: "", aud: "sub", iat: 1234).toJSONData().payload) + let envelopeSerializer = try EnvelopedSerialiser( + SDJWT: compactParser.getSignedSdJwt(), + jwTpayload: JWTBody(nonce: "", aud: "sub", iat: 1234).toJSONData()) let parser = try EnvelopedParser(serialiserProtocol: envelopeSerializer) diff --git a/Tests/Verification/VerifierTest.swift b/Tests/Verification/VerifierTest.swift index fd92e2f..d5607e6 100644 --- a/Tests/Verification/VerifierTest.swift +++ b/Tests/Verification/VerifierTest.swift @@ -14,9 +14,10 @@ * limitations under the License. */ import Foundation - +import JSONWebKey +import JSONWebSignature +import JSONWebToken import SwiftyJSON -import JOSESwift import XCTest @testable import eudi_lib_sdjwt_swift @@ -25,7 +26,7 @@ final class VerifierTest: XCTestCase { func testVerifierBehaviour_WhenPassedValidSignatures_ThenExpectToPassAllCriterias() throws { - let pk = try! ECPublicKey(data: JSON(parseJSON: key).rawData()) + let pk = try JSONDecoder.jwt.decode(JWK.self, from: key.tryToData()) // Copied from Spec https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-05.html#name-example-3-complex-structure let complexStructureSDJWTString = """ @@ -62,7 +63,7 @@ final class VerifierTest: XCTestCase { let result = try SDJWTVerifier(parser: CompactParser(serialisedString: complexStructureSDJWTString)) .verifyIssuance { jws in - try SignatureVerifier(signedJWT: jws, publicKey: pk.converted(to: SecKey.self)) + try SignatureVerifier(signedJWT: jws, publicKey: pk) } claimVerifier: { _, _ in ClaimsVerifier() } @@ -202,28 +203,28 @@ final class VerifierTest: XCTestCase { func testVerifierWhenClaimsContainIatExpNbfClaims_ThenExpectTobeInCorrectTimeRanges() throws { let iatJwt = try SDJWTIssuer.issue(issuersPrivateKey: issuersKeyPair.private, - header: .init(algorithm: .ES256), buildSDJWT: { + header: DefaultJWSHeaderImpl(algorithm: .ES256), buildSDJWT: { ConstantClaims.iat(time: Date()) FlatDisclosedClaim("time", "is created at \(Date())") }) let expSdJwt = try SDJWTIssuer.issue( issuersPrivateKey: issuersKeyPair.private, - header: .init(algorithm: .ES256)) { + header: DefaultJWSHeaderImpl(algorithm: .ES256)) { ConstantClaims.exp(time: Date(timeIntervalSinceNow: 36000)) FlatDisclosedClaim("time", "time runs out") } let nbfSdJwt = try SDJWTIssuer.issue( issuersPrivateKey: issuersKeyPair.private, - header: .init(algorithm: .ES256)) { + header: DefaultJWSHeaderImpl(algorithm: .ES256)) { ConstantClaims.nbf(time: Date(timeIntervalSinceNow: -36000)) FlatDisclosedClaim("time", "we are ahead of time") } let nbfAndExpSdJwt = try SDJWTIssuer.issue( issuersPrivateKey: issuersKeyPair.private, - header: .init(algorithm: .ES256) + header: DefaultJWSHeaderImpl(algorithm: .ES256) ) { ConstantClaims.exp(time: Date(timeIntervalSinceNow: 36000)) ConstantClaims.nbf(time: Date(timeIntervalSinceNow: -36000)) @@ -251,12 +252,12 @@ final class VerifierTest: XCTestCase { func testVerifierWhenProvidingAKeyBindingJWT_WHenProvidedWithAudNonceAndIatClaims_ThenExpectToPassClaimVerificationAndKBVerification () throws { let holdersJWK = holdersKeyPair.public + let jwk = try holdersJWK.jwk - let ecpubKey = try ECPublicKey(publicKey: holdersJWK) - let json = JSON(parseJSON: ecpubKey.jsonString()!) - - let issuerSignedSDJWT = try SDJWTIssuer.issue(issuersPrivateKey: issuersKeyPair.private, - header: .init(algorithm: .ES256)) { + let issuerSignedSDJWT = try SDJWTIssuer.issue( + issuersPrivateKey: issuersKeyPair.private, + header: DefaultJWSHeaderImpl(algorithm: .ES256) + ) { ConstantClaims.iat(time: Date()) ConstantClaims.exp(time: Date() + 3600) ConstantClaims.iss(domain: "https://example.com/issuer") @@ -276,9 +277,9 @@ final class VerifierTest: XCTestCase { ObjectClaim("cnf") { ObjectClaim("jwk") { PlainClaim("kty", "EC") - PlainClaim("y", json["y"].stringValue) - PlainClaim("x", json["x"].stringValue) - PlainClaim("crv", json["crv"].stringValue) + PlainClaim("y", jwk.y?.base64URLEncode()) + PlainClaim("x", jwk.x?.base64URLEncode()) + PlainClaim("crv", jwk.curve) } } } @@ -296,7 +297,7 @@ final class VerifierTest: XCTestCase { signedSDJWT: issuerSignedSDJWT, disclosuresToPresent: issuerSignedSDJWT.disclosures, keyBindingJWT: KBJWT( - header: .init(algorithm: .ES256), + header: DefaultJWSHeaderImpl(algorithm: .ES256), kbJwtPayload: .init([ Keys.nonce.rawValue: "123456789", Keys.aud.rawValue: "example.com", @@ -313,10 +314,10 @@ final class VerifierTest: XCTestCase { signedJWT: jws, publicKey: issuersKeyPair.public ) - + } claimVerifier: { _, _ in ClaimsVerifier() - + } keyBindingVerifier: { jws, holdersPublicKey in try KeyBindingVerifier( iatOffset: .init( @@ -338,21 +339,26 @@ final class VerifierTest: XCTestCase { let compactParser = try CompactParser(serialisedString: serializerTest.testSerializerWhenSerializedFormatIsSelected_ThenExpectSerialisedFormattedSignedSDJWT()) - let envelopeSerializer = try EnvelopedSerialiser(SDJWT: compactParser.getSignedSdJwt(), - jwTpayload: JWTBody(nonce: "", aud: "sub", iat: 1234).toJSONData().payload) + let envelopeSerializer = try EnvelopedSerialiser( + SDJWT: compactParser.getSignedSdJwt(), + jwTpayload: JWTBody(nonce: "", aud: "sub", iat: 1234 + ).toJSONData()) - _ = try SignatureVerifier(signedJWT: .init(header: .init(algorithm: .ES256), payload: envelopeSerializer.data.payload, signer: .init(signingAlgorithm: .ES256, key: holdersKeyPair.private)!), publicKey: holdersKeyPair.public) + _ = try SignatureVerifier( + signedJWT: JWS( + payload: envelopeSerializer.data, + protectedHeader: DefaultJWSHeaderImpl(algorithm: .ES256), + key: holdersKeyPair.private + ), + publicKey: holdersKeyPair.public) let jwt = try JWS( - header: .init(algorithm: .ES256), - payload: envelopeSerializer.data.payload, - signer: .init( - signingAlgorithm: .ES256, - key: holdersKeyPair.private - )! + payload: envelopeSerializer.data, + protectedHeader: DefaultJWSHeaderImpl(algorithm: .ES256), + key: holdersKeyPair.private ) - let envelopedJws = try JWS(compactSerialization: jwt.compactSerializedString) + let envelopedJws = try JWS(jwsString: jwt.compactSerialization) let verifyEnvelope = try SDJWTVerifier(parser: EnvelopedParser(data: envelopeSerializer.data))