diff options
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 50 | ||||
| -rw-r--r-- | ios/MullvadVPN/Keychain/Keychain.swift | 154 | ||||
| -rw-r--r-- | ios/MullvadVPN/Keychain/KeychainAttributes.swift | 139 | ||||
| -rw-r--r-- | ios/MullvadVPN/Keychain/KeychainClass.swift | 50 | ||||
| -rw-r--r-- | ios/MullvadVPN/Keychain/KeychainError.swift | 32 | ||||
| -rw-r--r-- | ios/MullvadVPN/Keychain/KeychainMatchLimit.swift | 48 | ||||
| -rw-r--r-- | ios/MullvadVPN/Keychain/KeychainReturn.swift | 57 | ||||
| -rw-r--r-- | ios/MullvadVPN/KeychainError.swift | 25 |
8 files changed, 26 insertions, 529 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index ad36d1046a..7517459efb 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -8,13 +8,7 @@ /* Begin PBXBuildFile section */ 5801C9A527A14B2A0031566A /* TunnelManagerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5801C9A427A14B2A0031566A /* TunnelManagerState.swift */; }; - 5806766D27048E5500C858CB /* KeychainMatchLimit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDFC24533A5500CB0F5B /* KeychainMatchLimit.swift */; }; - 5806766E27048E5600C858CB /* KeychainMatchLimit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDFC24533A5500CB0F5B /* KeychainMatchLimit.swift */; }; - 5806767927048E8800C858CB /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDF6245088E100CB0F5B /* Keychain.swift */; }; - 5806767A27048E8800C858CB /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDF6245088E100CB0F5B /* Keychain.swift */; }; - 5806767B27048E8900C858CB /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDF6245088E100CB0F5B /* Keychain.swift */; }; 5806767C27048E9B00C858CB /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CE5E7B224146470008646E /* PacketTunnelProvider.swift */; }; - 5806768127048EE000C858CB /* KeychainMatchLimit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDFC24533A5500CB0F5B /* KeychainMatchLimit.swift */; }; 5807483B27DB8A980020ECBF /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 5807483A27DB8A980020ECBF /* WireGuardKitTypes */; }; 5807E2C02432038B00F5FF30 /* String+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2BF2432038B00F5FF30 /* String+Split.swift */; }; 5807E2C2243203D000F5FF30 /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2C1243203D000F5FF30 /* StringTests.swift */; }; @@ -194,9 +188,6 @@ 5891BF1C25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */; }; 5891BF5125E66B1E006D6FB0 /* UIBarButtonItem+KeyboardNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF5025E66B1E006D6FB0 /* UIBarButtonItem+KeyboardNavigation.swift */; }; 5892A45E265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5892A45D265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift */; }; - 5896AE7E246ACE65005B36CB /* KeychainAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */; }; - 5896AE80246ACE79005B36CB /* KeychainClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */; }; - 5896AE82246ACE84005B36CB /* KeychainReturn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDFE24533A7000CB0F5B /* KeychainReturn.swift */; }; 5896AE84246D5889005B36CB /* CustomDateComponentsFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */; }; 5896AE86246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */; }; 5896AE88246D7FAF005B36CB /* CustomDateComponentsFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */; }; @@ -290,13 +281,7 @@ 58F8AC0E25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F8AC0D25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift */; }; 58F97A1B280EEBC00050C2FC /* RESTProxyFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F97A1A280EEBC00050C2FC /* RESTProxyFactory.swift */; }; 58F97A1E280FDE230050C2FC /* RESTRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F97A1D280FDE230050C2FC /* RESTRequestHandler.swift */; }; - 58FAEDEF245069C700CB0F5B /* KeychainAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */; }; - 58FAEDF1245069CA00CB0F5B /* KeychainAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */; }; 58FAEDF4245088B300CB0F5B /* KeychainError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58AEEF642344A36000C9BBD5 /* KeychainError.swift */; }; - 58FAEDFF24533A7000CB0F5B /* KeychainReturn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDFE24533A7000CB0F5B /* KeychainReturn.swift */; }; - 58FAEE0124533A9C00CB0F5B /* KeychainClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */; }; - 58FAEE0324533ABE00CB0F5B /* KeychainReturn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDFE24533A7000CB0F5B /* KeychainReturn.swift */; }; - 58FAEE0424533AC000CB0F5B /* KeychainClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */; }; 58FB865526E8BF3100F188BC /* AppStorePaymentManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865426E8BF3100F188BC /* AppStorePaymentManagerError.swift */; }; 58FB865A26EA214400F188BC /* RelayCacheObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865926EA214400F188BC /* RelayCacheObserver.swift */; }; 58FB865E26EA284E00F188BC /* LogFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865D26EA284E00F188BC /* LogFormatting.swift */; }; @@ -578,11 +563,6 @@ 58F8AC0D25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportReviewViewController.swift; sourceTree = "<group>"; }; 58F97A1A280EEBC00050C2FC /* RESTProxyFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTProxyFactory.swift; sourceTree = "<group>"; }; 58F97A1D280FDE230050C2FC /* RESTRequestHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTRequestHandler.swift; sourceTree = "<group>"; }; - 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAttributes.swift; sourceTree = "<group>"; }; - 58FAEDF6245088E100CB0F5B /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; }; - 58FAEDFC24533A5500CB0F5B /* KeychainMatchLimit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainMatchLimit.swift; sourceTree = "<group>"; }; - 58FAEDFE24533A7000CB0F5B /* KeychainReturn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainReturn.swift; sourceTree = "<group>"; }; - 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainClass.swift; sourceTree = "<group>"; }; 58FB865426E8BF3100F188BC /* AppStorePaymentManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorePaymentManagerError.swift; sourceTree = "<group>"; }; 58FB865926EA214400F188BC /* RelayCacheObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheObserver.swift; sourceTree = "<group>"; }; 58FB865D26EA284E00F188BC /* LogFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogFormatting.swift; sourceTree = "<group>"; }; @@ -910,7 +890,7 @@ 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */, 5850366725A47AC700A43E93 /* IPAddressRange+Codable.swift */, 58561C98239A5D1500BD6B5E /* IPEndpoint.swift */, - 58FB865626E8C06800F188BC /* Keychain */, + 58AEEF642344A36000C9BBD5 /* KeychainError.swift */, 58727282265D173C00F315B2 /* LaunchScreen.storyboard */, 58E20770274672CA00DE5D77 /* LaunchViewController.swift */, 58A1AA8623F43901009F7EA6 /* Location.swift */, @@ -1026,19 +1006,6 @@ path = Assets; sourceTree = "<group>"; }; - 58FB865626E8C06800F188BC /* Keychain */ = { - isa = PBXGroup; - children = ( - 58FAEDF6245088E100CB0F5B /* Keychain.swift */, - 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */, - 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */, - 58AEEF642344A36000C9BBD5 /* KeychainError.swift */, - 58FAEDFC24533A5500CB0F5B /* KeychainMatchLimit.swift */, - 58FAEDFE24533A7000CB0F5B /* KeychainReturn.swift */, - ); - path = Keychain; - sourceTree = "<group>"; - }; /* End PBXGroup section */ /* Begin PBXLegacyTarget section */ @@ -1277,15 +1244,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5896AE80246ACE79005B36CB /* KeychainClass.swift in Sources */, 582AE3132440CA2700E6733A /* AccountTokenInput.swift in Sources */, 58CAF4EF26025954007C5886 /* SimulatorTunnelProvider.swift in Sources */, 58B0A2AA238EE6A900BC001D /* RelaySelector.swift in Sources */, 5896AE86246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift in Sources */, 5807E2C3243203E700F5FF30 /* String+Split.swift in Sources */, - 5896AE82246ACE84005B36CB /* KeychainReturn.swift in Sources */, 58B0A2A8238EE68200BC001D /* RelaySelectorTests.swift in Sources */, - 5806766D27048E5500C858CB /* KeychainMatchLimit.swift in Sources */, 5819C2152726CC9400D6EC38 /* DataSourceSnapshot.swift in Sources */, 584E96BE240FD4DB00D3334F /* Location.swift in Sources */, 5857F23424C8443700CF6F47 /* AsyncOperation.swift in Sources */, @@ -1297,7 +1261,6 @@ 5857F23824C8446700CF6F47 /* AsyncBlockOperation.swift in Sources */, 582AE3122440CA0D00E6733A /* AccountTokenInputTests.swift in Sources */, 585DA8A526B14EE000B8C587 /* PacketTunnelStatus.swift in Sources */, - 5896AE7E246ACE65005B36CB /* KeychainAttributes.swift in Sources */, 58B0A2A9238EE6A100BC001D /* RelayConstraints.swift in Sources */, 5807E2C2243203D000F5FF30 /* StringTests.swift in Sources */, 5819C2142726CC8D00D6EC38 /* DataSourceSnapshotTests.swift in Sources */, @@ -1308,7 +1271,6 @@ 58A8BE81239FBE62006B74AC /* IPEndpoint.swift in Sources */, 5846227A26E24F1F0035F7C2 /* ExclusivityController.swift in Sources */, 58871D2325D535D2002297FA /* IPAddressRange+Codable.swift in Sources */, - 5806767B27048E8900C858CB /* Keychain.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1369,7 +1331,6 @@ 5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */, 58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */, 5846227326E22A160035F7C2 /* AppStorePaymentObserver.swift in Sources */, - 58FAEDEF245069C700CB0F5B /* KeychainAttributes.swift in Sources */, 58F2E146276A2C9900A79513 /* StopTunnelOperation.swift in Sources */, 585DA87A26B024F900B8C587 /* RelayCacheError.swift in Sources */, 5856D13727450A8A00DFD627 /* UIImage+TintColor.swift in Sources */, @@ -1388,12 +1349,10 @@ 58293FB3251241B4005D0BB5 /* CustomTextView.swift in Sources */, 58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */, 58A99ED3240014A0006599E9 /* ConsentViewController.swift in Sources */, - 58FAEE0124533A9C00CB0F5B /* KeychainClass.swift in Sources */, 58CCA0162242560B004F3011 /* UIColor+Palette.swift in Sources */, 58095C4F2760BA9100890776 /* AddressCacheStore.swift in Sources */, 58AEEF6B2344A46200C9BBD5 /* TunnelSettingsManager.swift in Sources */, 587CBFE322807F530028DED3 /* UIColor+Helpers.swift in Sources */, - 5806767927048E8800C858CB /* Keychain.swift in Sources */, 588527B4276B4F2F00BAA373 /* SetAccountOperation.swift in Sources */, 585CA70F25F8C44600B47C62 /* UIMetrics.swift in Sources */, 58F97A1B280EEBC00050C2FC /* RESTProxyFactory.swift in Sources */, @@ -1469,7 +1428,6 @@ 58554F79280B037400013055 /* RESTAccessTokenManager.swift in Sources */, 58F2E144276A13F300A79513 /* StartTunnelOperation.swift in Sources */, 5868BD33261DCD2600E6027F /* CustomSplitViewController.swift in Sources */, - 5806766E27048E5600C858CB /* KeychainMatchLimit.swift in Sources */, 58CCA01E2242787B004F3011 /* AccountTextField.swift in Sources */, 586E54FB27A2DF6D0029B88B /* TunnelIPCRequestOperation.swift in Sources */, 584592612639B4A200EF967F /* ConsentContentView.swift in Sources */, @@ -1498,7 +1456,6 @@ 5811DE50239014550011EB53 /* NEVPNStatus+Debug.swift in Sources */, 58C3A4B222456F1B00340BDB /* AccountInputGroupView.swift in Sources */, 58F840B22464491D0044E708 /* ChainedError.swift in Sources */, - 58FAEDFF24533A7000CB0F5B /* KeychainReturn.swift in Sources */, 588BCF24280FE43D009ADCEC /* RESTDevicesProxy.swift in Sources */, 58ACF64B26553C3F00ACE4B7 /* SettingsSwitchCell.swift in Sources */, 587EB67027143B6500123C75 /* DataSourceSnapshot.swift in Sources */, @@ -1513,11 +1470,8 @@ files = ( 5850366825A47AC700A43E93 /* IPAddressRange+Codable.swift in Sources */, 58FB865F26EA2E6D00F188BC /* LogFormatting.swift in Sources */, - 5806767A27048E8800C858CB /* Keychain.swift in Sources */, 585DA89726B0328000B8C587 /* TunnelIPCResponse.swift in Sources */, - 5806768127048EE000C858CB /* KeychainMatchLimit.swift in Sources */, 587C575426D2615F005EF767 /* PacketTunnelOptions.swift in Sources */, - 58FAEE0324533ABE00CB0F5B /* KeychainReturn.swift in Sources */, 58BFA5CD22A7CE1F00A6173D /* ApplicationConfiguration.swift in Sources */, 5850368D25A49E2200A43E93 /* PrivateKeyWithMetadata.swift in Sources */, 5820675826E652AF00655B05 /* RelayCacheIO.swift in Sources */, @@ -1534,11 +1488,9 @@ 58FC040A27B3EE03001C21F0 /* TunnelMonitor.swift in Sources */, 5838318B27C40A3900000571 /* Pinger.swift in Sources */, 5820675C26E6576800655B05 /* RelayCache.swift in Sources */, - 58FAEDF1245069CA00CB0F5B /* KeychainAttributes.swift in Sources */, 585DA89A26B0329200B8C587 /* PacketTunnelStatus.swift in Sources */, 585DA88526B0270700B8C587 /* ServerRelaysResponse.swift in Sources */, 581503A724D6F4AE00C9C50E /* Logging.swift in Sources */, - 58FAEE0424533AC000CB0F5B /* KeychainClass.swift in Sources */, 58AEEF6C2344A49D00C9BBD5 /* TunnelSettingsManager.swift in Sources */, 581503A424D6F1EC00C9C50E /* ChainedError+Logger.swift in Sources */, 5815039824D6ECAE00C9C50E /* CustomFormatLogHandler.swift in Sources */, diff --git a/ios/MullvadVPN/Keychain/Keychain.swift b/ios/MullvadVPN/Keychain/Keychain.swift deleted file mode 100644 index 383219990a..0000000000 --- a/ios/MullvadVPN/Keychain/Keychain.swift +++ /dev/null @@ -1,154 +0,0 @@ -// -// Keychain.swift -// MullvadVPN -// -// Created by pronebird on 22/04/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Security - -protocol KeychainAttributeDecodable { - init?(attributes: [CFString: Any]) -} - -protocol KeychainAttributeEncodable { - func keychainRepresentation() -> [CFString: Any] - func updateKeychainAttributes(in attributes: inout [CFString: Any]) -} - -extension KeychainAttributeEncodable { - func keychainRepresentation() -> [CFString: Any] { - var attributes = [CFString: Any]() - updateKeychainAttributes(in: &attributes) - return attributes - } -} - -enum Keychain {} - -extension Keychain { - - /// A Keychain Result type - typealias Result<T> = Swift.Result<T, Keychain.Error> - - static func add(_ attributes: Keychain.Attributes) -> Result<Keychain.Attributes?> { - var result: CFTypeRef? - let status = SecItemAdd(attributes.keychainRepresentation() as CFDictionary, &result) - - return mapSecResultAndReturnValue( - status: status, - value: result, - returnSet: attributes.return ?? [], - limit: .one) - .map { $0.first } - } - - static func update(query: Keychain.Attributes, update: Keychain.Attributes) -> Result<()> { - let queryAttributes = query.keychainRepresentation() as CFDictionary - let updateAttributes = update.keychainRepresentation() as CFDictionary - - let status = SecItemUpdate(queryAttributes, updateAttributes) - - return mapSecResult(status: status) { - return () - } - } - - static func delete(query: Keychain.Attributes) -> Result<()> { - let status = SecItemDelete(query.keychainRepresentation() as CFDictionary) - - return mapSecResult(status: status) { - return () - } - } - - static func findFirst(query: Keychain.Attributes) -> Result<Keychain.Attributes?> { - return find(query: query).map { $0.first } - } - - static func find(query: Keychain.Attributes) -> Result<[Keychain.Attributes]> { - let attributes = query.keychainRepresentation() - - var result: CFTypeRef? - let status = SecItemCopyMatching(attributes as CFDictionary, &result) - - return mapSecResultAndReturnValue( - status: status, - value: result, - returnSet: query.return ?? [], - limit: query.matchLimit ?? .one - ) - } - - static private func mapSecResultAndReturnValue( - status: OSStatus, - value: CFTypeRef?, - returnSet: Set<Keychain.Return>, - limit: Keychain.MatchLimit) -> Result<[Keychain.Attributes]> - { - return mapSecResult(status: status) { () -> [Keychain.Attributes] in - return value.map { parseReturnValue(value: $0, returnSet: returnSet, limit: limit) } - ?? [] - } - } - - static private func parseReturnValue( - value: CFTypeRef, - returnSet: Set<Keychain.Return>, - limit: Keychain.MatchLimit) -> [Keychain.Attributes] - { - switch returnSet { - case []: - return [] - - case [.data]: - let values: [Data] = unsafelyCastReturnValue(value: value, limit: limit) - - return values.map { (data) -> Keychain.Attributes in - var attributes = Keychain.Attributes() - attributes.valueData = data - return attributes - } - - case [.persistentReference]: - let values: [Data] = unsafelyCastReturnValue(value: value, limit: limit) - - return values.map { (persistentReference) -> Keychain.Attributes in - var attributes = Keychain.Attributes() - attributes.valuePersistentReference = persistentReference - return attributes - } - - default: - let rawAttributeList: [[CFString: Any]] = - unsafelyCastReturnValue(value: value, limit: limit) - - return rawAttributeList.map { Keychain.Attributes(attributes: $0) } - } - } - - /// A private helper that casts and normalizes the return value from Keychain to produce - /// an array even when a single item is expected to be returned. - static private func unsafelyCastReturnValue<T>( - value: CFTypeRef, - limit: Keychain.MatchLimit) -> [T] - { - switch limit { - case .one: - return [value as! T] - case .all: - return value as! [T] - } - } - - /// A private helper that verifies the given `status` and executes `body` on success - static private func mapSecResult<T>(status: OSStatus, body: () -> T) -> Result<T> { - if status == errSecSuccess { - return .success(body()) - } else { - return .failure(Keychain.Error(code: status)) - } - } -} diff --git a/ios/MullvadVPN/Keychain/KeychainAttributes.swift b/ios/MullvadVPN/Keychain/KeychainAttributes.swift deleted file mode 100644 index 397620f37b..0000000000 --- a/ios/MullvadVPN/Keychain/KeychainAttributes.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// KeychainAttributes.swift -// MullvadVPN -// -// Created by pronebird on 22/04/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Security - -extension Keychain { - - enum Accessible: RawRepresentable, CaseIterable, KeychainAttributeDecodable, KeychainAttributeEncodable { - - case whenPasscodeSetThisDeviceOnly - case whenUnlocked - case whenUnlockedThisDeviceOnly - case afterFirstUnlock - case afterFirstUnlockThisDeviceOnly - - var rawValue: CFString { - switch self { - case .whenPasscodeSetThisDeviceOnly: - return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly - case .whenUnlocked: - return kSecAttrAccessibleWhenUnlocked - case .whenUnlockedThisDeviceOnly: - return kSecAttrAccessibleWhenUnlockedThisDeviceOnly - case .afterFirstUnlock: - return kSecAttrAccessibleAfterFirstUnlock - case .afterFirstUnlockThisDeviceOnly: - return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly - } - } - - init?(rawValue: CFString) { - let maybeCase = Self.allCases.first { $0.rawValue == rawValue } - - if let maybeCase = maybeCase { - self = maybeCase - } else { - return nil - } - } - - init?(attributes: [CFString: Any]) { - if let rawValue = attributes[kSecAttrAccessible] as? String { - self.init(rawValue: rawValue as CFString) - } else { - return nil - } - } - - func updateKeychainAttributes(in attributes: inout [CFString : Any]) { - attributes[kSecAttrAccessible] = rawValue - } - - } - - struct Attributes: KeychainAttributeEncodable, KeychainAttributeDecodable { - var `class`: KeychainClass? - var service: String? - var account: String? - var accessGroup: String? - var accessible: Accessible? - var creationDate: Date? - var modificationDate: Date? - var generic: Data? - - var valueData: Data? - var valuePersistentReference: Data? - - var `return`: Set<Keychain.Return>? - var matchLimit: Keychain.MatchLimit? - - init() {} - - init(attributes: [CFString: Any]) { - `class` = KeychainClass(attributes: attributes) - service = attributes[kSecAttrService] as? String - account = attributes[kSecAttrAccount] as? String - accessGroup = attributes[kSecAttrAccessGroup] as? String - accessible = Accessible(attributes: attributes) - creationDate = attributes[kSecAttrCreationDate] as? Date - modificationDate = attributes[kSecAttrModificationDate] as? Date - generic = attributes[kSecAttrGeneric] as? Data - - valueData = attributes[kSecValueData] as? Data - valuePersistentReference = attributes[kSecValuePersistentRef] as? Data - - `return` = Set(attributes: attributes) - matchLimit = Keychain.MatchLimit(attributes: attributes) - } - - func updateKeychainAttributes(in attributes: inout [CFString: Any]) { - `class`?.updateKeychainAttributes(in: &attributes) - - if let service = service { - attributes[kSecAttrService] = service - } - - if let account = account { - attributes[kSecAttrAccount] = account - } - - if let accessGroup = accessGroup { - attributes[kSecAttrAccessGroup] = accessGroup - } - - accessible?.updateKeychainAttributes(in: &attributes) - - if let creationDate = creationDate { - attributes[kSecAttrCreationDate] = creationDate - } - - if let modificationDate = modificationDate { - attributes[kSecAttrModificationDate] = modificationDate - } - - if let generic = generic { - attributes[kSecAttrGeneric] = generic - } - - if let valueData = valueData { - attributes[kSecValueData] = valueData - } - - if let valuePersistentReference = valuePersistentReference { - attributes[kSecValuePersistentRef] = valuePersistentReference - } - - `return`?.updateKeychainAttributes(in: &attributes) - matchLimit?.updateKeychainAttributes(in: &attributes) - } - - } - -} diff --git a/ios/MullvadVPN/Keychain/KeychainClass.swift b/ios/MullvadVPN/Keychain/KeychainClass.swift deleted file mode 100644 index 86e13f2bd6..0000000000 --- a/ios/MullvadVPN/Keychain/KeychainClass.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// KeychainClass.swift -// MullvadVPN -// -// Created by pronebird on 24/04/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Security - -extension Keychain { - - enum KeychainClass: RawRepresentable, CaseIterable, KeychainAttributeDecodable, KeychainAttributeEncodable { - case genericPassword - case internetPassword - - var rawValue: CFString { - switch self { - case .genericPassword: - return kSecClassGenericPassword - case .internetPassword: - return kSecClassInternetPassword - } - } - - init?(rawValue: CFString) { - let maybeCase = Self.allCases.first { $0.rawValue == rawValue } - - if let maybeCase = maybeCase { - self = maybeCase - } else { - return nil - } - } - - init?(attributes: [CFString: Any]) { - if let rawValue = attributes[kSecClass] as? String { - self.init(rawValue: rawValue as CFString) - } else { - return nil - } - } - - func updateKeychainAttributes(in attributes: inout [CFString : Any]) { - attributes[kSecClass] = rawValue - } - } - -} diff --git a/ios/MullvadVPN/Keychain/KeychainError.swift b/ios/MullvadVPN/Keychain/KeychainError.swift deleted file mode 100644 index 4758d08b66..0000000000 --- a/ios/MullvadVPN/Keychain/KeychainError.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// KeychainError.swift -// MullvadVPN -// -// Created by pronebird on 02/10/2019. -// Copyright © 2019 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Security - -extension Keychain { - struct Error: Swift.Error, LocalizedError { - let code: OSStatus - - var errorDescription: String? { - return SecCopyErrorMessageString(code, nil) as String? - } - } -} - - -extension Keychain.Error { - - static let duplicateItem = Keychain.Error(code: errSecDuplicateItem) - static let itemNotFound = Keychain.Error(code: errSecItemNotFound) - - static func ~= (lhs: Keychain.Error, rhs: Swift.Error) -> Bool { - guard let rhsError = rhs as? Keychain.Error else { return false } - return lhs.code == rhsError.code - } -} diff --git a/ios/MullvadVPN/Keychain/KeychainMatchLimit.swift b/ios/MullvadVPN/Keychain/KeychainMatchLimit.swift deleted file mode 100644 index f86b010532..0000000000 --- a/ios/MullvadVPN/Keychain/KeychainMatchLimit.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// KeychainMatchLimit.swift -// MullvadVPN -// -// Created by pronebird on 24/04/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Security - -extension Keychain { - enum MatchLimit: RawRepresentable, CaseIterable, KeychainAttributeDecodable, KeychainAttributeEncodable { - case one - case all - - var rawValue: CFString { - switch self { - case .one: - return kSecMatchLimitOne - case .all: - return kSecMatchLimitAll - } - } - - init?(rawValue: CFString) { - let maybeCase = Self.allCases.first { $0.rawValue == rawValue } - - if let maybeCase = maybeCase { - self = maybeCase - } else { - return nil - } - } - - init?(attributes: [CFString : Any]) { - if let rawValue = attributes[kSecMatchLimit] as? String { - self.init(rawValue: rawValue as CFString) - } else { - return nil - } - } - - func updateKeychainAttributes(in attributes: inout [CFString : Any]) { - attributes[kSecMatchLimit] = rawValue - } - } -} diff --git a/ios/MullvadVPN/Keychain/KeychainReturn.swift b/ios/MullvadVPN/Keychain/KeychainReturn.swift deleted file mode 100644 index 7216ffbd80..0000000000 --- a/ios/MullvadVPN/Keychain/KeychainReturn.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// KeychainReturn.swift -// MullvadVPN -// -// Created by pronebird on 24/04/2020. -// Copyright © 2020 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import Security - -extension Keychain { - enum Return: KeychainAttributeEncodable, CaseIterable { - case data - case attributes - case persistentReference - - fileprivate var attributeKey: CFString { - switch self { - case .attributes: - return kSecReturnAttributes - case .data: - return kSecReturnData - case .persistentReference: - return kSecReturnPersistentRef - } - } - - func updateKeychainAttributes(in attributes: inout [CFString: Any]) { - attributes[attributeKey] = true - } - } -} - -extension Set: KeychainAttributeDecodable, KeychainAttributeEncodable - where Element == Keychain.Return -{ - init?(attributes: [CFString: Any]) { - let items = Keychain.Return.allCases.filter { (returnType) -> Bool in - return attributes[returnType.attributeKey] as? Bool == .some(true) - } - - if items.isEmpty { - return nil - } else { - self.init(items) - } - } - - func updateKeychainAttributes(in attributes: inout [CFString : Any]) { - Keychain.Return.allCases.forEach { (returnType) in - attributes.removeValue(forKey: returnType.attributeKey) - } - - forEach { $0.updateKeychainAttributes(in: &attributes) } - } -} diff --git a/ios/MullvadVPN/KeychainError.swift b/ios/MullvadVPN/KeychainError.swift new file mode 100644 index 0000000000..5ee831c83f --- /dev/null +++ b/ios/MullvadVPN/KeychainError.swift @@ -0,0 +1,25 @@ +// +// KeychainError.swift +// MullvadVPN +// +// Created by pronebird on 02/10/2019. +// Copyright © 2019 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Security + +struct KeychainError: LocalizedError, Equatable { + let code: OSStatus + + var errorDescription: String? { + return SecCopyErrorMessageString(code, nil) as String? + } + + static let duplicateItem = KeychainError(code: errSecDuplicateItem) + static let itemNotFound = KeychainError(code: errSecItemNotFound) + + static func == (lhs: KeychainError, rhs: KeychainError) -> Bool { + return lhs.code == rhs.code + } +} |
