Skip to content

Commit 21e8446

Browse files
Neno StefanovNenoStefanov
authored andcommitted
test: validate signature verification certificate
1 parent 713afab commit 21e8446

File tree

8 files changed

+100
-124
lines changed

8 files changed

+100
-124
lines changed

MIRACLTrust/MIRACLTrust.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@
240240
6DFE8FC527EB6E380085380D /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFE8FC427EB6E380085380D /* Helpers.swift */; };
241241
6DFEC8CE26E1049100E8F682 /* MockURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFEC8CD26E1049100E8F682 /* MockURLSession.swift */; };
242242
83695C1C2C9734AA00411EAA /* EmailVerificationMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83695C1B2C9734AA00411EAA /* EmailVerificationMethod.swift */; };
243+
838EE9302EDECD2000042452 /* SignatureCertificateJWTPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 838EE92F2EDECD1200042452 /* SignatureCertificateJWTPayload.swift */; };
243244
83BD3EFB2CC902F000D4E47F /* GmailServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83BD3EFA2CC902DF00D4E47F /* GmailServiceWrapper.swift */; };
244245
83BD3F0B2CCA23A900D4E47F /* AuthenticationJWTPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83BD3F0A2CCA23A500D4E47F /* AuthenticationJWTPayload.swift */; };
245246
83DE9EBE2CCA320D00BD7749 /* JWTKit in Frameworks */ = {isa = PBXBuildFile; productRef = 83DE9EBD2CCA320D00BD7749 /* JWTKit */; };
@@ -555,6 +556,7 @@
555556
6DFE8FC427EB6E380085380D /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
556557
6DFEC8CD26E1049100E8F682 /* MockURLSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = "<group>"; };
557558
83695C1B2C9734AA00411EAA /* EmailVerificationMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailVerificationMethod.swift; sourceTree = "<group>"; };
559+
838EE92F2EDECD1200042452 /* SignatureCertificateJWTPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignatureCertificateJWTPayload.swift; sourceTree = "<group>"; };
558560
83BD3EF82CC7CDBD00D4E47F /* GmailService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GmailService.swift; sourceTree = "<group>"; };
559561
83BD3EFA2CC902DF00D4E47F /* GmailServiceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GmailServiceWrapper.swift; sourceTree = "<group>"; };
560562
83BD3F0A2CCA23A500D4E47F /* AuthenticationJWTPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationJWTPayload.swift; sourceTree = "<group>"; };
@@ -956,6 +958,7 @@
956958
83BD3EFA2CC902DF00D4E47F /* GmailServiceWrapper.swift */,
957959
6DFE8FC427EB6E380085380D /* Helpers.swift */,
958960
83BD3F0A2CCA23A500D4E47F /* AuthenticationJWTPayload.swift */,
961+
838EE92F2EDECD1200042452 /* SignatureCertificateJWTPayload.swift */,
959962
);
960963
path = Helpers;
961964
sourceTree = "<group>";
@@ -1501,6 +1504,7 @@
15011504
6D26142C24AB1B7C004F0B54 /* Constants.swift in Sources */,
15021505
6D2E4F192E2E7A290001B5A7 /* CrossDeviceSessionIntegrationTest.swift in Sources */,
15031506
6D91ABDA26FDF4C000EE28CF /* SessionDetailsTestCase.swift in Sources */,
1507+
838EE9302EDECD2000042452 /* SignatureCertificateJWTPayload.swift in Sources */,
15041508
6D6B94B82E46141600BC7A60 /* CrossDeviceSessionAbortCase.swift in Sources */,
15051509
6D4167A52E435C78002E59EA /* CrossDeviceSessionAuthenticationCase.swift in Sources */,
15061510
6DDEB15127070C2C00C3313D /* AbortSessionIntergrationTest.swift in Sources */,

