From 466e603abcc2551244e39471d43ad4df9a08e4d1 Mon Sep 17 00:00:00 2001 From: Joel-David Date: Thu, 25 Jul 2024 23:00:03 +0800 Subject: [PATCH] Fixed and added GA tests --- Chronos.xcodeproj/project.pbxproj | 4 ++ Chronos/Services/Import/ImportService.swift | 18 ++++-- ChronosTests/Import/GoogleAuthenticator.swift | 59 +++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 ChronosTests/Import/GoogleAuthenticator.swift diff --git a/Chronos.xcodeproj/project.pbxproj b/Chronos.xcodeproj/project.pbxproj index a4707f3..4b7c4e0 100644 --- a/Chronos.xcodeproj/project.pbxproj +++ b/Chronos.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ 6BC5F04A2C4FDE6E00BA106F /* ParseOtpAuthUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC5F0492C4FDE6E00BA106F /* ParseOtpAuthUrl.swift */; }; 6BC5F0522C52429100BA106F /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = 6BC5F0512C52429100BA106F /* SwiftProtobuf */; }; 6BC5F0552C52464600BA106F /* GoogleAuth.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC5F0542C5242B500BA106F /* GoogleAuth.pb.swift */; }; + 6BC5F0572C529A2A00BA106F /* GoogleAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC5F0562C529A2A00BA106F /* GoogleAuthenticator.swift */; }; 6BD6D2012C11FEB4004512BF /* OTPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BD6D2002C11FEB4004512BF /* OTPService.swift */; }; 6BD90AA52B8E34BB00FABD91 /* PasswordLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BD90AA42B8E34BB00FABD91 /* PasswordLoginView.swift */; }; 6BE122922BD6413D008636D2 /* ChronosCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE122912BD6413D008636D2 /* ChronosCrypto.swift */; }; @@ -142,6 +143,7 @@ 6BC3C3B42BA6B91E00B181B9 /* BiometricsSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricsSetupView.swift; sourceTree = ""; }; 6BC5F0492C4FDE6E00BA106F /* ParseOtpAuthUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseOtpAuthUrl.swift; sourceTree = ""; }; 6BC5F0542C5242B500BA106F /* GoogleAuth.pb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuth.pb.swift; sourceTree = ""; }; + 6BC5F0562C529A2A00BA106F /* GoogleAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuthenticator.swift; sourceTree = ""; }; 6BD6D2002C11FEB4004512BF /* OTPService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTPService.swift; sourceTree = ""; }; 6BD90AA42B8E34BB00FABD91 /* PasswordLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordLoginView.swift; sourceTree = ""; }; 6BE122912BD6413D008636D2 /* ChronosCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChronosCrypto.swift; sourceTree = ""; }; @@ -316,6 +318,7 @@ children = ( 6B4CBF2E2C490FB700983D44 /* Chronos.swift */, 6B8132F12C4975DA00DB367E /* Raivo.swift */, + 6BC5F0562C529A2A00BA106F /* GoogleAuthenticator.swift */, ); path = Import; sourceTree = ""; @@ -681,6 +684,7 @@ files = ( 6B8132FD2C4E5F6B00DB367E /* QrCodeGenerationAndParsing.swift in Sources */, 6B4CBF2F2C490FB700983D44 /* Chronos.swift in Sources */, + 6BC5F0572C529A2A00BA106F /* GoogleAuthenticator.swift in Sources */, 6BC5F04A2C4FDE6E00BA106F /* ParseOtpAuthUrl.swift in Sources */, 6B8132F22C4975DA00DB367E /* Raivo.swift in Sources */, 6B8132FA2C4C0F6300DB367E /* TokenToOtpAuthUrl.swift in Sources */, diff --git a/Chronos/Services/Import/ImportService.swift b/Chronos/Services/Import/ImportService.swift index 28ab995..aa42f55 100644 --- a/Chronos/Services/Import/ImportService.swift +++ b/Chronos/Services/Import/ImportService.swift @@ -62,7 +62,7 @@ extension ImportService { } } - private func importFromRaivo(json: JSON) -> [Token]? { + func importFromRaivo(json: JSON) -> [Token]? { var tokens: [Token] = [] for (key, subJson) in json { @@ -105,7 +105,7 @@ extension ImportService { return tokens } - private func importFromGoogleAuth(otpAuthMigration: String) -> [Token]? { + func importFromGoogleAuth(otpAuthMigration: String) -> [Token]? { guard let otpAuthMigrationUrl = URL(string: otpAuthMigration), let components = URLComponents(url: otpAuthMigrationUrl, resolvingAgainstBaseURL: false), let scheme = components.scheme, scheme == "otpauth-migration", @@ -155,17 +155,23 @@ extension ImportService { default: tokenAlgo = TokenAlgorithmEnum.SHA1 } - + let token = Token() token.issuer = gaToken.issuer token.account = gaToken.name token.digits = tokenDigits - token.period = 30 - token.counter = Int(gaToken.counter) token.type = tokenType token.algorithm = tokenAlgo token.secret = gaToken.secret.base32EncodedString - + + if tokenType == .TOTP { + token.period = 30 // GA only allows 30 secs + } + + if tokenType == .HOTP { + token.counter = Int(gaToken.counter) + } + tokens.append(token) } diff --git a/ChronosTests/Import/GoogleAuthenticator.swift b/ChronosTests/Import/GoogleAuthenticator.swift new file mode 100644 index 0000000..e13f960 --- /dev/null +++ b/ChronosTests/Import/GoogleAuthenticator.swift @@ -0,0 +1,59 @@ +@testable import Chronos +import SwiftyJSON +import XCTest + +final class GoogleAuthenticatorTests: XCTestCase { + func testValidImport() throws { + let authOtpMigratation = "otpauth-migration://offline?data=Ci0KCkhlbGxvId6tvu8SEmpvaG5AYXBwbGVzZWVkLmNvbRoFQXBwbGUgASgBMAIKLgoKSGVsbG8h3q2%2B7xITam9objJAYXBwbGVzZWVkLmNvbRoFQXBwbGUgAigCMAIKNAoKSGVsbG8h3q2%2B7xIXam9obitob3RwQGFwcGxlc2VlZC5jb20aBUFwcGxlIAEoATABOAAKNQoKSGVsbG8h3q2%2B7xIYam9obitob3RwMkBhcHBsZXNlZWQuY29tGgVBcHBsZSABKAIwATgAEAIYASAA" + + let importService = ImportService() + let tokens = importService.importFromGoogleAuth(otpAuthMigration: authOtpMigratation)! + + XCTAssertEqual(tokens.count, 4) + + XCTAssertEqual(tokens[0].digits, 6) + XCTAssertEqual(tokens[0].type, TokenTypeEnum.TOTP) + XCTAssertEqual(tokens[0].counter, 0) + XCTAssertEqual(tokens[0].algorithm, TokenAlgorithmEnum.SHA1) + XCTAssertEqual(tokens[0].issuer, "Apple") + XCTAssertEqual(tokens[0].account, "john@appleseed.com") + XCTAssertEqual(tokens[0].period, 30) + XCTAssertEqual(tokens[0].secret, "JBSWY3DPEHPK3PXP") + + XCTAssertEqual(tokens[1].digits, 8) + XCTAssertEqual(tokens[1].type, TokenTypeEnum.TOTP) + XCTAssertEqual(tokens[1].counter, 0) + XCTAssertEqual(tokens[1].algorithm, TokenAlgorithmEnum.SHA256) + XCTAssertEqual(tokens[1].issuer, "Apple") + XCTAssertEqual(tokens[1].account, "john2@appleseed.com") + XCTAssertEqual(tokens[1].period, 30) + XCTAssertEqual(tokens[1].secret, "JBSWY3DPEHPK3PXP") + + XCTAssertEqual(tokens[2].digits, 6) + XCTAssertEqual(tokens[2].type, TokenTypeEnum.HOTP) + XCTAssertEqual(tokens[2].counter, 0) + XCTAssertEqual(tokens[2].algorithm, TokenAlgorithmEnum.SHA1) + XCTAssertEqual(tokens[2].issuer, "Apple") + XCTAssertEqual(tokens[2].account, "john+hotp@appleseed.com") + XCTAssertEqual(tokens[2].counter, 0) + XCTAssertEqual(tokens[2].secret, "JBSWY3DPEHPK3PXP") + + XCTAssertEqual(tokens[3].digits, 8) + XCTAssertEqual(tokens[3].type, TokenTypeEnum.HOTP) + XCTAssertEqual(tokens[3].counter, 0) + XCTAssertEqual(tokens[3].algorithm, TokenAlgorithmEnum.SHA1) + XCTAssertEqual(tokens[3].issuer, "Apple") + XCTAssertEqual(tokens[3].account, "john+hotp2@appleseed.com") + XCTAssertEqual(tokens[3].counter, 0) + XCTAssertEqual(tokens[3].secret, "JBSWY3DPEHPK3PXP") + } + + func testInvalidImport_AlgoMD5() throws { + let authOtpMigratation = "otpauth-migration://offline?data=Ci0KCkhlbGxvId6tvu8SEmpvaG5AYXBwbGVzZWVkLmNvbRoFQXBwbGUgASgBMAIKLgoKSGVsbG8h3q2%2B6RIRbWQ1QGFwcGxlc2VlZC5jb20aBUFwcGxlIAQoATABOAAQAhgBIAA%3D" + + let importService = ImportService() + let tokens = importService.importFromGoogleAuth(otpAuthMigration: authOtpMigratation) + + XCTAssertNil(tokens, "Should fail fast if any tokens contains md5 algo") + } +}