From dc6c9f8fc4bf5e4b44cc9735826c9deaa37a7874 Mon Sep 17 00:00:00 2001 From: Frank Rotermund Date: Thu, 25 Jul 2024 12:51:43 +0200 Subject: [PATCH] feat: add JWT Support to MDM and refacture (MESSENGER-6162) --- Config/BWIBuildSettings.swift | 6 ++- .../MXKAuthenticationViewController.m | 12 +----- Riot/target-bum-beta.yml | 1 + Riot/target-messenger.yml | 1 + RiotNSE/target.yml | 1 + .../AuthenticationServerSelectionScreen.swift | 30 ++------------ bwi/AppConfig/AppConfigService.swift | 18 +++++---- .../LoginProtectionService.swift | 33 +++++++++++++--- bwi/TokenVerification/TokenVerificator.swift | 39 ++----------------- 9 files changed, 53 insertions(+), 88 deletions(-) diff --git a/Config/BWIBuildSettings.swift b/Config/BWIBuildSettings.swift index 934d8c5e7..fc659e431 100644 --- a/Config/BWIBuildSettings.swift +++ b/Config/BWIBuildSettings.swift @@ -173,8 +173,10 @@ class BWIBuildSettings: NSObject { var bwiHashes = [ "a3f65e35a7476799afe8d80282fb3c45b39dab06d1d8c70dc98e45ab7d8e93a9", "2fda1a831655c22a5e6096d7cfbff4429fbf27891141e191b46adbf168142a11", "4f8cbb3fef885f7284d0477d797d7007f0e1ba76221834132752f4d645796e28", - /* "24c2ec541e61e8e68944b96dc45ed5df12f6bdbda283cb0b3a522742aa970256", remove internal test server that is handled by token verification*/ - "1be0b314a6c915d4475290522baef5b642db1b6d68937792b8e0eb5b7b0d6666", + /* "24c2ec541e61e8e68944b96dc45ed5df12f6bdbda283cb0b3a522742aa970256", + "1be0b314a6c915d4475290522baef5b642db1b6d68937792b8e0eb5b7b0d6666", + remove internal test server that is handled by token verification*/ + "3deb73db8cafcd1d5a59e25e251c35816162e1f6ee67b5d7d011da0e8d6ef931", "42e57985d61202c2c7dd87d898cef9bdce020877a4c7a8c7cd699f6a28f58c0c", "e1c3c7cac12bd65bd48de79a2677187d2e768d2769377627534023588b8d7a33", diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m index 35dfd8271..920766584 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m @@ -978,17 +978,7 @@ if (sender == _submitButton) { - if (BWIBuildSettings.shared.bwiEnableLoginProtection) { - LoginProtectionService* service = [[LoginProtectionService alloc] init]; - service.hashes = BWIBuildSettings.shared.bwiHashes; - if (![service isValid:self->mxRestClient.homeserver]) { - NSError *error = [[NSError alloc] initWithDomain:@"LoginProtectionError" code:0 userInfo:@{ - NSLocalizedDescriptionKey: [BWIL10n bwiLoginProtectionErrorMessage:AppInfo.current.displayName]}]; - [self onFailureDuringAuthRequest:error]; - return; - } - } - + // Disable user interaction to prevent multiple requests self.userInteractionEnabled = NO; diff --git a/Riot/target-bum-beta.yml b/Riot/target-bum-beta.yml index 066ba790d..190fb729a 100644 --- a/Riot/target-bum-beta.yml +++ b/Riot/target-bum-beta.yml @@ -109,3 +109,4 @@ targets: - path: Assets/de.lproj/Vector.strings - path: Assets/de.lproj/Bwi.strings - path: ../publickeys + type: folder diff --git a/Riot/target-messenger.yml b/Riot/target-messenger.yml index cd78a43b6..f18ce440d 100644 --- a/Riot/target-messenger.yml +++ b/Riot/target-messenger.yml @@ -95,3 +95,4 @@ targets: - path: Assets/de.lproj/Vector.strings - path: Assets/de.lproj/Bwi.strings - path: ../publickeys + type: folder diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index ae459e641..9718733cf 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -68,6 +68,7 @@ targets: - path: ../Config/BuM/BWIBuildSettings+BuM.swift - path: ../bwi/UserAgent/UserAgentService.swift - path: ../bwi/LoginProtection/LoginProtectionService.swift + - path: ../bwi/TokenVerification/TokenVerificator.swift - path: ../Config/MDMSettings.swift - path: ../Riot/Utils/DataProtectionHelper.swift - path: ../Config/CommonConfiguration.swift diff --git a/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift b/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift index 3dd599406..a8891f85d 100644 --- a/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift +++ b/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift @@ -226,37 +226,15 @@ struct AuthenticationServerSelectionScreen: View { } private func isHomeserverAddressValid(_ homeserverAddress: String) async -> Bool { - - // bwi #6162 a homeserveraddress is valid when there is either - // a) no homeserver protection (bwm) - // b) tokenized protection and there is a valid token - // c) hashed protection and there is a valid hash (this will be disabled soon) - // d) b) && c) can be combined for now - if !BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection && !BWIBuildSettings.shared.bwiEnableLoginProtection { - return true - } - - var validHomeserver = false - - if BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection { - - let tokenVerificator = ServerTokenVerificator() - - let token = await tokenVerificator.fetchToken(baseURL: homeserverAddress) - - if let token = token { - validHomeserver = tokenVerificator.verifyToken(baseURL: homeserverAddress, token: token) - } - } - - if BWIBuildSettings.shared.bwiEnableLoginProtection && !validHomeserver { + + if BWIBuildSettings.shared.bwiEnableLoginProtection || BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection { let protectionService = LoginProtectionService() protectionService.hashes = BWIBuildSettings.shared.bwiHashes - validHomeserver = protectionService.isValid(homeserverAddress) + return await protectionService.isValid(homeserverAddress) } - return validHomeserver + return true } /// bwi: jump directly into the iOS settings app to allow camera access diff --git a/bwi/AppConfig/AppConfigService.swift b/bwi/AppConfig/AppConfigService.swift index 48b60bade..981fe208c 100644 --- a/bwi/AppConfig/AppConfigService.swift +++ b/bwi/AppConfig/AppConfigService.swift @@ -76,12 +76,12 @@ extension UserDefaults } } - private func checkUrlSavety(_ serverUrl: String) -> Bool { + private func checkUrlSavety(_ serverUrl: String) async -> Bool { if BWIBuildSettings.shared.bwiEnableLoginProtection { let protectionService = LoginProtectionService() protectionService.hashes = BWIBuildSettings.shared.bwiHashes - return protectionService.isValid(serverUrl) + return await protectionService.isValid(serverUrl) } else { return true } @@ -112,11 +112,13 @@ extension UserDefaults func registerForAppConfig() { NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: OperationQueue.main) { [self] (note) in - handleAppConfig() + Task { + await handleAppConfig() + } } } - func handleAppConfig() { + func handleAppConfig() async { if let dict = UserDefaults.standard.dictionary(forKey: configKey) { // only compute if serverURL has not changed (this may need to be changed on Adminportal integration if !isSameConfig(dict: dict) { @@ -125,22 +127,22 @@ extension UserDefaults if let serverUrl = dict[serverUrlKey] as? String { if serverUrl.count == 0 { config.serverUrl = nil - } else if checkUrlSavety(serverUrl) { + } else if await checkUrlSavety(serverUrl) { config.serverUrl = serverUrl } } if let contentScannerUrl = dict[contentScannerKey] as? String { - if checkUrlSavety(contentScannerUrl) { + if await checkUrlSavety(contentScannerUrl) { config.contentScannerUrl = contentScannerUrl } } if let pusherUrl = dict[pusherUrlKey] as? String { - if checkUrlSavety(pusherUrl) { + if await checkUrlSavety(pusherUrl) { config.pusherUrl = pusherUrl } } if let permalinkUrl = dict[permalinkUrlKey] as? String { - if checkUrlSavety(permalinkUrl) { + if await checkUrlSavety(permalinkUrl) { config.permalinkUrl = permalinkUrl } } diff --git a/bwi/LoginProtection/LoginProtectionService.swift b/bwi/LoginProtection/LoginProtectionService.swift index d1f34cbf9..583db3e4f 100644 --- a/bwi/LoginProtection/LoginProtectionService.swift +++ b/bwi/LoginProtection/LoginProtectionService.swift @@ -21,15 +21,36 @@ import CryptoKit @objcMembers class LoginProtectionService : NSObject { var hashes: [String]? - @objc func isValid(_ urlString: String) -> Bool { - guard let hashes = hashes else { - return false + @objc func isValid(_ homeserverAddress: String) async -> Bool { + // bwi #6162 a homeserveraddress is valid when there is either + // a) no homeserver protection (bwm) + // b) tokenized protection and there is a valid token + // c) hashed protection and there is a valid hash (this will be disabled soon) + // d) b) && c) can be combined for now + + var validHomeserver = false + + if BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection { + + let tokenVerificator = ServerTokenVerificator() + + let token = await tokenVerificator.fetchToken(baseURL: homeserverAddress) + + if let token = token { + validHomeserver = tokenVerificator.verifyToken(baseURL: homeserverAddress, token: token) + } } - let string = self.normalizeLoginUrl(urlString) - let hashedString = self.hashedString(string) + if BWIBuildSettings.shared.bwiEnableLoginProtection && !validHomeserver { + if let hashes = hashes { + let string = self.normalizeLoginUrl(homeserverAddress) + let hashedString = self.hashedString(string) + + validHomeserver = hashes.contains(hashedString) + } + } - return hashes.contains(hashedString) + return validHomeserver } private func normalizeLoginUrl(_ urlString: String) -> String { diff --git a/bwi/TokenVerification/TokenVerificator.swift b/bwi/TokenVerification/TokenVerificator.swift index 0af2be050..19247ec36 100644 --- a/bwi/TokenVerification/TokenVerificator.swift +++ b/bwi/TokenVerification/TokenVerificator.swift @@ -16,40 +16,8 @@ */ import Foundation - -/* - let publicKey: Data = publicKeyStr.data(using: .utf8)! - - struct MyClaims: Claims { - let version: Int - let hostname: String - } - - let signedJWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJob3N0bmFtZSI6ImJ3aS5kZSJ9.bondtDczLlOHlHLLZj1C9tn60LqBpIhFNaUy6nYL6CVVwWGIv8EIxYWMTx-MP9OSjj-aeMcy0tmDqSz6nbdbgvUJvkB6r-ByH7fTsVj6OtEEs8mWnqHBOFBwTy9tv5vSTfjFX7PBSko2OK3HQrZkFSkfr-xZoOIc_PxblUnec2hClxVq7ImJnIAW1HCh85DMz2c-MiEHd7wQwBcgwWKWmAY9X6uS25WWhQcPH9i0-QMEQNjXGJp-_wM10KJuuOMDx7QdmcX78QgcOyP-G64cA36NL4-6Aby5EnJUDX-uzFbM_ZERgPVmjfzHoZarFCHSK6-fTBg_MQuDF-O2OOdM6Q" - - let jwtVerifier = JWTVerifier.rs256(publicKey: publicKey) - - - - do { - let verified = JWT.verify(signedJWT, using: jwtVerifier) - let newJWT = try JWT(jwtString: signedJWT, verifier: jwtVerifier) - print(newJWT.claims) - } catch let error { - print(error.localizedDescription) - } - */ - import SwiftJWT -/* - - "sub": "1234567890", - "name": "John Doe", - "admin": true, - "iat": 1516239022 - */ - struct ServerTokenClaims: Claims { let issuer: String let sub: String @@ -83,12 +51,13 @@ struct ServerTokenVerificator { if verified && (validated == .success) && matchingHomeServer { return true } - } catch let error { + } catch { // counts like an unverified Token } } - return true + // no public key was able to verify the token + return false } // fetch the current token from the new endpoint, @@ -104,7 +73,7 @@ struct ServerTokenVerificator { config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData let session : URLSession = URLSession(configuration: config) - let (data, response) = try await session.data(from: url) + let (data, _) = try await session.data(from: url) // the token may have additional endlines if let str = String(data: data, encoding: .utf8) {