MIRACLTrust/MIRACLTrustIntegrationTests/Cases/Swift/AuthenticationIntegrationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class AuthenticationIntegrationTests: XCTestCase {
8787
XCTAssertNil(jwtError)
8888
XCTAssertNotNil(jwt)
8989

90-
let jwks = try XCTUnwrap(api.getJWKS(projectURL: projectURL))
90+
let jwks = try String(contentsOf: XCTUnwrap(URL(string: "\(projectURL)/.well-known/jwks")))
9191
let signers = JWTSigners()
9292
try signers.use(jwksJSON: jwks)
9393
let payload = try signers.verify(jwt!, as: AuthenticationJWTPayload.self)

MIRACLTrust/MIRACLTrustIntegrationTests/Cases/Swift/SigningIntegrationTests.swift

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import CryptoKit
2+
import JWTKit
23
import XCTest
34

45
@testable import MIRACLTrust
@@ -106,23 +107,33 @@ class SigningIntegrationTests: XCTestCase {
106107
XCTAssertNil(error)
107108

108109
let unwrappedSigningResult = try XCTUnwrap(signingResult)
109-
let isSignatureVerified = api.verifySignature(
110-
signingResult: unwrappedSigningResult,
111-
serviceAccountToken: serviceAccountToken,
112-
projectId: projectId,
113-
projectURL: projectURL
110+
XCTAssertEqual(messageHash.hex, unwrappedSigningResult.signature.signatureHash)
111+
112+
let verifySigningResponse = try XCTUnwrap(
113+
api.verifySignature(
114+
signingResult: unwrappedSigningResult,
115+
serviceAccountToken: serviceAccountToken,
116+
projectId: projectId,
117+
projectURL: projectURL
118+
)
114119
)
115120

116-
XCTAssertTrue(isSignatureVerified)
121+
let jwks = try String(contentsOf: XCTUnwrap(URL(string: "\(projectURL)/dvs/jwks")))
122+
let signers = JWTSigners()
123+
try signers.use(jwksJSON: jwks)
124+
let payload = try signers.verify(verifySigningResponse.certificate, as: SignatureCertificateJWTPayload.self)
125+
126+
XCTAssertEqual(messageHash.hex, payload.hash)
117127
}
118128

119129
func testSigningCorrectnessWithSessionDetails() throws {
130+
let hash = messageHash.hex
120131
let qrCode = try XCTUnwrap(
121132
api.startSigningSession(
122133
projectID: projectId,
123134
projectURL: projectURL,
124135
userID: userId,
125-
hash: UUID().uuidString,
136+
hash: hash,
126137
description: "Test transaction"
127138
)
128139
)
@@ -140,14 +151,23 @@ class SigningIntegrationTests: XCTestCase {
140151
XCTAssertNil(error)
141152

142153
let unwrappedSigningResult = try XCTUnwrap(signingResult)
143-
let isSignatureVerified = api.verifySignature(
144-
signingResult: unwrappedSigningResult,
145-
serviceAccountToken: serviceAccountToken,
146-
projectId: projectId,
147-
projectURL: projectURL
154+
XCTAssertEqual(hash, unwrappedSigningResult.signature.signatureHash)
155+
156+
let verifySigningResponse = try XCTUnwrap(
157+
api.verifySignature(
158+
signingResult: unwrappedSigningResult,
159+
serviceAccountToken: serviceAccountToken,
160+
projectId: projectId,
161+
projectURL: projectURL
162+
)
148163
)
149164

150-
XCTAssertTrue(isSignatureVerified)
165+
let jwks = try String(contentsOf: XCTUnwrap(URL(string: "\(projectURL)/dvs/jwks")))
166+
let signers = JWTSigners()
167+
try signers.use(jwksJSON: jwks)
168+
let payload = try signers.verify(verifySigningResponse.certificate, as: SignatureCertificateJWTPayload.self)
169+
170+
XCTAssertEqual(messageHash.hex, payload.hash)
151171
}
152172

153173
func testSigningCorrectnessWithCrossDeviceSession() async throws {
@@ -183,15 +203,22 @@ class SigningIntegrationTests: XCTestCase {
183203
let timeInterval = TimeInterval(signature.timestamp)
184204
let date = Date(timeIntervalSince1970: timeInterval)
185205

186-
let isSignatureVerified = api.verifySignature(
187-
signature: signature,
188-
timestamp: date,
189-
serviceAccountToken: serviceAccountToken,
190-
projectId: projectId,
191-
projectURL: projectURL
206+
let verifySigningResponse = try XCTUnwrap(
207+
api.verifySignature(
208+
signature: signature,
209+
timestamp: date,
210+
serviceAccountToken: serviceAccountToken,
211+
projectId: projectId,
212+
projectURL: projectURL
213+
)
192214
)
193215

194-
XCTAssertTrue(isSignatureVerified)
216+
let jwks = try String(contentsOf: XCTUnwrap(URL(string: "\(projectURL)/dvs/jwks")))
217+
let signers = JWTSigners()
218+
try signers.use(jwksJSON: jwks)
219+
let payload = try signers.verify(verifySigningResponse.certificate, as: SignatureCertificateJWTPayload.self)
220+
221+
XCTAssertEqual(signature.signatureHash, payload.hash)
195222
}
196223

197224
func testSigningCorrectnessWithCrossDeviceSessionForUniversalLink() throws {
@@ -242,14 +269,23 @@ class SigningIntegrationTests: XCTestCase {
242269
XCTAssertNil(error)
243270

244271
let unwrappedSigningResult = try XCTUnwrap(signingResult)
245-
let isSignatureVerified = api.verifySignature(
246-
signingResult: unwrappedSigningResult,
247-
serviceAccountToken: serviceAccountToken,
248-
projectId: projectId,
249-
projectURL: projectURL
272+
XCTAssertEqual(messageHash.hex, unwrappedSigningResult.signature.signatureHash)
273+
274+
let verifySigningResponse = try XCTUnwrap(
275+
api.verifySignature(
276+
signingResult: unwrappedSigningResult,
277+
serviceAccountToken: serviceAccountToken,
278+
projectId: projectId,
279+
projectURL: projectURL
280+
)
250281
)
251282

252-
XCTAssertTrue(isSignatureVerified)
283+
let jwks = try String(contentsOf: XCTUnwrap(URL(string: "\(projectURL)/dvs/jwks")))
284+
let signers = JWTSigners()
285+
try signers.use(jwksJSON: jwks)
286+
let payload = try signers.verify(verifySigningResponse.certificate, as: SignatureCertificateJWTPayload.self)
287+
288+
XCTAssertEqual(messageHash.hex, payload.hash)
253289
}
254290

255291
func testSigningEmptyMessageHash() throws {

MIRACLTrust/MIRACLTrustIntegrationTests/Helpers/MIRACLTrustHelperAPI/HelperAPIModel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ struct VerifySigningRequestBody: Codable {
2525
var timestamp: Int32
2626
}
2727

28+
struct VerifySigningResponse: Codable {
29+
var certificate: String
30+
}
31+
2832
struct StartSessionResponse: Codable {
2933
var qrURL: URL
3034
var webOTT: String

MIRACLTrust/MIRACLTrustIntegrationTests/Helpers/MIRACLTrustHelperAPI/PlatformAPI.swift

Lines changed: 6 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -84,55 +84,6 @@ import MIRACLTrust
8484
}
8585
}
8686

87-
public func getJWKS(
88-
projectURL: String,
89-
completionHandler: @escaping @Sendable (String?, Error?) -> Void
90-
) {
91-
guard let url = URL(string: projectURL) else {
92-
return
93-
}
94-
95-
guard let request = URLRequest.jwksRequest(url: url) else {
96-
return
97-
}
98-
99-
let task = URLSession.shared.dataTask(with: request) { responseData, response, error in
100-
if error != nil {
101-
DispatchQueue.main.async {
102-
completionHandler(nil, HelperAPIError.internalError)
103-
}
104-
return
105-
}
106-
107-
if let response = response as? HTTPURLResponse {
108-
if response.statusCode != 200 {
109-
DispatchQueue.main.async {
110-
completionHandler(nil, HelperAPIError.internalError)
111-
}
112-
return
113-
}
114-
}
115-
116-
guard let data = responseData else {
117-
DispatchQueue.main.async {
118-
completionHandler(nil, HelperAPIError.noData)
119-
}
120-
return
121-
}
122-
123-
if data.isEmpty {
124-
DispatchQueue.main.async {
125-
completionHandler(nil, nil)
126-
}
127-
return
128-
}
129-
130-
completionHandler(String(data: data, encoding: String.Encoding.utf8), nil)
131-
}
132-
133-
task.resume()
134-
}
135-
13687
public func accessRequest(
13788
projectURL: String,
13889
webOTT: String,
@@ -189,13 +140,13 @@ import MIRACLTrust
189140
}
190141
}
191142

192-
public func verifySignature(
143+
func verifySignature(
193144
for signature: Signature,
194145
timestamp: Date,
195146
serviceAccountToken: String,
196147
projectId: String,
197148
projectURL: String,
198-
completionHandler: @escaping @Sendable (Bool, Error?) -> Void
149+
completionHandler: @escaping @Sendable (VerifySigningResponse?, Error?) -> Void
199150
) {
200151
guard let url = URL(string: projectURL) else {
201152
return
@@ -205,12 +156,12 @@ import MIRACLTrust
205156
return
206157
}
207158

208-
requestExecutor.executeHTTPRequest(request: request) { (result: Result<EmptyResponse?, HelperAPIError>) in
159+
requestExecutor.executeHTTPRequest(request: request) { (result: Result<VerifySigningResponse?, HelperAPIError>) in
209160
switch result {
210-
case .success:
211-
completionHandler(true, nil)
161+
case let .success(success):
162+
completionHandler(success, nil)
212163
case let .failure(error):
213-
completionHandler(false, error)
164+
completionHandler(nil, error)
214165
}
215166
}
216167
}

MIRACLTrust/MIRACLTrustIntegrationTests/Helpers/MIRACLTrustHelperAPI/URLRequest+Extensions.swift

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -168,20 +168,6 @@ extension URLRequest {
168168
return request
169169
}
170170

171-
static func jwksRequest(url: URL) -> Self? {
172-
guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
173-
return nil
174-
}
175-
176-
components.path = "/.well-known/jwks"
177-
178-
guard let url = components.url else {
179-
return nil
180-
}
181-
182-
return URLRequest(url: url)
183-
}
184-
185171
static func signingSessionRequest(
186172
url: URL,
187173
projectID: String,

MIRACLTrust/MIRACLTrustIntegrationTests/Helpers/PlatformAPIWrapper.swift

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,6 @@ import MIRACLTrust
3535
return verificationUrl
3636
}
3737

38-
@objc func getJWKS(projectURL: String) -> String? {
39-
let jwksExpectation = XCTestExpectation(description: "wait for JWKS")
40-
nonisolated(unsafe) var jwkSet: String?
41-
42-
platformAPI.getJWKS(projectURL: projectURL) { jwks, error in
43-
if let jwks = jwks {
44-
jwkSet = jwks
45-
} else if let error = error {
46-
print("Error when fetching JWKS \(error)")
47-
}
48-
jwksExpectation.fulfill()
49-
}
50-
_ = XCTWaiter.wait(for: [jwksExpectation], timeout: operationTimeout)
51-
return jwkSet
52-
}
53-
5438
@objc func startSession(
5539
projectId: String,
5640
projectURL: String,
@@ -108,13 +92,13 @@ import MIRACLTrust
10892
return qrCode
10993
}
11094

111-
@objc func verifySignature(
95+
func verifySignature(
11296
signingResult: SigningResult,
11397
serviceAccountToken: String,
11498
projectId: String,
11599
projectURL: String
116-
) -> Bool {
117-
nonisolated(unsafe) var verifiedSignature = false
100+
) -> VerifySigningResponse? {
101+
nonisolated(unsafe) var verifySigningResponse: VerifySigningResponse?
118102
let expectation = XCTestExpectation(description: "Waiting for signature verification")
119103

120104
platformAPI.verifySignature(
@@ -123,23 +107,23 @@ import MIRACLTrust
123107
serviceAccountToken: serviceAccountToken,
124108
projectId: projectId,
125109
projectURL: projectURL
126-
) { isVerified, _ in
127-
verifiedSignature = isVerified
110+
) { signingResponse, _ in
111+
verifySigningResponse = signingResponse
128112
expectation.fulfill()
129113
}
130114
_ = XCTWaiter.wait(for: [expectation], timeout: operationTimeout)
131115

132-
return verifiedSignature
116+
return verifySigningResponse
133117
}
134118

135-
@objc func verifySignature(
119+
func verifySignature(
136120
signature: Signature,
137121
timestamp: Date,
138122
serviceAccountToken: String,
139123
projectId: String,
140124
projectURL: String
141-
) -> Bool {
142-
nonisolated(unsafe) var verifiedSignature = false
125+
) -> VerifySigningResponse? {
126+
nonisolated(unsafe) var verifySigningResponse: VerifySigningResponse?
143127
let expectation = XCTestExpectation(description: "Waiting for signature verification")
144128

145129
platformAPI.verifySignature(
@@ -148,13 +132,13 @@ import MIRACLTrust
148132
serviceAccountToken: serviceAccountToken,
149133
projectId: projectId,
150134
projectURL: projectURL
151-
) { isVerified, _ in
152-
verifiedSignature = isVerified
135+
) { signingResponse, _ in
136+
verifySigningResponse = signingResponse
153137
expectation.fulfill()
154138
}
155139
_ = XCTWaiter.wait(for: [expectation], timeout: operationTimeout)
156140

157-
return verifiedSignature
141+
return verifySigningResponse
158142
}
159143

160144
func getVerificationURL(
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import JWTKit
2+
3+
struct SignatureCertificateJWTPayload: JWTPayload {
4+
var cAt: IssuedAtClaim
5+
var exp: ExpirationClaim
6+
var hash: String
7+
8+
func verify(using _: JWTKit.JWTSigner) throws {
9+
try exp.verifyNotExpired()
10+
}
11+
}

0 commit comments

Comments
 (0)