diff --git a/Package.resolved b/Package.resolved index d371e37..c2b3e1e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -89,6 +89,33 @@ "revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7", "version" : "0.10.1" } + }, + { + "identity" : "swift-asn1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-asn1.git", + "state" : { + "revision" : "c7e239b5c1492ffc3ebd7fbcc7a92548ce4e78f0", + "version" : "1.1.0" + } + }, + { + "identity" : "swift-certificates", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-certificates.git", + "state" : { + "revision" : "bc566f88842b3b8001717326d935c2d113af5741", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "cc76b894169a3c86b71bac10c78a4db6beb7a9ad", + "version" : "3.2.0" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index 8da84d8..8b036e2 100644 --- a/Package.swift +++ b/Package.swift @@ -23,7 +23,15 @@ let package = Package( .package( url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-presentation-exchange-swift.git", .upToNextMinor(from: "0.0.41") - ) + ), + .package( + url: "https://github.com/apple/swift-certificates.git", + .upToNextMajor(from: "1.0.0") + ), + .package( + url: "https://github.com/apple/swift-asn1.git", + .upToNextMajor(from: "1.0.0") + ), ], targets: [ .target( @@ -36,7 +44,15 @@ let package = Package( .product( name: "PresentationExchange", package: "eudi-lib-ios-presentation-exchange-swift" - ) + ), + .product( + name: "X509", + package: "swift-certificates" + ), + .product( + name: "SwiftASN1", + package: "swift-asn1" + ), ], path: "Sources", resources: [ diff --git a/Sources/Main/Encryption/KeyController.swift b/Sources/Main/Encryption/KeyController.swift index 361da75..10f7ea0 100644 --- a/Sources/Main/Encryption/KeyController.swift +++ b/Sources/Main/Encryption/KeyController.swift @@ -20,7 +20,7 @@ public class KeyController { public static func generateHardcodedRSAPrivateKey() throws -> SecKey? { - // Convert PEM key to Data + // Convert DER key to Data guard let contents = String.loadStringFileFromBundle( named: "sample_derfile", @@ -106,6 +106,37 @@ public class KeyController { pemString += "\n-----END RSA PRIVATE KEY-----\n" return pemString } + + public static func convertPEMToPublicKey(_ pem: String) throws -> SecKey? { + + let key = pem + .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "") + .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "") + .split(separator: "\n").joined() + + // Define the key attributes + let attributes: [CFString: Any] = [ + kSecAttrKeyType: kSecAttrKeyTypeRSA, + kSecAttrKeyClass: kSecAttrKeyClassPublic + ] + + guard let privateKeyData = Data( + base64Encoded: key, + options: .ignoreUnknownCharacters + ) else { + return nil + } + + // Create the SecKey object + var error: Unmanaged? + guard let secKey = SecKeyCreateWithData(privateKeyData as CFData, attributes as CFDictionary, &error) else { + if let error = error?.takeRetainedValue() { + print("Failed to create SecKey:", error) + } + return nil + } + return secKey + } } private extension String { diff --git a/Sources/Utilities/Extensions/Certificate+Extensions.swift b/Sources/Utilities/Extensions/Certificate+Extensions.swift new file mode 100644 index 0000000..2cf3dfd --- /dev/null +++ b/Sources/Utilities/Extensions/Certificate+Extensions.swift @@ -0,0 +1,39 @@ +/* + * 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 Foundation +import X509 + +extension SubjectAlternativeNames { + func rawSubjectAlternativeNames() -> [String] { + self.compactMap { generalName in + switch generalName { + case .dnsName(let name): + return name + default: return nil + } + } + } + + func rawUniformResourceIdentifiers() -> [String] { + self.compactMap { generalName in + switch generalName { + case .uniformResourceIdentifier(let identifier): + return identifier + default: return nil + } + } + } +} diff --git a/Tests/Certificates/X509CertificateTests.swift b/Tests/Certificates/X509CertificateTests.swift new file mode 100644 index 0000000..e8b50c3 --- /dev/null +++ b/Tests/Certificates/X509CertificateTests.swift @@ -0,0 +1,67 @@ +/* + * 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 XCTest +import X509 + +@testable import SiopOpenID4VP + +final class X509CertificateTests: XCTestCase { + + override func setUpWithError() throws { + } + + override func tearDownWithError() throws { + } + + func testSecKeyCreationFromX509Certificate() throws { + + if let data = Data(base64Encoded: TestsConstants.x5cCertificate) { + let derBytes = [UInt8](data) + let certificate = try Certificate(derEncoded: derBytes) + + let publicKey = certificate.publicKey + let pem = try publicKey.serializeAsPEM().pemString + + let secKey = try KeyController.convertPEMToPublicKey(pem) + XCTAssertNotNil(secKey) + + let clientIndentifier = "client_identifer.example.com" + guard let dnss = try? certificate.extensions.subjectAlternativeNames?.rawSubjectAlternativeNames() else { + XCTFail("Could not locate subject alternative names") + return + } + XCTAssert(!dnss.isEmpty) + XCTAssert(dnss.contains(where: { $0 == clientIndentifier })) + + let uri = "https://www.example.com" + guard let uris = try? certificate.extensions.subjectAlternativeNames?.rawUniformResourceIdentifiers() else { + XCTFail("Could not locate uri's") + return + } + XCTAssert(!uris.isEmpty) + XCTAssert(uris.contains(where: { $0 == uri })) + + let valid = publicKey.isValidSignature(certificate.signature, for: certificate) + XCTAssert(valid) + + let legacyCertificate = SecCertificateCreateWithData(nil, data as CFData) + XCTAssertNotNil(legacyCertificate) + + } else { + XCTFail("Could not get SecKey from base64 x509") + } + } +} diff --git a/Tests/Constants/TestsConstants.swift b/Tests/Constants/TestsConstants.swift index 78e8761..882b1a7 100644 --- a/Tests/Constants/TestsConstants.swift +++ b/Tests/Constants/TestsConstants.swift @@ -374,6 +374,11 @@ struct TestsConstants { static var encryptedResponseEnc = "A128CBC-HS256" static var subjectSyntaxTypesSupported = "urn:ietf:params:oauth:jwk-thumbprint" - static var host = "https://eudi.netcompany-intrasoft.com" + static var localHost = "http://localhost:8080" + static var remoteHost = "https://eudi.netcompany-intrasoft.com" + static var host = Self.localHost + static var cbor = "o2d2ZXJzaW9uYzEuMGlkb2N1bWVudHOBo2dkb2NUeXBleBhldS5ldXJvcGEuZWMuZXVkaXcucGlkLjFsaXNzdWVyU2lnbmVkompuYW1lU3BhY2VzoXgYZXUuZXVyb3BhLmVjLmV1ZGl3LnBpZC4xmB3YGFhZpGhkaWdlc3RJRBghZnJhbmRvbVC3GZQ4Vaowh4KULM5o7dhhcWVsZW1lbnRJZGVudGlmaWVya2ZhbWlseV9uYW1lbGVsZW1lbnRWYWx1ZWlBTkRFUlNTT07YGFhRpGhkaWdlc3RJRA1mcmFuZG9tUL-V-LtKk1Tnb2BD-75yb2dxZWxlbWVudElkZW50aWZpZXJqZ2l2ZW5fbmFtZWxlbGVtZW50VmFsdWVjSkFO2BhYW6RoZGlnZXN0SUQMZnJhbmRvbVDEd6i_vCHSwwUh0cYis_2EcWVsZW1lbnRJZGVudGlmaWVyamJpcnRoX2RhdGVsZWxlbWVudFZhbHVl2QPsajE5ODUtMDMtMzDYGFhPpGhkaWdlc3RJRAZmcmFuZG9tUC9Iodu5b6Z6RBIlCTasrgJxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMThsZWxlbWVudFZhbHVl9dgYWFKkaGRpZ2VzdElEGCBmcmFuZG9tUOA2yxnnNGnBHl_-8Mnn_LZxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJzbGVsZW1lbnRWYWx1ZRgm2BhYVKRoZGlnZXN0SUQEZnJhbmRvbVD5ahR3sjQQA7vJvAmxHVwhcWVsZW1lbnRJZGVudGlmaWVybmFnZV9iaXJ0aF95ZWFybGVsZW1lbnRWYWx1ZRkHwdgYWFikaGRpZ2VzdElEGBtmcmFuZG9tUGvvcr45W1M-TOXWhqRtGGVxZWxlbWVudElkZW50aWZpZXJpdW5pcXVlX2lkbGVsZW1lbnRWYWx1ZWowMTI4MTk2NTMy2BhYXqRoZGlnZXN0SUQPZnJhbmRvbVCdK4qRNr7JWc2xdOA0bzjvcWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRobGVsZW1lbnRWYWx1ZWlBTkRFUlNTT07YGFhXpGhkaWdlc3RJRBdmcmFuZG9tUNtO_9G4ZlB1FNureyu40FFxZWxlbWVudElkZW50aWZpZXJwZ2l2ZW5fbmFtZV9iaXJ0aGxlbGVtZW50VmFsdWVjSkFO2BhYVaRoZGlnZXN0SUQOZnJhbmRvbVApwjr0dHp75VqkyCojGZkbcWVsZW1lbnRJZGVudGlmaWVya2JpcnRoX3BsYWNlbGVsZW1lbnRWYWx1ZWZTV0VERU7YGFhTpGhkaWdlc3RJRBVmcmFuZG9tUNZ7jedRLHgQ00_WB9umaIxxZWxlbWVudElkZW50aWZpZXJtYmlydGhfY291bnRyeWxlbGVtZW50VmFsdWViU0XYGFhSpGhkaWdlc3RJRBgZZnJhbmRvbVCN_FgslPAt6ncEwX4jv3NicWVsZW1lbnRJZGVudGlmaWVya2JpcnRoX3N0YXRlbGVsZW1lbnRWYWx1ZWJTRdgYWFqkaGRpZ2VzdElEGBhmcmFuZG9tUAlRldKQE3gdvstn8eAE48JxZWxlbWVudElkZW50aWZpZXJqYmlydGhfY2l0eWxlbGVtZW50VmFsdWVrS0FUUklORUhPTE3YGFhkpGhkaWdlc3RJRBgeZnJhbmRvbVCMsClpQzri9Ts3rvrGQyNHcWVsZW1lbnRJZGVudGlmaWVycHJlc2lkZW50X2FkZHJlc3NsZWxlbWVudFZhbHVlb0ZPUlRVTkFHQVRBTiAxNdgYWFakaGRpZ2VzdElEC2ZyYW5kb21QeJHFNssLiRkDK8XFJFGuQHFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ5bGVsZW1lbnRWYWx1ZWJTRdgYWFWkaGRpZ2VzdElEGBpmcmFuZG9tUFf3D57jOLFNyMGkPOeq439xZWxlbWVudElkZW50aWZpZXJucmVzaWRlbnRfc3RhdGVsZWxlbWVudFZhbHVlYlNF2BhYXKRoZGlnZXN0SUQTZnJhbmRvbVBWB0GNrKdBQVrlpImIRgUUcWVsZW1lbnRJZGVudGlmaWVybXJlc2lkZW50X2NpdHlsZWxlbWVudFZhbHVla0tBVFJJTkVIT0xN2BhYXaRoZGlnZXN0SUQSZnJhbmRvbVDsZlLl2N7J71jX-6bXsnwEcWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2RlbGVsZW1lbnRWYWx1ZWU2NDEzM9gYWF-kaGRpZ2VzdElEAGZyYW5kb21QeVoB8I5BSgsvMvSFktXxSXFlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXRsZWxlbWVudFZhbHVlbEZPUlRVTkFHQVRBTtgYWFykaGRpZ2VzdElEGB1mcmFuZG9tUPZqEH9sCb0LsU7Q1r6NY9pxZWxlbWVudElkZW50aWZpZXJ1cmVzaWRlbnRfaG91c2VfbnVtYmVybGVsZW1lbnRWYWx1ZWIxMtgYWEukaGRpZ2VzdElEGBxmcmFuZG9tUECs5kRT8jGbvlJFfN9PzHVxZWxlbWVudElkZW50aWZpZXJmZ2VuZGVybGVsZW1lbnRWYWx1ZQHYGFhRpGhkaWdlc3RJRBRmcmFuZG9tUJAnJk_8qaLhZyz16KD1mm5xZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHlsZWxlbWVudFZhbHVlYlNF2BhYZqRoZGlnZXN0SUQQZnJhbmRvbVDnNyg3BVSwxg7oPIz_ex1lcWVsZW1lbnRJZGVudGlmaWVybWlzc3VhbmNlX2RhdGVsZWxlbWVudFZhbHVlwHQyMDA5LTAxLTAxVDAwOjAwOjAwWtgYWGSkaGRpZ2VzdElEEWZyYW5kb21QiLdvkB7-ePM8bQhtrw03P3FlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZWxlbGVtZW50VmFsdWXAdDIwNTAtMDMtMzBUMDA6MDA6MDBa2BhYWaRoZGlnZXN0SUQYH2ZyYW5kb21Q88ycru5RpECbD5sO1xF5JXFlbGVtZW50SWRlbnRpZmllcnFpc3N1aW5nX2F1dGhvcml0eWxlbGVtZW50VmFsdWVjVVRP2BhYXKRoZGlnZXN0SUQHZnJhbmRvbVD5ok_CSVzYG_zxW4dCLYgRcWVsZW1lbnRJZGVudGlmaWVyb2RvY3VtZW50X251bWJlcmxlbGVtZW50VmFsdWVpMTExMTExMTE02BhYY6RoZGlnZXN0SUQWZnJhbmRvbVADONLlWKTDtc-PYFNRXifWcWVsZW1lbnRJZGVudGlmaWVydWFkbWluaXN0cmF0aXZlX251bWJlcmxlbGVtZW50VmFsdWVqOTAxMDE2NzQ2NNgYWFWkaGRpZ2VzdElEAWZyYW5kb21QnEwHop2wmETQk18jh7jsMnFlbGVtZW50SWRlbnRpZmllcm9pc3N1aW5nX2NvdW50cnlsZWxlbWVudFZhbHVlYlNF2BhYXKRoZGlnZXN0SUQJZnJhbmRvbVCakKMOPVNIi1XdtiS3RAEGcWVsZW1lbnRJZGVudGlmaWVydGlzc3VpbmdfanVyaXNkaWN0aW9ubGVsZW1lbnRWYWx1ZWRTRS1Jamlzc3VlckF1dGiEQ6EBJqEYIVkChTCCAoEwggImoAMCAQICCRZK5ZkC3AUQZDAKBggqhkjOPQQDAjBYMQswCQYDVQQGEwJCRTEcMBoGA1UEChMTRXVyb3BlYW4gQ29tbWlzc2lvbjErMCkGA1UEAxMiRVUgRGlnaXRhbCBJZGVudGl0eSBXYWxsZXQgVGVzdCBDQTAeFw0yMzA1MzAxMjMwMDBaFw0yNDA1MjkxMjMwMDBaMGUxCzAJBgNVBAYTAkJFMRwwGgYDVQQKExNFdXJvcGVhbiBDb21taXNzaW9uMTgwNgYDVQQDEy9FVSBEaWdpdGFsIElkZW50aXR5IFdhbGxldCBUZXN0IERvY3VtZW50IFNpZ25lcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHyTE_TBpKpOsLPraBGkmU5Z3meZZDHC864IjrehBhy2WL2MORJsGVl6yQ35nQeNPvORO6NL2yy8aYfQJ-mvnfyjgcswgcgwHQYDVR0OBBYEFNGksSQ5MvtFcnKZSPJSfZVYp00tMB8GA1UdIwQYMBaAFDKR6w4cAR0UDnZPbE_qTJY42vsEMA4GA1UdDwEB_wQEAwIHgDASBgNVHSUECzAJBgcogYxdBQECMB8GA1UdEgQYMBaGFGh0dHA6Ly93d3cuZXVkaXcuZGV2MEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHBzOi8vc3RhdGljLmV1ZGl3LmRldi9wa2kvY3JsL2lzbzE4MDEzLWRzLmNybDAKBggqhkjOPQQDAgNJADBGAiEA3l-Y5x72V1ISa_LEuE_e34HSQ8pXsVvTGKq58evrP30CIQD-Ivcya0tXWP8W_obTOo2NKYghadoEm1peLIBqsUcISFkF9tgYWQXxpmd2ZXJzaW9uYzEuMG9kaWdlc3RBbGdvcml0aG1nU0hBLTI1Nmdkb2NUeXBleBhldS5ldXJvcGEuZWMuZXVkaXcucGlkLjFsdmFsdWVEaWdlc3RzoXgYZXUuZXVyb3BhLmVjLmV1ZGl3LnBpZC4xuCIAWCD7W3-dBVCt3o3cDIYNaDkM1DS45diRQiz4K3YWFeSWFQFYIKJtyxTxqSQabkLeNzlU47KF9EGDkj3V5rH0e9Q1-lsEAlggUNP1_0Zc8kTYS8QL6z4oQomUBgH6O-shAj5iyZ8bCAUDWCCuQigYwxLYp4LVGOTGUs1qnnTU1tvLMcC98b_VigL_swRYIAfS34ulNQZvT0E22diN-NuIae52N9SzZXe-xlMp5C1vBVggAkEv95LMRoGJyOteiPfAU8_PThHsdmzt0Xyt6JoEsZoGWCDXqUDv-8ZAiWDnaafykV_T_01Lp1riTPapZNU5zI40wgdYIFLiTVYD7-VcrqniT04k_Q5H-hN6tYOEhk9hsTo11doKCFggxZXKN--iXILeaopizcn63992DEtS0KUxuEY6G7tSqB0JWCA7b-nAaWHc3AjTMtCTo178-Fq1bUrtCL39os-grqpa7QpYIIiHGCd2RZ3WPvhk7IIs-dxoWlc1v8SStjYi7uIzo__2C1ggmJ0y-WZefrnjeKUSoJgp48nLSgUGpKTllcz75lcj8TwMWCAteXytlADF9YQcRhXnbHZ4hU-3Fn5V-Yfbmo4A04Cq0Q1YIMQKhcyPGZizCKvulDn8dUumLukLSmAqeno7xdvBPYlQDlggh5HapQ08xo6J5hPpvxtKamGU5Q2yNAc_dwLuyZ9vZ7UPWCBquMRplGPA8YtUtWPsWswGMb-G8N9ZDVBEtMU96CJN0hBYIJLyfEQ3cYpDg7P6qDlmAO0zG7uQB_RVzHsPRXOtJZrTEVggJujmxDXHoF1vp6Os2d_7Y5lyuo0JVrxd78aAU8OnLOgSWCA-9Wlkv2ooawcektfcHHta08eB06bKB5ckORg3_6Gt6RNYIOrbnoAvuGPILZb4oU9OXuFwhrmN24sUHQHaJM1wPo6CFFgg-YDc24tVJZ1aBiVZrIryUkklnztL_DjkSW_0qLuDjsUVWCAxr4Uys8fXbxeTvKfofNqpxTmo7mwCTygExduL5M38MBZYIJXXKk1Az7gaKrhY_Ahsz_n8pDhDY1Lfmqm0QS4JoaF8F1ggl-1ikQmy375eg5ya4CmO4iUZRb7iWm1zeI6zhUhM7lEYGFggMJUtUrlwL9MPNFtEb0Hnz5SqB2qlQrrQj3ZnQM-mGmgYGVggWl3f3p6lfBFnbu2daWLJm39SoGElzlfavTOx3F_3E74YGlggvdD45pF51Cy3yiZ2_nSYqVqJyHT8QNToZG6TNzWEwHAYG1gg8ANMye6A3IzWp2c8WNSRM0Y2Mh1mjIRPw0HKx3isb5gYHFggNDf7Ax-w_4phWXsVvPRk7P7ofgHjKkUBT78O75cwXIkYHVggDXc6YZNFdRk46rqUsKlvVmMzpBHnqA2XZJmqaugJvzcYHlggXH0jeH3-U3jTzR37HDW-jWK6ouF-G5NNPuJmuj6hpQEYH1ggG0an4SprpVwTUMcScwaAZg0Le3EUMRXs2kXZEMi1YiMYIFggI1FLAHl0o8wOVSfw9YJXN8sMeV9UlVg6hpK5ftfTO7EYIVggplIunh7mRw7jAxRznT1H65zgNia37L_reyAW-NqDRPBtZGV2aWNlS2V5SW5mb6FpZGV2aWNlS2V5pAECIAEhWCABzyHIg6bpG-9oGY8eJKRliIpIZAkYu6kgXPmqWEat-yJYIINkaU-HyQEVbtaFN1tc2jlxpe-HF1qvKIpq_oZyZ9gtbHZhbGlkaXR5SW5mb6Nmc2lnbmVkwHQyMDIzLTA5LTA1VDEyOjIyOjUwWml2YWxpZEZyb23AdDIwMjMtMDktMDVUMTI6MjI6NTBaanZhbGlkVW50aWzAdDIwMjQtMDktMDVUMTI6MjI6NTBaWEBrha1cC82HzHS162luGdghMM6OKLzqSaFZk_n1sxiHVkt3Hg9p8N5nE0lHUeUSoGTPzxfLRy-iX98Hd2YRSoybbGRldmljZVNpZ25lZKJqbmFtZVNwYWNlc9gYQaBqZGV2aWNlQXV0aKFvZGV2aWNlU2lnbmF0dXJlhEOhASag9lhA2m2BqQWbJmPL5xogKMm0Vw7_kakFqEStS3nGjaWZmTXmUzuVTLNw8pHw-0rcgd4oPIwpFwHyFYcS5AFaDLujPmZzdGF0dXMA" + + static var x5cCertificate = "MIIDSTCCAjGgAwIBAgIUBAfbyjpjLFsSTAaCY43xSz/aKB8wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjQwMTEwMTQzNTQyWhcNMjUwMTA5MTQzNTQyWjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK6JArDMWQH+JImQW4Kdlzt25obTlUzmXQBMHVZ47M5VgPBAQ25Db8y7ev7CVV7WFTxNrISLER0tVW47I7H4mzfbcK1UCEyRA6A1uwbfdw1af4ajOuVqoWqJFB2zqK3VmjymqWAvSPfnXR1UyMQQj2NObABz4YacuuK3uzcOKnmHYQ8adavzvPLmeA06s9Hjk6RjEoCazAngYABdA3bVfi6TS0Nqj4B5580BVu5HFTj8Pw7aDBVQ6tj/uBgJW4tKQlARn3aMGJbZ1zUC2pFyJ8bMQnejqmuD4mJpmPf+Ihz4nQlYTFKFlK3ASRZjfgDd3rkktPu8CQ9Sg1bTaZWOw1UCAwEAAaOBjjCBizBqBgNVHREEYzBhggtleGFtcGxlLmNvbYIVc3ViZG9tYWluLmV4YW1wbGUuY29tghxjbGllbnRfaWRlbnRpZmVyLmV4YW1wbGUuY29thwTAqAEBhhdodHRwczovL3d3dy5leGFtcGxlLmNvbTAdBgNVHQ4EFgQUEkq36yycm2K2uF1lYCOZDwHmFmIwDQYJKoZIhvcNAQELBQADggEBAIz/fqjYX6iFYqJyJVESoXuLigceG/mGz2mOnXnA5EDjZqk+0rwngMA4So8cHSUcD31UNmG26zWrPM1gFVkjZNn5gcpxdRkYzONDbBNFKoHBxUJIRvDuR3JpesI7aBmYWr3gm68EYa2CUyUztW7hIc7KAao85UI5Q49o9cJxT6EjwDXz8NsJS6lHCDEP7R0ZBjI1Qnv8BIzZKsLoPMt5LxUCVpoV+MjrcKIBTsoISJpI4SAYG/Yz1YWlhSD1rYNax1V21EeN9T+E111JqVve2AQMr3CLLtMAiY5jPIXlFvtiIUtY9I3uGdd2QA/HNiE87Q6o07wf8n/groYy2fVONYo=" } diff --git a/Tests/DirectPost/DirectPostJWTTests.swift b/Tests/DirectPost/DirectPostJWTTests.swift index 18293a6..30654f0 100644 --- a/Tests/DirectPost/DirectPostJWTTests.swift +++ b/Tests/DirectPost/DirectPostJWTTests.swift @@ -211,7 +211,8 @@ final class DirectPostJWTTests: DiXCTest { let session = try? await TestsHelpers.getDirectPostJwtSession(nonce: nonce) guard let session = session else { - XCTAssert(true, "this tests depends on a local verifier running") + XCTExpectFailure("this tests depends on a local verifier running") + XCTAssert(false) return } @@ -225,7 +226,8 @@ final class DirectPostJWTTests: DiXCTest { // Do not fail 404 guard let result = result else { - XCTAssert(true, "this tests depends on a local verifier running") + XCTExpectFailure("this tests depends on a local verifier running") + XCTAssert(false) return } @@ -276,7 +278,7 @@ final class DirectPostJWTTests: DiXCTest { resolvedRequest: resolved, consent: consent, walletOpenId4VPConfig: wallet - ), "Expected a non-nil item") + ), "Expected item to be non-nil") // Dispatch XCTAssertNotNil(response) @@ -380,7 +382,7 @@ final class DirectPostJWTTests: DiXCTest { )) let consent: ClientConsent = .vpToken( - vpToken: "dummy_vp_token", + vpToken: TestsConstants.cbor, presentationSubmission: .init( id: "psId", definitionID: "psId", diff --git a/Tests/DirectPost/DirectPostTests.swift b/Tests/DirectPost/DirectPostTests.swift index 5fcce26..5a6e525 100644 --- a/Tests/DirectPost/DirectPostTests.swift +++ b/Tests/DirectPost/DirectPostTests.swift @@ -14,6 +14,7 @@ * limitations under the License. */ import Foundation +import CryptoKit import XCTest import Mockingbird @@ -263,13 +264,19 @@ final class DirectPostTests: DiXCTest { XCTAssertNotNil(result) } + func testSDKEndtoEndDirectPostVpToken() async throws { - let nonce = UUID().uuidString + let nonce = Data(ChaChaPoly.Nonce()).base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .trimmingCharacters(in: CharacterSet(charactersIn: "=")) + let session = try? await TestsHelpers.getDirectPostVpTokenSession(nonce: nonce) guard let session = session else { - XCTAssert(true, "this tests depends on a local verifier running") + XCTExpectFailure("this tests depends on a local verifier running") + XCTAssert(false) return } @@ -283,7 +290,8 @@ final class DirectPostTests: DiXCTest { // Do not fail 404 guard let result = result else { - XCTAssert(true, "this tests depends on a local verifier running") + XCTExpectFailure("this tests depends on a local verifier running") + XCTAssert(false) return } @@ -363,7 +371,8 @@ final class DirectPostTests: DiXCTest { let session = try? await TestsHelpers.getDirectPostSession(nonce: nonce) guard let session = session else { - XCTAssert(true, "this tests depends on a local verifier running") + XCTExpectFailure("this tests depends on a local verifier running") + XCTAssert(false) return } @@ -377,7 +386,8 @@ final class DirectPostTests: DiXCTest { // Do not fail 404 guard let result = result else { - XCTAssert(true) + XCTExpectFailure("this tests depends on a local verifier running") + XCTAssert(false) return } diff --git a/Tests/Helpers/TestsHelpers.swift b/Tests/Helpers/TestsHelpers.swift index 8da382d..5b7544f 100644 --- a/Tests/Helpers/TestsHelpers.swift +++ b/Tests/Helpers/TestsHelpers.swift @@ -63,7 +63,25 @@ class TestsHelpers { "presentation_definition_mode": "by_reference", "presentation_definition": [ "id": "32f54163-7166-48f1-93d8-ff217bdb0653", - "input_descriptors": [] + "input_descriptors": [ + [ + "id": "wa_driver_license", + "name": "Washington State Business License", + "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference", + "constraints": [ + "fields": [ + [ + "path": [ + "$.credentialSubject.dateOfBirth", + "$.credentialSubject.dob", + "$.vc.credentialSubject.dateOfBirth", + "$.vc.credentialSubject.dob" + ] + ] + ] + ] + ] + ] ] ] as [String : Any] @@ -94,6 +112,8 @@ class TestsHelpers { "presentation_definition_mode": "by_reference", "presentation_definition": [ "id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "name": "name", + "purpose": "purpose", "input_descriptors": [] ] ] as [String : Any] diff --git a/Tests/Resolvers/WebKeyResolverTests.swift b/Tests/Resolvers/WebKeyResolverTests.swift index 4474850..4d41432 100644 --- a/Tests/Resolvers/WebKeyResolverTests.swift +++ b/Tests/Resolvers/WebKeyResolverTests.swift @@ -63,6 +63,7 @@ final class WebKeyResolverTests: DiXCTest { case .success(let webKeys): XCTAssertEqual(webKeys?.keys.first?.kty, TestsConstants.webKeySet.keys.first?.kty) case .failure(let error): + XCTExpectFailure() XCTFail(error.localizedDescription) } } diff --git a/Tests/Validators/ClientMetaDataValidatorTests.swift b/Tests/Validators/ClientMetaDataValidatorTests.swift index ad64d09..4ca05a8 100644 --- a/Tests/Validators/ClientMetaDataValidatorTests.swift +++ b/Tests/Validators/ClientMetaDataValidatorTests.swift @@ -35,19 +35,25 @@ final class ClientMetaDataValidatorTests: XCTestCase { func testValidate_WhenFetchByReferenceWithValidURL_ThenReturnSuccess() async throws { - let response: ClientMetaData.Validated = try await self.validator.validate(clientMetaData: .init( - jwksUri: TestsConstants.validByReferenceWebKeyUrlString, - jwks: nil, - idTokenSignedResponseAlg: TestsConstants.signedResponseAlg, - idTokenEncryptedResponseAlg: TestsConstants.encryptedResponseAlg, - idTokenEncryptedResponseEnc: TestsConstants.encryptedResponseEnc, - subjectSyntaxTypesSupported: [TestsConstants.subjectSyntaxTypesSupported], - authorizationSignedResponseAlg: TestsConstants.signedResponseAlg, - authorizationEncryptedResponseAlg: TestsConstants.encryptedResponseAlg, - authorizationEncryptedResponseEnc: TestsConstants.encryptedResponseEnc - ))! - - XCTAssertEqual(response.jwkSet?.keys.first?.kty, TestsConstants.webKeySet.keys.first?.kty) + do { + let response: ClientMetaData.Validated = try await self.validator.validate(clientMetaData: .init( + jwksUri: TestsConstants.validByReferenceWebKeyUrlString, + jwks: nil, + idTokenSignedResponseAlg: TestsConstants.signedResponseAlg, + idTokenEncryptedResponseAlg: TestsConstants.encryptedResponseAlg, + idTokenEncryptedResponseEnc: TestsConstants.encryptedResponseEnc, + subjectSyntaxTypesSupported: [TestsConstants.subjectSyntaxTypesSupported], + authorizationSignedResponseAlg: TestsConstants.signedResponseAlg, + authorizationEncryptedResponseAlg: TestsConstants.encryptedResponseAlg, + authorizationEncryptedResponseEnc: TestsConstants.encryptedResponseEnc + ))! + + XCTAssertEqual(response.jwkSet?.keys.first?.kty, TestsConstants.webKeySet.keys.first?.kty) + } catch { + + XCTExpectFailure() + XCTFail() + } } func testValidate_WhenFetchByReferenceWithInvalidURL_ThenReturnFailure() async throws { diff --git a/create_certificate.sh b/create_certificate.sh new file mode 100755 index 0000000..30c7b0c --- /dev/null +++ b/create_certificate.sh @@ -0,0 +1,26 @@ +config_content="[ req ] +req_extensions = v3_req + +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = example.com +DNS.2 = subdomain.example.com +DNS.3 = client_identifer.example.com +IP.1 = 192.168.1.1 +URI.1 = https://www.example.com +" + +echo "$config_content" > openssl.cnf + +openssl genpkey -algorithm RSA -out private-key.pem +openssl req -new -key private-key.pem -out csr.pem -subj "/CN=example.com" +openssl x509 -req -in csr.pem -signkey private-key.pem -out certificate.pem -extfile openssl.cnf -extensions v3_req -days 365 +openssl x509 -in certificate.pem -text -noout +cat certificate.pem | sed -e '/-----BEGIN CERTIFICATE-----/d' -e '/-----END CERTIFICATE-----/d' | tr -d '\n\r' + +rm openssl.cnf +rm private-key.pem +rm csr.pem +rm certificate.pem \ No newline at end of file