From 63efef5fc4445db0fb8e6264001ad82fc5890d53 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Wed, 8 Oct 2025 13:24:33 +0300 Subject: [PATCH] Use EC signature configuration MOPPIOS-1493 Signed-off-by: Raul Metsma --- .gitignore | 4 +- MoppApp/MoppApp.xcodeproj/project.pbxproj | 16 +++---- MoppApp/MoppApp/SettingsConfiguration.swift | 10 ++--- MoppApp/MoppApp/SignatureVerifier.swift | 34 +++++++------- MoppApp/SetupConfiguration/Package.swift | 18 +++----- MoppApp/SetupConfiguration/Sources/main.swift | 44 +++++++++++-------- 6 files changed, 67 insertions(+), 59 deletions(-) diff --git a/.gitignore b/.gitignore index 841541d6d..d80087af5 100644 --- a/.gitignore +++ b/.gitignore @@ -34,5 +34,5 @@ xcuserdata MoppApp/GoogleService-Info.plist MoppApp/MoppApp/config.json MoppApp/MoppApp/defaultConfiguration.json -MoppApp/MoppApp/publicKey.pub -MoppApp/MoppApp/signature.rsa +MoppApp/MoppApp/publicKey.ecpub +MoppApp/MoppApp/signature.ecc diff --git a/MoppApp/MoppApp.xcodeproj/project.pbxproj b/MoppApp/MoppApp.xcodeproj/project.pbxproj index 266713a25..7af57fb32 100644 --- a/MoppApp/MoppApp.xcodeproj/project.pbxproj +++ b/MoppApp/MoppApp.xcodeproj/project.pbxproj @@ -248,9 +248,9 @@ DFF3C3B22332314A0079458A /* RuntimeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF3C3B02332314A0079458A /* RuntimeError.swift */; }; DFF3C3B32332314A0079458A /* SignatureVerifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF3C3B12332314A0079458A /* SignatureVerifier.swift */; }; DFF3C3B5233231F20079458A /* Notification+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF3C3B4233231F20079458A /* Notification+Additions.swift */; }; - DFF3C3BC2332355D0079458A /* publicKey.pub in Resources */ = {isa = PBXBuildFile; fileRef = DFF3C3B82332355C0079458A /* publicKey.pub */; }; + DFF3C3BC2332355D0079458A /* publicKey.ecpub in Resources */ = {isa = PBXBuildFile; fileRef = DFF3C3B82332355C0079458A /* publicKey.ecpub */; }; DFF3C3BD2332355D0079458A /* config.json in Resources */ = {isa = PBXBuildFile; fileRef = DFF3C3B92332355C0079458A /* config.json */; }; - DFF3C3BE2332355D0079458A /* signature.rsa in Resources */ = {isa = PBXBuildFile; fileRef = DFF3C3BA2332355C0079458A /* signature.rsa */; }; + DFF3C3BE2332355D0079458A /* signature.ecc in Resources */ = {isa = PBXBuildFile; fileRef = DFF3C3BA2332355C0079458A /* signature.ecc */; }; DFF3C3BF2332355D0079458A /* defaultConfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = DFF3C3BB2332355D0079458A /* defaultConfiguration.json */; }; E4250CCB1E0968D200530370 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4250CCA1E0968D200530370 /* AppDelegate.swift */; }; E4250D0A1E0AA7AF00530370 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = E4250D091E0AA7AF00530370 /* LaunchScreen.xib */; }; @@ -570,9 +570,9 @@ DFF3C3B02332314A0079458A /* RuntimeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeError.swift; sourceTree = ""; }; DFF3C3B12332314A0079458A /* SignatureVerifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignatureVerifier.swift; sourceTree = ""; }; DFF3C3B4233231F20079458A /* Notification+Additions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Notification+Additions.swift"; sourceTree = ""; }; - DFF3C3B82332355C0079458A /* publicKey.pub */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = publicKey.pub; path = MoppApp/publicKey.pub; sourceTree = ""; }; + DFF3C3B82332355C0079458A /* publicKey.ecpub */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = publicKey.ecpub; path = MoppApp/publicKey.ecpub; sourceTree = ""; }; DFF3C3B92332355C0079458A /* config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = config.json; path = MoppApp/config.json; sourceTree = ""; }; - DFF3C3BA2332355C0079458A /* signature.rsa */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = signature.rsa; path = MoppApp/signature.rsa; sourceTree = ""; }; + DFF3C3BA2332355C0079458A /* signature.ecc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = signature.ecc; path = MoppApp/signature.ecc; sourceTree = ""; }; DFF3C3BB2332355D0079458A /* defaultConfiguration.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = defaultConfiguration.json; path = MoppApp/defaultConfiguration.json; sourceTree = ""; }; DFF6A55627E14D3B0055F8D5 /* RoleDetailsViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoleDetailsViewControllerTests.swift; sourceTree = ""; }; E4250CC31E0968D200530370 /* MoppApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MoppApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1048,8 +1048,8 @@ DFB1D5EA2ACAFE1B00FDC7B8 /* GoogleService-Info.plist */, DFF3C3B92332355C0079458A /* config.json */, DFF3C3BB2332355D0079458A /* defaultConfiguration.json */, - DFF3C3B82332355C0079458A /* publicKey.pub */, - DFF3C3BA2332355C0079458A /* signature.rsa */, + DFF3C3B82332355C0079458A /* publicKey.ecpub */, + DFF3C3BA2332355C0079458A /* signature.ecc */, E4250CC51E0968D200530370 /* MoppApp */, 54A418251E83FAD200559E2B /* shareExtension */, DFF6A54E27E14C180055F8D5 /* MoppAppTests */, @@ -1378,8 +1378,8 @@ DFB1D5EB2ACAFE1B00FDC7B8 /* GoogleService-Info.plist in Resources */, DFF3C3BF2332355D0079458A /* defaultConfiguration.json in Resources */, DFF3C3BD2332355D0079458A /* config.json in Resources */, - DFF3C3BE2332355D0079458A /* signature.rsa in Resources */, - DFF3C3BC2332355D0079458A /* publicKey.pub in Resources */, + DFF3C3BE2332355D0079458A /* signature.ecc in Resources */, + DFF3C3BC2332355D0079458A /* publicKey.ecpub in Resources */, DF900C972386768F00887385 /* tslFiles.bundle in Resources */, DFC2ADC329437790008A1CD2 /* Accessibility.storyboard in Resources */, C50DCD011FC6E97400D48E16 /* Container.storyboard in Resources */, diff --git a/MoppApp/MoppApp/SettingsConfiguration.swift b/MoppApp/MoppApp/SettingsConfiguration.swift index c38ca4595..947a3511a 100644 --- a/MoppApp/MoppApp/SettingsConfiguration.swift +++ b/MoppApp/MoppApp/SettingsConfiguration.swift @@ -102,7 +102,7 @@ class SettingsConfiguration: NSObject, URLSessionDelegate, URLSessionTaskDelegat internal func loadLocalConfiguration() { do { let localConfigData = try String(contentsOfFile: Bundle.main.path(forResource: "config", ofType: "json")!) - let localSignature = try String(contentsOfFile: Bundle.main.path(forResource: "signature", ofType: "rsa")!) + let localSignature = try String(contentsOfFile: Bundle.main.path(forResource: "signature", ofType: "ecc")!) let decodedData = try MOPPConfiguration(json: localConfigData) setAllConfigurationToCache(configData: localConfigData, signature: localSignature, initialUpdateDate: MoppDateFormatter().stringToDate(dateString: getDefaultMoppConfiguration().UPDATEDATE), versionSerial: decodedData.METAINF.SERIAL) setConfigurationToCache("", forKey: "lastUpdateDateCheck") @@ -119,7 +119,7 @@ class SettingsConfiguration: NSObject, URLSessionDelegate, URLSessionTaskDelegat internal func loadCachedConfiguration() { do { let cachedConfigData = getConfigurationFromCache(forKey: "config") as! String - let localPublicKey = try String(contentsOfFile: Bundle.main.path(forResource: "publicKey", ofType: "pub")!) + let localPublicKey = try String(contentsOfFile: Bundle.main.path(forResource: "publicKey", ofType: "ecpub")!) let cachedSignature = getConfigurationFromCache(forKey: "signature") as! String _ = try SignatureVerifier.isSignatureCorrect(configData: cachedConfigData, publicKey: localPublicKey, signature: cachedSignature) @@ -152,14 +152,14 @@ class SettingsConfiguration: NSObject, URLSessionDelegate, URLSessionTaskDelegat internal func loadCentralConfiguration(completionHandler: @escaping (Error) -> () = { _ in }) { do { let cachedSignature = getConfigurationFromCache(forKey: "signature") as? String - let localPublicKey = try String(contentsOfFile: Bundle.main.path(forResource: "publicKey", ofType: "pub")!) - + let localPublicKey = try String(contentsOfFile: Bundle.main.path(forResource: "publicKey", ofType: "ecpub")!) + if isInitialSetup() { setConfigurationToCache(true, forKey: "isCentralConfigurationLoaded") loadCachedConfiguration() } - getFetchedData(fromUrl: "\(getDefaultMoppConfiguration().CENTRALCONFIGURATIONSERVICEURL)/config.rsa") { (centralSignature, signatureError) in + getFetchedData(fromUrl: "\(getDefaultMoppConfiguration().CENTRALCONFIGURATIONSERVICEURL)/config.ecc") { (centralSignature, signatureError) in if let error = signatureError { printLog(error.localizedDescription) return completionHandler(error) diff --git a/MoppApp/MoppApp/SignatureVerifier.swift b/MoppApp/MoppApp/SignatureVerifier.swift index 412e07b7b..0500df7bd 100644 --- a/MoppApp/MoppApp/SignatureVerifier.swift +++ b/MoppApp/MoppApp/SignatureVerifier.swift @@ -21,7 +21,7 @@ * */ -import Security +import CryptoKit class SignatureVerifier { @@ -30,24 +30,28 @@ class SignatureVerifier { throw Exception("Invalid signature") } guard let pubKey = fromBase64(publicKey - .replacingOccurrences(of: "-----BEGIN RSA PUBLIC KEY-----", with: "") - .replacingOccurrences(of: "-----END RSA PUBLIC KEY-----", with: "")) else { + .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "") + .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")) else { throw Exception("Invalid public key") } - let parameters: [CFString: Any] = [ - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrKeyClass: kSecAttrKeyClassPublic, - kSecReturnPersistentRef: false - ] - var error: Unmanaged? - guard let key = SecKeyCreateWithData(pubKey as CFData, parameters as CFDictionary, &error) else { - printLog("Failed to create key: \(error!.takeRetainedValue())") - throw Exception("Failed to create key: \(error!.takeRetainedValue())") + let result: Bool + switch pubKey.count { + case 80...100: + let key = try P256.Signing.PublicKey(derRepresentation: pubKey) + let sig = try P256.Signing.ECDSASignature(derRepresentation: sigData) + result = key.isValidSignature(sig, for: Data(configData.utf8)) + case 110...130: + let key = try P384.Signing.PublicKey(derRepresentation: pubKey) + let sig = try P384.Signing.ECDSASignature(derRepresentation: sigData) + result = key.isValidSignature(sig, for: Data(configData.utf8)) + case 150...170: + let key = try P521.Signing.PublicKey(derRepresentation: pubKey) + let sig = try P521.Signing.ECDSASignature(derRepresentation: sigData) + result = key.isValidSignature(sig, for: Data(configData.utf8)) + default: + throw Exception("Unknown key size") } - let algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA512 - let result = SecKeyVerifySignature(key, algorithm, Data(configData.utf8) as CFData, sigData as CFData, &error) if !result { - print("Verification error: \(error!.takeRetainedValue())") throw Exception("Signature verification unsuccessful") } } diff --git a/MoppApp/SetupConfiguration/Package.swift b/MoppApp/SetupConfiguration/Package.swift index 03670b98d..f34da0587 100644 --- a/MoppApp/SetupConfiguration/Package.swift +++ b/MoppApp/SetupConfiguration/Package.swift @@ -1,14 +1,10 @@ // swift-tools-version:4.2 import PackageDescription -let package = Package(name: "SetupConfiguration") - -package.products = [ - .executable(name: "SetupConfiguration", targets: ["SetupConfiguration"]) -] -package.dependencies = [ - -] -package.targets = [ - .target(name: "SetupConfiguration", dependencies: [], path: "Sources") -] +let package = Package( + name: "SetupConfiguration", + platforms: [.macOS(.v11)], + products: [.executable(name: "SetupConfiguration", targets: ["SetupConfiguration"])], + dependencies: [], + targets: [.target(name: "SetupConfiguration", dependencies: [], path: "Sources")], +) diff --git a/MoppApp/SetupConfiguration/Sources/main.swift b/MoppApp/SetupConfiguration/Sources/main.swift index 3c85cdfb4..5beb26803 100644 --- a/MoppApp/SetupConfiguration/Sources/main.swift +++ b/MoppApp/SetupConfiguration/Sources/main.swift @@ -1,7 +1,7 @@ #!/usr/bin/swift sh import Foundation -import Security +import CryptoKit // MARK: - Settings Configuration @@ -26,8 +26,8 @@ class SettingsConfiguration { log("1 / 4 - Downloading configuration data...") let configData = try fetchData(from: "\(configBaseUrl)/config.json") - let publicKey = try fetchData(from: "\(configBaseUrl)/config.pub") - let signature = try fetchData(from: "\(configBaseUrl)/config.rsa") + let publicKey = try fetchData(from: "\(configBaseUrl)/config.ecpub") + let signature = try fetchData(from: "\(configBaseUrl)/config.ecc") log("2 / 4 - Verifying signature...") try verifySignature(configData: configData, publicKey: publicKey, signature: signature) @@ -38,8 +38,8 @@ class SettingsConfiguration { log("4 / 4 - Saving and moving files...") try saveFile(named: "config.json", content: configData) - try saveFile(named: "publicKey.pub", content: publicKey) - try saveFile(named: "signature.rsa", content: signature) + try saveFile(named: "publicKey.ecpub", content: publicKey) + try saveFile(named: "signature.ecc", content: signature) try saveFile(named: "defaultConfiguration.json", content: defaultConfiguration) log("Default configuration initialized successfully!") @@ -64,27 +64,35 @@ extension SettingsConfiguration { throw ConfigurationError.signatureVerificationFailed } guard let pubKey = Data(base64Encoded: publicKey - .replacingOccurrences(of: "-----BEGIN RSA PUBLIC KEY-----", with: "") - .replacingOccurrences(of: "-----END RSA PUBLIC KEY-----", with: ""), options: .ignoreUnknownCharacters) else { + .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "") + .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: ""), options: .ignoreUnknownCharacters) else { + log("Failed to parse key") throw ConfigurationError.signatureVerificationFailed } guard let sigData = Data(base64Encoded: signature, options: .ignoreUnknownCharacters) else { throw ConfigurationError.signatureVerificationFailed } - let parameters: [CFString: Any] = [ - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrKeyClass: kSecAttrKeyClassPublic, - kSecReturnPersistentRef: false - ] - var error: Unmanaged? - guard let key = SecKeyCreateWithData(pubKey as CFData, parameters as CFDictionary, &error) else { - log("Key importing failed \(error?.takeRetainedValue().localizedDescription ?? "")") + + let result: Bool + switch pubKey.count { + case 80...100: + let key = try P256.Signing.PublicKey(derRepresentation: pubKey) + let sig = try P256.Signing.ECDSASignature(derRepresentation: sigData) + result = key.isValidSignature(sig, for: Data(configData.utf8)) + case 110...130: + let key = try P384.Signing.PublicKey(derRepresentation: pubKey) + let sig = try P384.Signing.ECDSASignature(derRepresentation: sigData) + result = key.isValidSignature(sig, for: Data(configData.utf8)) + case 150...170: + let key = try P521.Signing.PublicKey(derRepresentation: pubKey) + let sig = try P521.Signing.ECDSASignature(derRepresentation: sigData) + result = key.isValidSignature(sig, for: Data(configData.utf8)) + default: + log("Unknown key size") throw ConfigurationError.signatureVerificationFailed } - let algorithm: SecKeyAlgorithm = .rsaSignatureMessagePKCS1v15SHA512 - let result = SecKeyVerifySignature(key, algorithm, Data(configData.utf8) as CFData, sigData as CFData, &error) if !result { - log("Signature verifying failed \(error?.takeRetainedValue().localizedDescription ?? "")") + log("Signature verifying failed") throw ConfigurationError.signatureVerificationFailed } log("Signature verified successfully!")