Repository: kishikawakatsumi/KeychainAccess Branch: master Commit: e0c7eebc5a44 Files: 54 Total size: 487.8 KB Directory structure: gitextract_9kvtxevm/ ├── .github/ │ └── workflows/ │ └── test.yml ├── .gitignore ├── .swiftpm/ │ └── xcode/ │ └── package.xcworkspace/ │ └── contents.xcworkspacedata ├── Examples/ │ └── Example-iOS/ │ ├── Example-iOS/ │ │ ├── AccountsViewController.swift │ │ ├── AppDelegate.swift │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.xib │ │ │ └── Main.storyboard │ │ ├── Example-iOS.entitlements │ │ ├── Images.xcassets/ │ │ │ └── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── InputViewController.swift │ └── Example-iOS.xcodeproj/ │ ├── project.pbxproj │ └── xcshareddata/ │ └── xcschemes/ │ └── Example-iOS.xcscheme ├── KeychainAccess.podspec ├── KeychainAccess.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Lib/ │ ├── Configurations/ │ │ ├── Base.xcconfig │ │ ├── Debug.xcconfig │ │ ├── KeychainAccess.xcconfig │ │ ├── Release.xcconfig │ │ ├── TestHost.xcconfig │ │ └── Tests.xcconfig │ ├── KeychainAccess/ │ │ ├── Info.plist │ │ ├── Keychain.swift │ │ └── KeychainAccess.h │ ├── KeychainAccess.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── KeychainAccess.xcscheme │ │ └── TestHost.xcscheme │ ├── KeychainAccessTests/ │ │ ├── EnumTests.swift │ │ ├── ErrorTypeTests.swift │ │ ├── Info.plist │ │ ├── KeychainAccessTests.swift │ │ └── SharedCredentialTests.swift │ ├── TestHost/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ └── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── TestHost.entitlements │ └── TestHost-MacCatalyst/ │ ├── KeychainAccessTests-MacCatalyst/ │ │ └── Info.plist │ ├── TestHost-MacCatalyst/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── SceneDelegate.swift │ │ ├── TestHost-MacCatalyst.entitlements │ │ └── ViewController.swift │ └── TestHost-MacCatalyst.xcodeproj/ │ ├── project.pbxproj │ └── xcshareddata/ │ └── xcschemes/ │ └── TestHost-MacCatalyst.xcscheme ├── Package.swift ├── Package@swift-5.3.swift └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: pull_request: branches: [master] push: branches: [master] workflow_dispatch: jobs: test: name: ${{ matrix.command }} on  ${{ matrix.platform }} (xcode ${{ matrix.xcode }}, ${{ matrix.macos }}) runs-on: ${{ matrix.macos }} strategy: fail-fast: false matrix: macos: ["macos-11", "macos-12", "macos-13"] xcode: ["11.7", "12.5.1", "13.2.1", "14.2", "14.3.1", "15.0.1"] platform: ["ios"] command: ["test"] exclude: - macos: "macos-11" xcode: "14.2" - macos: "macos-11" xcode: "14.3.1" - macos: "macos-11" xcode: "15.0.1" - macos: "macos-12" xcode: "11.7" - macos: "macos-12" xcode: "12.5.1" - macos: "macos-12" xcode: "13.2.1" - macos: "macos-12" xcode: "14.3.1" - macos: "macos-12" xcode: "15.0.1" - macos: "macos-13" xcode: "11.7" - macos: "macos-13" xcode: "12.5.1" - macos: "macos-13" xcode: "13.2.1" - macos: "macos-13" xcode: "14.2" steps: - uses: actions/checkout@v4 - uses: maxim-lobanov/setup-xcode@v1.2.3 with: xcode-version: ${{ matrix.xcode }} - name: Install the Apple certificate and provisioning profile env: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} run: | # create variables CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 PP_PATH=$RUNNER_TEMP/build_pp.provisionprofile KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db # import certificate and provisioning profile from secrets echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH # create temporary keychain security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH # import certificate to keychain security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH # apply provisioning profile mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles - name: Test if: ${{ matrix.xcode=='11.7' && matrix.platform=='ios' }} run: | set -ex xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults.xcresult - name: Test if: ${{ matrix.xcode=='12.5.1' && matrix.platform=='ios' }} run: | set -ex xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults.xcresult - name: Test if: ${{ matrix.xcode=='13.2.1' && matrix.platform=='ios' }} run: | set -ex xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=15.2,name=iPhone 13 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults.xcresult - name: Test if: ${{ matrix.xcode=='14.2' && matrix.platform=='ios' }} run: | set -ex xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=16.2,name=iPhone 14 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults.xcresult - name: Test if: ${{ matrix.xcode=='14.3.1' && matrix.platform=='ios' }} run: | set -ex xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=16.4,name=iPhone 14 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults1.xcresult xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=17.0.1,name=iPhone 14 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults2.xcresult xcrun xcresulttool merge TestResults1.xcresult TestResults2.xcresult --output-path TestResults.xcresult - name: Test if: ${{ matrix.xcode=='15.0.1' && matrix.platform=='ios' }} run: | set -ex xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -destination 'platform=iOS Simulator,OS=17.0.1,name=iPhone 15 Pro' -only-testing:KeychainAccessTests -resultBundlePath TestResults.xcresult - uses: kishikawakatsumi/xcresulttool@v1 with: path: TestResults.xcresult title: "KeychainAccess test report" if: always() ================================================ FILE: .gitignore ================================================ ### https://raw.github.com/github/gitignore/2a4de265d37eca626309d8e115218d18985b5435/Swift.gitignore # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project # .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ ### https://raw.github.com/github/gitignore/2a4de265d37eca626309d8e115218d18985b5435/Global/macOS.gitignore # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### https://raw.github.com/github/gitignore/2a4de265d37eca626309d8e115218d18985b5435/Ruby.gitignore *.gem *.rbc /.config /coverage/ /InstalledFiles /pkg/ /spec/reports/ /spec/examples.txt /test/tmp/ /test/version_tmp/ /tmp/ # Used by dotenv library to load environment variables. # .env # Ignore Byebug command history file. .byebug_history ## Specific to RubyMotion: .dat* .repl_history build/ *.bridgesupport build-iPhoneOS/ build-iPhoneSimulator/ ## Specific to RubyMotion (use of CocoaPods): # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # vendor/Pods/ ## Documentation cache and generated files: /.yardoc/ /_yardoc/ /doc/ /rdoc/ ## Environment normalization: /.bundle/ /vendor/bundle /lib/bundler/man/ # for a library or gem, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # Gemfile.lock # .ruby-version # .ruby-gemset # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc # Used by RuboCop. Remote config files pulled in from inherit_from directive. # .rubocop-https?--* ================================================ FILE: .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Examples/Example-iOS/Example-iOS/AccountsViewController.swift ================================================ // // AccountsViewController.swift // Example // // Created by kishikawa katsumi on 2014/12/25. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit import KeychainAccess class AccountsViewController: UITableViewController { var itemsGroupedByService: [String: [[String: Any]]]? override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) reloadData() tableView.reloadData() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } // MARK: override func numberOfSections(in tableView: UITableView) -> Int { if itemsGroupedByService != nil { let services = Array(itemsGroupedByService!.keys) return services.count } return 0 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let services = Array(itemsGroupedByService!.keys) let service = services[section] let items = Keychain(service: service).allItems() return items.count } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let services = Array(itemsGroupedByService!.keys) return services[section] } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let services = Array(itemsGroupedByService!.keys) let service = services[indexPath.section] let items = Keychain(service: service).allItems() let item = items[indexPath.row] cell.textLabel?.text = item["key"] as? String cell.detailTextLabel?.text = item["value"] as? String return cell } #if swift(>=4.2) override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { let services = Array(itemsGroupedByService!.keys) let service = services[indexPath.section] let keychain = Keychain(service: service) let items = keychain.allItems() let item = items[indexPath.row] let key = item["key"] as! String keychain[key] = nil if items.count == 1 { reloadData() tableView.deleteSections(IndexSet(integer: indexPath.section), with: .automatic) } else { tableView.deleteRows(at: [indexPath], with: .automatic) } } #else override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { let services = Array(itemsGroupedByService!.keys) let service = services[indexPath.section] let keychain = Keychain(service: service) let items = keychain.allItems() let item = items[indexPath.row] let key = item["key"] as! String keychain[key] = nil if items.count == 1 { reloadData() tableView.deleteSections(IndexSet(integer: indexPath.section), with: .automatic) } else { tableView.deleteRows(at: [indexPath], with: .automatic) } } #endif // MARK: func reloadData() { let items = Keychain.allItems(.genericPassword) itemsGroupedByService = groupBy(items) { item -> String in if let service = item["service"] as? String { return service } return "" } } } private func groupBy(_ xs: C, key: (C.Iterator.Element) -> K) -> [K:[C.Iterator.Element]] { var gs: [K:[C.Iterator.Element]] = [:] for x in xs { let k = key(x) var ys = gs[k] ?? [] ys.append(x) gs.updateValue(ys, forKey: k) } return gs } ================================================ FILE: Examples/Example-iOS/Example-iOS/AppDelegate.swift ================================================ // // AppDelegate.swift // Example // // Created by kishikawa katsumi on 2014/12/25. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? #if swift(>=4.2) func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { return true } #else func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { return true } #endif } ================================================ FILE: Examples/Example-iOS/Example-iOS/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: Examples/Example-iOS/Example-iOS/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Examples/Example-iOS/Example-iOS/Example-iOS.entitlements ================================================ keychain-access-groups $(AppIdentifierPrefix)com.kishikawakatsumi.Example-iOS ================================================ FILE: Examples/Example-iOS/Example-iOS/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Examples/Example-iOS/Example-iOS/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Examples/Example-iOS/Example-iOS/InputViewController.swift ================================================ // // InputViewController.swift // Example // // Created by kishikawa katsumi on 2014/12/26. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit import KeychainAccess class InputViewController: UITableViewController { @IBOutlet weak var saveButton: UIBarButtonItem! @IBOutlet weak var cancelButton: UIBarButtonItem! @IBOutlet weak var usernameField: UITextField! @IBOutlet weak var passwordField: UITextField! @IBOutlet weak var serviceField: UITextField! override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = 44.0 tableView.estimatedRowHeight = 44.0 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } // MARK: @IBAction func cancelAction(sender: UIBarButtonItem) { dismiss(animated: true, completion: nil) } @IBAction func saveAction(sender: UIBarButtonItem) { let keychain: Keychain if let service = serviceField.text, !service.isEmpty { keychain = Keychain(service: service) } else { keychain = Keychain() } keychain[usernameField.text!] = passwordField.text dismiss(animated: true, completion: nil) } @IBAction func editingChanged(sender: UITextField) { switch (usernameField.text, passwordField.text) { case let (username?, password?): saveButton.isEnabled = !username.isEmpty && !password.isEmpty case (_?, nil): saveButton.isEnabled = false case (nil, _?): saveButton.isEnabled = false case (nil, nil): saveButton.isEnabled = false } } } ================================================ FILE: Examples/Example-iOS/Example-iOS.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 14DAEE961A51E1BE0070B77E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DAEE951A51E1BE0070B77E /* AppDelegate.swift */; }; 14DAEE9B1A51E1BE0070B77E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14DAEE991A51E1BE0070B77E /* Main.storyboard */; }; 14DAEE9D1A51E1BE0070B77E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14DAEE9C1A51E1BE0070B77E /* Images.xcassets */; }; 14DAEEA01A51E1BE0070B77E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14DAEE9E1A51E1BE0070B77E /* LaunchScreen.xib */; }; 14DAEEB71A51E2690070B77E /* AccountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DAEEB51A51E2690070B77E /* AccountsViewController.swift */; }; 14DAEEB81A51E2690070B77E /* InputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DAEEB61A51E2690070B77E /* InputViewController.swift */; }; 14DAEEC91A51E2D00070B77E /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */; }; 14DAEECB1A51E2E10070B77E /* KeychainAccess.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 1470425D1D6FFA97005A4C6E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; proxyType = 2; remoteGlobalIDString = 14A630151D3293C700809B3F; remoteInfo = TestHost; }; 14DAEEC01A51E2A60070B77E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; proxyType = 2; remoteGlobalIDString = 140F195C1A49D79400B0016A; remoteInfo = "KeychainAccess-iOS"; }; 14DAEEC21A51E2A60070B77E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; proxyType = 2; remoteGlobalIDString = 140F19671A49D79500B0016A; remoteInfo = "KeychainAccess-iOSTests"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 14DAEECA1A51E2D70070B77E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 14DAEECB1A51E2E10070B77E /* KeychainAccess.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 145652E11D898BB9006E8D0E /* Example-iOS.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Example-iOS.entitlements"; sourceTree = ""; }; 14DAEE901A51E1BE0070B77E /* Example-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 14DAEE941A51E1BE0070B77E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14DAEE951A51E1BE0070B77E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 14DAEE9A1A51E1BE0070B77E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 14DAEE9C1A51E1BE0070B77E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 14DAEE9F1A51E1BE0070B77E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 14DAEEB51A51E2690070B77E /* AccountsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsViewController.swift; sourceTree = ""; }; 14DAEEB61A51E2690070B77E /* InputViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputViewController.swift; sourceTree = ""; }; 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KeychainAccess.xcodeproj; path = ../../Lib/KeychainAccess.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 14DAEE8D1A51E1BE0070B77E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14DAEEC91A51E2D00070B77E /* KeychainAccess.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 14DAEE871A51E1BE0070B77E = { isa = PBXGroup; children = ( 14DAEE921A51E1BE0070B77E /* Example-iOS */, 14DAEE911A51E1BE0070B77E /* Products */, 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */, ); sourceTree = ""; }; 14DAEE911A51E1BE0070B77E /* Products */ = { isa = PBXGroup; children = ( 14DAEE901A51E1BE0070B77E /* Example-iOS.app */, ); name = Products; sourceTree = ""; }; 14DAEE921A51E1BE0070B77E /* Example-iOS */ = { isa = PBXGroup; children = ( 14DAEE951A51E1BE0070B77E /* AppDelegate.swift */, 14DAEEB51A51E2690070B77E /* AccountsViewController.swift */, 14DAEEB61A51E2690070B77E /* InputViewController.swift */, 14DAEE991A51E1BE0070B77E /* Main.storyboard */, 14DAEE9E1A51E1BE0070B77E /* LaunchScreen.xib */, 14DAEE9C1A51E1BE0070B77E /* Images.xcassets */, 14DAEE931A51E1BE0070B77E /* Supporting Files */, ); path = "Example-iOS"; sourceTree = ""; }; 14DAEE931A51E1BE0070B77E /* Supporting Files */ = { isa = PBXGroup; children = ( 14DAEE941A51E1BE0070B77E /* Info.plist */, 145652E11D898BB9006E8D0E /* Example-iOS.entitlements */, ); name = "Supporting Files"; sourceTree = ""; }; 14DAEEBA1A51E2A60070B77E /* Products */ = { isa = PBXGroup; children = ( 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */, 14DAEEC31A51E2A60070B77E /* KeychainAccessTests.xctest */, 1470425E1D6FFA97005A4C6E /* TestHost.app */, ); name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 14DAEE8F1A51E1BE0070B77E /* Example-iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 14DAEEAF1A51E1BE0070B77E /* Build configuration list for PBXNativeTarget "Example-iOS" */; buildPhases = ( 14DAEE8C1A51E1BE0070B77E /* Sources */, 14DAEE8D1A51E1BE0070B77E /* Frameworks */, 14DAEE8E1A51E1BE0070B77E /* Resources */, 14DAEECA1A51E2D70070B77E /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = "Example-iOS"; productName = "Example-iOS"; productReference = 14DAEE901A51E1BE0070B77E /* Example-iOS.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 14DAEE881A51E1BE0070B77E /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 1200; ORGANIZATIONNAME = "kishikawa katsumi"; TargetAttributes = { 14DAEE8F1A51E1BE0070B77E = { CreatedOnToolsVersion = 6.2; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 14DAEE8B1A51E1BE0070B77E /* Build configuration list for PBXProject "Example-iOS" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 14DAEE871A51E1BE0070B77E; productRefGroup = 14DAEE911A51E1BE0070B77E /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 14DAEEBA1A51E2A60070B77E /* Products */; ProjectRef = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; }, ); projectRoot = ""; targets = ( 14DAEE8F1A51E1BE0070B77E /* Example-iOS */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 1470425E1D6FFA97005A4C6E /* TestHost.app */ = { isa = PBXReferenceProxy; fileType = wrapper.application; path = TestHost.app; remoteRef = 1470425D1D6FFA97005A4C6E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = KeychainAccess.framework; remoteRef = 14DAEEC01A51E2A60070B77E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 14DAEEC31A51E2A60070B77E /* KeychainAccessTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; path = KeychainAccessTests.xctest; remoteRef = 14DAEEC21A51E2A60070B77E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ 14DAEE8E1A51E1BE0070B77E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 14DAEE9B1A51E1BE0070B77E /* Main.storyboard in Resources */, 14DAEEA01A51E1BE0070B77E /* LaunchScreen.xib in Resources */, 14DAEE9D1A51E1BE0070B77E /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 14DAEE8C1A51E1BE0070B77E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14DAEEB81A51E2690070B77E /* InputViewController.swift in Sources */, 14DAEEB71A51E2690070B77E /* AccountsViewController.swift in Sources */, 14DAEE961A51E1BE0070B77E /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 14DAEE991A51E1BE0070B77E /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 14DAEE9A1A51E1BE0070B77E /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 14DAEE9E1A51E1BE0070B77E /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( 14DAEE9F1A51E1BE0070B77E /* Base */, ); name = LaunchScreen.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 14DAEEAD1A51E1BE0070B77E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = "Example-iOS/Example-iOS.entitlements"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.1; }; name = Debug; }; 14DAEEAE1A51E1BE0070B77E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = "Example-iOS/Example-iOS.entitlements"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.1; VALIDATE_PRODUCT = YES; }; name = Release; }; 14DAEEB01A51E1BE0070B77E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "Example-iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 14DAEEB11A51E1BE0070B77E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "Example-iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 14DAEE8B1A51E1BE0070B77E /* Build configuration list for PBXProject "Example-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 14DAEEAD1A51E1BE0070B77E /* Debug */, 14DAEEAE1A51E1BE0070B77E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14DAEEAF1A51E1BE0070B77E /* Build configuration list for PBXNativeTarget "Example-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 14DAEEB01A51E1BE0070B77E /* Debug */, 14DAEEB11A51E1BE0070B77E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 14DAEE881A51E1BE0070B77E /* Project object */; } ================================================ FILE: Examples/Example-iOS/Example-iOS.xcodeproj/xcshareddata/xcschemes/Example-iOS.xcscheme ================================================ ================================================ FILE: KeychainAccess.podspec ================================================ Pod::Spec.new do |s| s.name = 'KeychainAccess' s.version = '4.2.2' s.summary = 'KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and macOS.' s.description = <<-DESC KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and macOS. Makes using Keychain APIs exremely easy and much more palatable to use in Swift. Features - Simple interface - Support access group - Support accessibility - Support iCloud sharing - **Support TouchID and Keychain integration (iOS 8+)** - **Support Shared Web Credentials (iOS 8+)** - Works on both iOS & macOS - watchOS and tvOS are also supported DESC s.homepage = 'https://github.com/kishikawakatsumi/KeychainAccess' s.screenshots = 'https://raw.githubusercontent.com/kishikawakatsumi/KeychainAccess/master/Screenshots/01.png' s.license = 'MIT' s.author = { 'kishikawa katsumi' => 'kishikawakatsumi@mac.com' } s.source = { :git => 'https://github.com/kishikawakatsumi/KeychainAccess.git', :tag => "v#{s.version}" } s.social_media_url = 'https://twitter.com/k_katsumi' s.requires_arc = true s.source_files = 'Lib/KeychainAccess/*.swift' s.swift_version = '5.1' s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.9' s.watchos.deployment_target = '3.0' s.tvos.deployment_target = '9.0' end ================================================ FILE: KeychainAccess.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: KeychainAccess.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 kishikawa katsumi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Lib/Configurations/Base.xcconfig ================================================ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES CLANG_CXX_LANGUAGE_STANDARD = gnu++0x; CLANG_CXX_LIBRARY = libc++; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CURRENT_PROJECT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_DYNAMIC_NO_PIC = NO; GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; CODE_SIGN_IDENTITY = ; DEVELOPMENT_TEAM = ; IPHONEOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 3.0; TVOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; SWIFT_VERSION = 5.0; ================================================ FILE: Lib/Configurations/Debug.xcconfig ================================================ #include "Base.xcconfig" BITCODE_GENERATION_MODE = marker; MTL_ENABLE_DEBUG_INFO = YES; COPY_PHASE_STRIP = NO; ENABLE_TESTABILITY = YES; GCC_OPTIMIZATION_LEVEL = 0; ONLY_ACTIVE_ARCH = YES; SWIFT_OPTIMIZATION_LEVEL = -Onone; GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1; ================================================ FILE: Lib/Configurations/KeychainAccess.xcconfig ================================================ SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator; TARGETED_DEVICE_FAMILY = 1,2,3,4; SUPPORTS_MACCATALYST = YES; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; COMBINE_HIDPI_IMAGES = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.$(PLATFORM_NAME).$(PRODUCT_NAME:rfc1034identifier); PRODUCT_NAME = $(PROJECT_NAME); APPLICATION_EXTENSION_API_ONLY = YES; INFOPLIST_FILE = KeychainAccess/Info.plist; SKIP_INSTALL = YES; BUILD_LIBRARY_FOR_DISTRIBUTION = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = @rpath; ENABLE_BITCODE[sdk=iphone*] = YES; ENABLE_BITCODE[sdk=watch*] = YES; ENABLE_BITCODE[sdk=appletv*] = YES; LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks; ================================================ FILE: Lib/Configurations/Release.xcconfig ================================================ #include "Base.xcconfig" BITCODE_GENERATION_MODE = bitcode; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; MTL_ENABLE_DEBUG_INFO = NO; VALIDATE_PRODUCT = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = -Owholemodule; ================================================ FILE: Lib/Configurations/TestHost.xcconfig ================================================ SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator; TARGETED_DEVICE_FAMILY = 1,2,3; SUPPORTS_MACCATALYST = YES; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; INFOPLIST_FILE = TestHost/Info.plist; PRODUCT_NAME = $(TARGET_NAME); CLANG_MODULES_AUTOLINK = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.KeychainAccess.TestHost; CODE_SIGN_ENTITLEMENTS = TestHost/TestHost.entitlements; CODE_SIGN_IDENTITY[sdk=iphone*] = iPhone Developer; CODE_SIGN_IDENTITY[sdk=macosx*] = Developer ID Application; PROVISIONING_PROFILE_SPECIFIER[sdk=iphone*] = iOS Development; PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*] = KeychainAccess Tests; DEVELOPMENT_TEAM = 27AEDK3C9F; PRINCIPAL_CLASS[sdk=iphone*] = UIApplication; PRINCIPAL_CLASS[sdk=appletv*] = UIApplication; PRINCIPAL_CLASS[sdk=macosx*] = NSApplication; LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks; ================================================ FILE: Lib/Configurations/Tests.xcconfig ================================================ SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator; TARGETED_DEVICE_FAMILY = 1,2; SUPPORTS_MACCATALYST = YES; COMBINE_HIDPI_IMAGES = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier); PRODUCT_NAME = $(TARGET_NAME); APPLICATION_EXTENSION_API_ONLY = NO; INFOPLIST_FILE = KeychainAccessTests/Info.plist; LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks; TEST_HOST[sdk=iphone*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost; TEST_HOST[sdk=appletv*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost; TEST_HOST[sdk=macosx*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/Contents/MacOS/TestHost; EXCLUDED_SOURCE_FILE_NAMES[sdk=watch*] = *; EXCLUDED_SOURCE_FILE_NAMES[sdk=appletv*] = SharedCredentialTests.swift; EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = SharedCredentialTests.swift; DEVELOPMENT_TEAM = 27AEDK3C9F; ================================================ FILE: Lib/KeychainAccess/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 4.2.2 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: Lib/KeychainAccess/Keychain.swift ================================================ // // Keychain.swift // KeychainAccess // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation import Security #if os(iOS) || os(macOS) import LocalAuthentication #endif public let KeychainAccessErrorDomain = "com.kishikawakatsumi.KeychainAccess.error" public enum ItemClass { case genericPassword case internetPassword } public enum ProtocolType { case ftp case ftpAccount case http case irc case nntp case pop3 case smtp case socks case imap case ldap case appleTalk case afp case telnet case ssh case ftps case https case httpProxy case httpsProxy case ftpProxy case smb case rtsp case rtspProxy case daap case eppc case ipp case nntps case ldaps case telnetS case imaps case ircs case pop3S } public enum AuthenticationType { case ntlm case msn case dpa case rpa case httpBasic case httpDigest case htmlForm case `default` } public enum Accessibility { /** Item data can only be accessed while the device is unlocked. This is recommended for items that only need be accesible while the application is in the foreground. Items with this attribute will migrate to a new device when using encrypted backups. */ case whenUnlocked /** Item data can only be accessed once the device has been unlocked after a restart. This is recommended for items that need to be accesible by background applications. Items with this attribute will migrate to a new device when using encrypted backups. */ case afterFirstUnlock /** Item data can always be accessed regardless of the lock state of the device. This is not recommended for anything except system use. Items with this attribute will migrate to a new device when using encrypted backups. */ @available(macCatalyst, unavailable) case always /** Item data can only be accessed while the device is unlocked. This class is only available if a passcode is set on the device. This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode will cause all items in this class to be deleted. */ @available(iOS 8.0, macOS 10.10, *) case whenPasscodeSetThisDeviceOnly /** Item data can only be accessed while the device is unlocked. This is recommended for items that only need be accesible while the application is in the foreground. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. */ case whenUnlockedThisDeviceOnly /** Item data can only be accessed once the device has been unlocked after a restart. This is recommended for items that need to be accessible by background applications. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device these items will be missing. */ case afterFirstUnlockThisDeviceOnly /** Item data can always be accessed regardless of the lock state of the device. This option is not recommended for anything except system use. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. */ @available(macCatalyst, unavailable) case alwaysThisDeviceOnly } /** Predefined item attribute constants used to get or set values in a dictionary. The kSecUseAuthenticationUI constant is the key and its value is one of the constants defined here. If the key kSecUseAuthenticationUI not provided then kSecUseAuthenticationUIAllow is used as default. */ public enum AuthenticationUI { /** Specifies that authenticate UI can appear. */ case allow /** Specifies that the error errSecInteractionNotAllowed will be returned if an item needs to authenticate with UI */ case fail /** Specifies that all items which need to authenticate with UI will be silently skipped. This value can be used only with SecItemCopyMatching. */ case skip } @available(iOS 9.0, macOS 10.11, *) extension AuthenticationUI { public var rawValue: String { switch self { case .allow: return UseAuthenticationUIAllow case .fail: return UseAuthenticationUIFail case .skip: return UseAuthenticationUISkip } } public var description: String { switch self { case .allow: return "allow" case .fail: return "fail" case .skip: return "skip" } } } public struct AuthenticationPolicy: OptionSet { /** User presence policy using Touch ID or Passcode. Touch ID does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed. */ @available(iOS 8.0, macOS 10.10, watchOS 2.0, tvOS 8.0, *) public static let userPresence = AuthenticationPolicy(rawValue: 1 << 0) /** Constraint: Touch ID (any finger) or Face ID. Touch ID or Face ID must be available. With Touch ID at least one finger must be enrolled. With Face ID user has to be enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. */ @available(iOS 11.3, macOS 10.13.4, watchOS 4.3, tvOS 11.3, *) public static let biometryAny = AuthenticationPolicy(rawValue: 1 << 1) /** Deprecated, please use biometryAny instead. */ @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny") @available(macOS, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryAny") @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryAny") @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny") public static let touchIDAny = AuthenticationPolicy(rawValue: 1 << 1) /** Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated. When Face ID is re-enrolled this item is invalidated. */ @available(iOS 11.3, macOS 10.13, watchOS 4.3, tvOS 11.3, *) public static let biometryCurrentSet = AuthenticationPolicy(rawValue: 1 << 3) /** Deprecated, please use biometryCurrentSet instead. */ @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet") @available(macOS, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryCurrentSet") @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryCurrentSet") @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet") public static let touchIDCurrentSet = AuthenticationPolicy(rawValue: 1 << 3) /** Constraint: Device passcode */ @available(iOS 9.0, macOS 10.11, watchOS 2.0, tvOS 9.0, *) public static let devicePasscode = AuthenticationPolicy(rawValue: 1 << 4) /** Constraint: Watch */ @available(iOS, unavailable) @available(macOS 10.15, *) @available(watchOS, unavailable) @available(tvOS, unavailable) public static let watch = AuthenticationPolicy(rawValue: 1 << 5) /** Constraint logic operation: when using more than one constraint, at least one of them must be satisfied. */ @available(iOS 9.0, macOS 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let or = AuthenticationPolicy(rawValue: 1 << 14) /** Constraint logic operation: when using more than one constraint, all must be satisfied. */ @available(iOS 9.0, macOS 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let and = AuthenticationPolicy(rawValue: 1 << 15) /** Create access control for private key operations (i.e. sign operation) */ @available(iOS 9.0, macOS 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let privateKeyUsage = AuthenticationPolicy(rawValue: 1 << 30) /** Security: Application provided password for data encryption key generation. This is not a constraint but additional item encryption mechanism. */ @available(iOS 9.0, macOS 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let applicationPassword = AuthenticationPolicy(rawValue: 1 << 31) #if swift(>=2.3) public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } #else public let rawValue: Int public init(rawValue: Int) { self.rawValue = rawValue } #endif } public struct Attributes { public var `class`: String? { return attributes[Class] as? String } public var data: Data? { return attributes[ValueData] as? Data } public var ref: Data? { return attributes[ValueRef] as? Data } public var persistentRef: Data? { return attributes[ValuePersistentRef] as? Data } public var accessible: String? { return attributes[AttributeAccessible] as? String } public var accessControl: SecAccessControl? { if #available(macOS 10.10, *) { if let accessControl = attributes[AttributeAccessControl] { return (accessControl as! SecAccessControl) } return nil } else { return nil } } public var accessGroup: String? { return attributes[AttributeAccessGroup] as? String } public var synchronizable: Bool? { return attributes[AttributeSynchronizable] as? Bool } public var creationDate: Date? { return attributes[AttributeCreationDate] as? Date } public var modificationDate: Date? { return attributes[AttributeModificationDate] as? Date } public var attributeDescription: String? { return attributes[AttributeDescription] as? String } public var comment: String? { return attributes[AttributeComment] as? String } public var creator: String? { return attributes[AttributeCreator] as? String } public var type: String? { return attributes[AttributeType] as? String } public var label: String? { return attributes[AttributeLabel] as? String } public var isInvisible: Bool? { return attributes[AttributeIsInvisible] as? Bool } public var isNegative: Bool? { return attributes[AttributeIsNegative] as? Bool } public var account: String? { return attributes[AttributeAccount] as? String } public var service: String? { return attributes[AttributeService] as? String } public var generic: Data? { return attributes[AttributeGeneric] as? Data } public var securityDomain: String? { return attributes[AttributeSecurityDomain] as? String } public var server: String? { return attributes[AttributeServer] as? String } public var `protocol`: String? { return attributes[AttributeProtocol] as? String } public var authenticationType: String? { return attributes[AttributeAuthenticationType] as? String } public var port: Int? { return attributes[AttributePort] as? Int } public var path: String? { return attributes[AttributePath] as? String } fileprivate let attributes: [String: Any] init(attributes: [String: Any]) { self.attributes = attributes } public subscript(key: String) -> Any? { get { return attributes[key] } } } public final class Keychain { public var itemClass: ItemClass { return options.itemClass } public var service: String { return options.service } // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. public var accessGroup: String? { return options.accessGroup } public var server: URL { return options.server } public var protocolType: ProtocolType { return options.protocolType } public var authenticationType: AuthenticationType { return options.authenticationType } public var accessibility: Accessibility { return options.accessibility } @available(iOS 8.0, macOS 10.10, *) @available(watchOS, unavailable) public var authenticationPolicy: AuthenticationPolicy? { return options.authenticationPolicy } public var synchronizable: Bool { return options.synchronizable } public var label: String? { return options.label } public var comment: String? { return options.comment } @available(iOS 8.0, macOS 10.10, *) @available(watchOS, unavailable) public var authenticationPrompt: String? { return options.authenticationPrompt } @available(iOS 9.0, macOS 10.11, *) public var authenticationUI: AuthenticationUI { return options.authenticationUI ?? .allow } #if os(iOS) || os(macOS) @available(iOS 9.0, macOS 10.11, *) public var authenticationContext: LAContext? { return options.authenticationContext as? LAContext } #endif fileprivate let options: Options // MARK: public convenience init() { var options = Options() if let bundleIdentifier = Bundle.main.bundleIdentifier { options.service = bundleIdentifier } self.init(options) } public convenience init(accessGroup: String) { var options = Options() if let bundleIdentifier = Bundle.main.bundleIdentifier { options.service = bundleIdentifier } options.accessGroup = accessGroup self.init(options) } public convenience init(service: String, accessGroup: String? = nil) { var options = Options() options.service = service options.accessGroup = accessGroup self.init(options) } public convenience init(server: String, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) { self.init(server: URL(string: server)!, protocolType: protocolType, accessGroup: accessGroup, authenticationType: authenticationType) } public convenience init(server: URL, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) { var options = Options() options.itemClass = .internetPassword options.server = server options.protocolType = protocolType options.accessGroup = accessGroup options.authenticationType = authenticationType self.init(options) } fileprivate init(_ opts: Options) { options = opts } // MARK: public func accessibility(_ accessibility: Accessibility) -> Keychain { var options = self.options options.accessibility = accessibility return Keychain(options) } @available(iOS 8.0, macOS 10.10, *) @available(watchOS, unavailable) public func accessibility(_ accessibility: Accessibility, authenticationPolicy: AuthenticationPolicy) -> Keychain { var options = self.options options.accessibility = accessibility options.authenticationPolicy = authenticationPolicy return Keychain(options) } public func synchronizable(_ synchronizable: Bool) -> Keychain { var options = self.options options.synchronizable = synchronizable return Keychain(options) } public func label(_ label: String) -> Keychain { var options = self.options options.label = label return Keychain(options) } public func comment(_ comment: String) -> Keychain { var options = self.options options.comment = comment return Keychain(options) } public func attributes(_ attributes: [String: Any]) -> Keychain { var options = self.options attributes.forEach { options.attributes.updateValue($1, forKey: $0) } return Keychain(options) } @available(iOS 8.0, macOS 10.10, *) @available(watchOS, unavailable) public func authenticationPrompt(_ authenticationPrompt: String) -> Keychain { var options = self.options options.authenticationPrompt = authenticationPrompt return Keychain(options) } @available(iOS 9.0, macOS 10.11, *) public func authenticationUI(_ authenticationUI: AuthenticationUI) -> Keychain { var options = self.options options.authenticationUI = authenticationUI return Keychain(options) } #if os(iOS) || os(macOS) @available(iOS 9.0, macOS 10.11, *) public func authenticationContext(_ authenticationContext: LAContext) -> Keychain { var options = self.options options.authenticationContext = authenticationContext return Keychain(options) } #endif // MARK: public func get(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> String? { return try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) } public func getString(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> String? { guard let data = try getData(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else { return nil } guard let string = String(data: data, encoding: .utf8) else { print("failed to convert data to string") throw Status.conversionError } return string } public func getData(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Data? { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[MatchLimit] = MatchLimitOne query[ReturnData] = kCFBooleanTrue query[AttributeAccount] = key var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: guard let data = result as? Data else { throw Status.unexpectedError } return data case errSecItemNotFound: return nil default: throw securityError(status: status) } } public func get(_ key: String, ignoringAttributeSynchronizable: Bool = true, handler: (Attributes?) -> T) throws -> T { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[MatchLimit] = MatchLimitOne query[ReturnData] = kCFBooleanTrue query[ReturnAttributes] = kCFBooleanTrue query[ReturnRef] = kCFBooleanTrue query[ReturnPersistentRef] = kCFBooleanTrue query[AttributeAccount] = key var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: guard let attributes = result as? [String: Any] else { throw Status.unexpectedError } return handler(Attributes(attributes: attributes)) case errSecItemNotFound: return handler(nil) default: throw securityError(status: status) } } // MARK: public func set(_ value: String, key: String, ignoringAttributeSynchronizable: Bool = true) throws { guard let data = value.data(using: .utf8, allowLossyConversion: false) else { print("failed to convert string to data") throw Status.conversionError } try set(data, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) } public func set(_ value: Data, key: String, ignoringAttributeSynchronizable: Bool = true) throws { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[AttributeAccount] = key #if os(iOS) if #available(iOS 9.0, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else { query[UseNoAuthenticationUI] = kCFBooleanTrue } #elseif os(macOS) query[ReturnData] = kCFBooleanTrue if #available(macOS 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } #else if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } #endif var status = SecItemCopyMatching(query as CFDictionary, nil) switch status { case errSecSuccess, errSecInteractionNotAllowed: var query = options.query() query[AttributeAccount] = key var (attributes, error) = options.attributes(key: nil, value: value) if let error = error { print(error.localizedDescription) throw error } options.attributes.forEach { attributes.updateValue($1, forKey: $0) } #if os(iOS) if status == errSecInteractionNotAllowed && floor(NSFoundationVersionNumber) <= floor(NSFoundationVersionNumber_iOS_8_0) { try remove(key) try set(value, key: key) } else { status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if status != errSecSuccess { throw securityError(status: status) } } #else status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if status != errSecSuccess { throw securityError(status: status) } #endif case errSecItemNotFound: var (attributes, error) = options.attributes(key: key, value: value) if let error = error { print(error.localizedDescription) throw error } options.attributes.forEach { attributes.updateValue($1, forKey: $0) } status = SecItemAdd(attributes as CFDictionary, nil) if status != errSecSuccess { throw securityError(status: status) } default: throw securityError(status: status) } } public subscript(key: String) -> String? { get { #if swift(>=5.0) return try? get(key) #else return (try? get(key)).flatMap { $0 } #endif } set { if let value = newValue { do { try set(value, key: key) } catch {} } else { do { try remove(key) } catch {} } } } public subscript(string key: String) -> String? { get { return self[key] } set { self[key] = newValue } } public subscript(data key: String) -> Data? { get { #if swift(>=5.0) return try? getData(key) #else return (try? getData(key)).flatMap { $0 } #endif } set { if let value = newValue { do { try set(value, key: key) } catch {} } else { do { try remove(key) } catch {} } } } public subscript(attributes key: String) -> Attributes? { get { #if swift(>=5.0) return try? get(key) { $0 } #else return (try? get(key) { $0 }).flatMap { $0 } #endif } } // MARK: public func remove(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[AttributeAccount] = key let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { throw securityError(status: status) } } public func removeAll() throws { var query = options.query() #if !os(iOS) && !os(watchOS) && !os(tvOS) query[MatchLimit] = MatchLimitAll #endif let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { throw securityError(status: status) } } // MARK: public func contains(_ key: String, withoutAuthenticationUI: Bool = false) throws -> Bool { var query = options.query() query[AttributeAccount] = key if withoutAuthenticationUI { #if os(iOS) || os(watchOS) || os(tvOS) if #available(iOS 9.0, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else { query[UseNoAuthenticationUI] = kCFBooleanTrue } #else if #available(macOS 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else if #available(macOS 10.10, *) { query[UseNoAuthenticationUI] = kCFBooleanTrue } #endif } else { if #available(iOS 9.0, macOS 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } } } let status = SecItemCopyMatching(query as CFDictionary, nil) switch status { case errSecSuccess: return true case errSecInteractionNotAllowed: if withoutAuthenticationUI { return true } return false case errSecItemNotFound: return false default: throw securityError(status: status) } } // MARK: public class func allKeys(_ itemClass: ItemClass) -> [(String, String)] { var query = [String: Any]() query[Class] = itemClass.rawValue query[AttributeSynchronizable] = SynchronizableAny query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return prettify(itemClass: itemClass, items: items).map { switch itemClass { case .genericPassword: return (($0["service"] ?? "") as! String, ($0["key"] ?? "") as! String) case .internetPassword: return (($0["server"] ?? "") as! String, ($0["key"] ?? "") as! String) } } } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } public func allKeys() -> [String] { let allItems = type(of: self).prettify(itemClass: itemClass, items: items()) let filter: ([String: Any]) -> String? = { $0["key"] as? String } #if swift(>=4.1) return allItems.compactMap(filter) #else return allItems.flatMap(filter) #endif } public class func allItems(_ itemClass: ItemClass) -> [[String: Any]] { var query = [String: Any]() query[Class] = itemClass.rawValue query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue #if os(iOS) || os(watchOS) || os(tvOS) query[ReturnData] = kCFBooleanTrue #endif var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return prettify(itemClass: itemClass, items: items) } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } public func allItems() -> [[String: Any]] { return type(of: self).prettify(itemClass: itemClass, items: items()) } #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func getSharedPassword(_ completion: @escaping (_ account: String?, _ password: String?, _ error: Error?) -> () = { account, password, error -> () in }) { if let domain = server.host { type(of: self).requestSharedWebCredential(domain: domain, account: nil) { (credentials, error) -> () in if let credential = credentials.first { let account = credential["account"] let password = credential["password"] completion(account, password, error) } else { completion(nil, nil, error) } } } else { let error = securityError(status: Status.param.rawValue) completion(nil, nil, error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func getSharedPassword(_ account: String, completion: @escaping (_ password: String?, _ error: Error?) -> () = { password, error -> () in }) { if let domain = server.host { type(of: self).requestSharedWebCredential(domain: domain, account: account) { (credentials, error) -> () in if let credential = credentials.first { if let password = credential["password"] { completion(password, error) } else { completion(nil, error) } } else { completion(nil, error) } } } else { let error = securityError(status: Status.param.rawValue) completion(nil, error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func setSharedPassword(_ password: String, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { setSharedPassword(password as String?, account: account, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) fileprivate func setSharedPassword(_ password: String?, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { if let domain = server.host { SecAddSharedWebCredential(domain as CFString, account as CFString, password as CFString?) { error -> () in if let error = error { completion(error.error) } else { completion(nil) } } } else { let error = securityError(status: Status.param.rawValue) completion(error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func removeSharedPassword(_ account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { setSharedPassword(nil, account: account, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(_ completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: nil, account: nil, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(domain: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: domain, account: nil, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(domain: String, account: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: Optional(domain), account: Optional(account)!, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) fileprivate class func requestSharedWebCredential(domain: String?, account: String?, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> ()) { SecRequestSharedWebCredential(domain as CFString?, account as CFString?) { (credentials, error) -> () in var remoteError: NSError? if let error = error { remoteError = error.error if remoteError?.code != Int(errSecItemNotFound) { print("error:[\(remoteError!.code)] \(remoteError!.localizedDescription)") } } if let credentials = credentials { let credentials = (credentials as NSArray).map { credentials -> [String: String] in var credential = [String: String]() if let credentials = credentials as? [String: String] { if let server = credentials[AttributeServer] { credential["server"] = server } if let account = credentials[AttributeAccount] { credential["account"] = account } if let password = credentials[SharedPassword] { credential["password"] = password } } return credential } completion(credentials, remoteError) } else { completion([], remoteError) } } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) /** @abstract Returns a randomly generated password. @return String password in the form xxx-xxx-xxx-xxx where x is taken from the sets "abcdefghkmnopqrstuvwxy", "ABCDEFGHJKLMNPQRSTUVWXYZ", "3456789" with at least one character from each set being present. */ @available(iOS 8.0, *) public class func generatePassword() -> String { return SecCreateSharedWebCredentialPassword()! as String } #endif // MARK: fileprivate func items() -> [[String: Any]] { var query = options.query() query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue #if os(iOS) || os(watchOS) || os(tvOS) query[ReturnData] = kCFBooleanTrue #endif var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return items } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } fileprivate class func prettify(itemClass: ItemClass, items: [[String: Any]]) -> [[String: Any]] { let items = items.map { attributes -> [String: Any] in var item = [String: Any]() item["class"] = itemClass.description if let accessGroup = attributes[AttributeAccessGroup] as? String { item["accessGroup"] = accessGroup } switch itemClass { case .genericPassword: if let service = attributes[AttributeService] as? String { item["service"] = service } case .internetPassword: if let server = attributes[AttributeServer] as? String { item["server"] = server } if let proto = attributes[AttributeProtocol] as? String { if let protocolType = ProtocolType(rawValue: proto) { item["protocol"] = protocolType.description } } if let auth = attributes[AttributeAuthenticationType] as? String { if let authenticationType = AuthenticationType(rawValue: auth) { item["authenticationType"] = authenticationType.description } } } if let key = attributes[AttributeAccount] as? String { item["key"] = key } if let data = attributes[ValueData] as? Data { if let text = String(data: data, encoding: .utf8) { item["value"] = text } else { item["value"] = data } } if let accessible = attributes[AttributeAccessible] as? String { if let accessibility = Accessibility(rawValue: accessible) { item["accessibility"] = accessibility.description } } if let synchronizable = attributes[AttributeSynchronizable] as? Bool { item["synchronizable"] = synchronizable ? "true" : "false" } return item } return items } // MARK: @discardableResult fileprivate class func securityError(status: OSStatus) -> Error { let error = Status(status: status) if error != .userCanceled { print("OSStatus error:[\(error.errorCode)] \(error.description)") } return error } @discardableResult fileprivate func securityError(status: OSStatus) -> Error { return type(of: self).securityError(status: status) } } struct Options { var itemClass: ItemClass = .genericPassword var service: String = "" var accessGroup: String? = nil var server: URL! var protocolType: ProtocolType! var authenticationType: AuthenticationType = .default var accessibility: Accessibility = .afterFirstUnlock var authenticationPolicy: AuthenticationPolicy? var synchronizable: Bool = false var label: String? var comment: String? var authenticationPrompt: String? var authenticationUI: AuthenticationUI? var authenticationContext: AnyObject? var attributes = [String: Any]() } /** Class Key Constant */ private let Class = String(kSecClass) /** Attribute Key Constants */ private let AttributeAccessible = String(kSecAttrAccessible) @available(iOS 8.0, macOS 10.10, *) private let AttributeAccessControl = String(kSecAttrAccessControl) private let AttributeAccessGroup = String(kSecAttrAccessGroup) private let AttributeSynchronizable = String(kSecAttrSynchronizable) private let AttributeCreationDate = String(kSecAttrCreationDate) private let AttributeModificationDate = String(kSecAttrModificationDate) private let AttributeDescription = String(kSecAttrDescription) private let AttributeComment = String(kSecAttrComment) private let AttributeCreator = String(kSecAttrCreator) private let AttributeType = String(kSecAttrType) private let AttributeLabel = String(kSecAttrLabel) private let AttributeIsInvisible = String(kSecAttrIsInvisible) private let AttributeIsNegative = String(kSecAttrIsNegative) private let AttributeAccount = String(kSecAttrAccount) private let AttributeService = String(kSecAttrService) private let AttributeGeneric = String(kSecAttrGeneric) private let AttributeSecurityDomain = String(kSecAttrSecurityDomain) private let AttributeServer = String(kSecAttrServer) private let AttributeProtocol = String(kSecAttrProtocol) private let AttributeAuthenticationType = String(kSecAttrAuthenticationType) private let AttributePort = String(kSecAttrPort) private let AttributePath = String(kSecAttrPath) private let SynchronizableAny = kSecAttrSynchronizableAny /** Search Constants */ private let MatchLimit = String(kSecMatchLimit) private let MatchLimitOne = kSecMatchLimitOne private let MatchLimitAll = kSecMatchLimitAll /** Return Type Key Constants */ private let ReturnData = String(kSecReturnData) private let ReturnAttributes = String(kSecReturnAttributes) private let ReturnRef = String(kSecReturnRef) private let ReturnPersistentRef = String(kSecReturnPersistentRef) /** Value Type Key Constants */ private let ValueData = String(kSecValueData) private let ValueRef = String(kSecValueRef) private let ValuePersistentRef = String(kSecValuePersistentRef) /** Other Constants */ @available(iOS 8.0, macOS 10.10, tvOS 8.0, *) private let UseOperationPrompt = String(kSecUseOperationPrompt) @available(iOS, introduced: 8.0, deprecated: 9.0, message: "Use a UseAuthenticationUI instead.") @available(macOS, introduced: 10.10, deprecated: 10.11, message: "Use UseAuthenticationUI instead.") @available(watchOS, introduced: 2.0, deprecated: 2.0, message: "Use UseAuthenticationUI instead.") @available(tvOS, introduced: 8.0, deprecated: 9.0, message: "Use UseAuthenticationUI instead.") private let UseNoAuthenticationUI = String(kSecUseNoAuthenticationUI) @available(iOS 9.0, macOS 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUI = String(kSecUseAuthenticationUI) @available(iOS 9.0, macOS 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationContext = String(kSecUseAuthenticationContext) @available(iOS 9.0, macOS 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUIAllow = String(kSecUseAuthenticationUIAllow) @available(iOS 9.0, macOS 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUIFail = String(kSecUseAuthenticationUIFail) @available(iOS 9.0, macOS 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUISkip = String(kSecUseAuthenticationUISkip) #if os(iOS) && !targetEnvironment(macCatalyst) /** Credential Key Constants */ private let SharedPassword = String(kSecSharedPassword) #endif extension Keychain: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { let items = allItems() if items.isEmpty { return "[]" } var description = "[\n" for item in items { description += " " description += "\(item)\n" } description += "]" return description } public var debugDescription: String { return "\(items())" } } extension Options { func query(ignoringAttributeSynchronizable: Bool = true) -> [String: Any] { var query = [String: Any]() query[Class] = itemClass.rawValue if let accessGroup = self.accessGroup { query[AttributeAccessGroup] = accessGroup } if ignoringAttributeSynchronizable { query[AttributeSynchronizable] = SynchronizableAny } else { query[AttributeSynchronizable] = synchronizable ? kCFBooleanTrue : kCFBooleanFalse } switch itemClass { case .genericPassword: query[AttributeService] = service case .internetPassword: query[AttributeServer] = server.host query[AttributePort] = server.port query[AttributeProtocol] = protocolType.rawValue query[AttributeAuthenticationType] = authenticationType.rawValue } if #available(macOS 10.10, *) { if authenticationPrompt != nil { query[UseOperationPrompt] = authenticationPrompt } } #if !os(watchOS) if #available(iOS 9.0, macOS 10.11, *) { if authenticationContext != nil { query[UseAuthenticationContext] = authenticationContext } } #endif return query } func attributes(key: String?, value: Data) -> ([String: Any], Error?) { var attributes: [String: Any] if key != nil { attributes = query() attributes[AttributeAccount] = key } else { attributes = [String: Any]() } attributes[ValueData] = value if label != nil { attributes[AttributeLabel] = label } if comment != nil { attributes[AttributeComment] = comment } if let policy = authenticationPolicy { if #available(macOS 10.10, *) { var error: Unmanaged? guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, SecAccessControlCreateFlags(rawValue: CFOptionFlags(policy.rawValue)), &error) else { if let error = error?.takeUnretainedValue() { return (attributes, error.error) } return (attributes, Status.unexpectedError) } attributes[AttributeAccessControl] = accessControl } else { print("Unavailable 'Touch ID integration' on macOS versions prior to 10.10.") } } else { attributes[AttributeAccessible] = accessibility.rawValue } attributes[AttributeSynchronizable] = synchronizable ? kCFBooleanTrue : kCFBooleanFalse return (attributes, nil) } } // MARK: extension Attributes: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { return "\(attributes)" } public var debugDescription: String { return description } } extension ItemClass: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecClassGenericPassword): self = .genericPassword case String(kSecClassInternetPassword): self = .internetPassword default: return nil } } public var rawValue: String { switch self { case .genericPassword: return String(kSecClassGenericPassword) case .internetPassword: return String(kSecClassInternetPassword) } } public var description: String { switch self { case .genericPassword: return "GenericPassword" case .internetPassword: return "InternetPassword" } } } extension ProtocolType: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecAttrProtocolFTP): self = .ftp case String(kSecAttrProtocolFTPAccount): self = .ftpAccount case String(kSecAttrProtocolHTTP): self = .http case String(kSecAttrProtocolIRC): self = .irc case String(kSecAttrProtocolNNTP): self = .nntp case String(kSecAttrProtocolPOP3): self = .pop3 case String(kSecAttrProtocolSMTP): self = .smtp case String(kSecAttrProtocolSOCKS): self = .socks case String(kSecAttrProtocolIMAP): self = .imap case String(kSecAttrProtocolLDAP): self = .ldap case String(kSecAttrProtocolAppleTalk): self = .appleTalk case String(kSecAttrProtocolAFP): self = .afp case String(kSecAttrProtocolTelnet): self = .telnet case String(kSecAttrProtocolSSH): self = .ssh case String(kSecAttrProtocolFTPS): self = .ftps case String(kSecAttrProtocolHTTPS): self = .https case String(kSecAttrProtocolHTTPProxy): self = .httpProxy case String(kSecAttrProtocolHTTPSProxy): self = .httpsProxy case String(kSecAttrProtocolFTPProxy): self = .ftpProxy case String(kSecAttrProtocolSMB): self = .smb case String(kSecAttrProtocolRTSP): self = .rtsp case String(kSecAttrProtocolRTSPProxy): self = .rtspProxy case String(kSecAttrProtocolDAAP): self = .daap case String(kSecAttrProtocolEPPC): self = .eppc case String(kSecAttrProtocolIPP): self = .ipp case String(kSecAttrProtocolNNTPS): self = .nntps case String(kSecAttrProtocolLDAPS): self = .ldaps case String(kSecAttrProtocolTelnetS): self = .telnetS case String(kSecAttrProtocolIMAPS): self = .imaps case String(kSecAttrProtocolIRCS): self = .ircs case String(kSecAttrProtocolPOP3S): self = .pop3S default: return nil } } public var rawValue: String { switch self { case .ftp: return String(kSecAttrProtocolFTP) case .ftpAccount: return String(kSecAttrProtocolFTPAccount) case .http: return String(kSecAttrProtocolHTTP) case .irc: return String(kSecAttrProtocolIRC) case .nntp: return String(kSecAttrProtocolNNTP) case .pop3: return String(kSecAttrProtocolPOP3) case .smtp: return String(kSecAttrProtocolSMTP) case .socks: return String(kSecAttrProtocolSOCKS) case .imap: return String(kSecAttrProtocolIMAP) case .ldap: return String(kSecAttrProtocolLDAP) case .appleTalk: return String(kSecAttrProtocolAppleTalk) case .afp: return String(kSecAttrProtocolAFP) case .telnet: return String(kSecAttrProtocolTelnet) case .ssh: return String(kSecAttrProtocolSSH) case .ftps: return String(kSecAttrProtocolFTPS) case .https: return String(kSecAttrProtocolHTTPS) case .httpProxy: return String(kSecAttrProtocolHTTPProxy) case .httpsProxy: return String(kSecAttrProtocolHTTPSProxy) case .ftpProxy: return String(kSecAttrProtocolFTPProxy) case .smb: return String(kSecAttrProtocolSMB) case .rtsp: return String(kSecAttrProtocolRTSP) case .rtspProxy: return String(kSecAttrProtocolRTSPProxy) case .daap: return String(kSecAttrProtocolDAAP) case .eppc: return String(kSecAttrProtocolEPPC) case .ipp: return String(kSecAttrProtocolIPP) case .nntps: return String(kSecAttrProtocolNNTPS) case .ldaps: return String(kSecAttrProtocolLDAPS) case .telnetS: return String(kSecAttrProtocolTelnetS) case .imaps: return String(kSecAttrProtocolIMAPS) case .ircs: return String(kSecAttrProtocolIRCS) case .pop3S: return String(kSecAttrProtocolPOP3S) } } public var description: String { switch self { case .ftp: return "FTP" case .ftpAccount: return "FTPAccount" case .http: return "HTTP" case .irc: return "IRC" case .nntp: return "NNTP" case .pop3: return "POP3" case .smtp: return "SMTP" case .socks: return "SOCKS" case .imap: return "IMAP" case .ldap: return "LDAP" case .appleTalk: return "AppleTalk" case .afp: return "AFP" case .telnet: return "Telnet" case .ssh: return "SSH" case .ftps: return "FTPS" case .https: return "HTTPS" case .httpProxy: return "HTTPProxy" case .httpsProxy: return "HTTPSProxy" case .ftpProxy: return "FTPProxy" case .smb: return "SMB" case .rtsp: return "RTSP" case .rtspProxy: return "RTSPProxy" case .daap: return "DAAP" case .eppc: return "EPPC" case .ipp: return "IPP" case .nntps: return "NNTPS" case .ldaps: return "LDAPS" case .telnetS: return "TelnetS" case .imaps: return "IMAPS" case .ircs: return "IRCS" case .pop3S: return "POP3S" } } } extension AuthenticationType: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecAttrAuthenticationTypeNTLM): self = .ntlm case String(kSecAttrAuthenticationTypeMSN): self = .msn case String(kSecAttrAuthenticationTypeDPA): self = .dpa case String(kSecAttrAuthenticationTypeRPA): self = .rpa case String(kSecAttrAuthenticationTypeHTTPBasic): self = .httpBasic case String(kSecAttrAuthenticationTypeHTTPDigest): self = .httpDigest case String(kSecAttrAuthenticationTypeHTMLForm): self = .htmlForm case String(kSecAttrAuthenticationTypeDefault): self = .`default` default: return nil } } public var rawValue: String { switch self { case .ntlm: return String(kSecAttrAuthenticationTypeNTLM) case .msn: return String(kSecAttrAuthenticationTypeMSN) case .dpa: return String(kSecAttrAuthenticationTypeDPA) case .rpa: return String(kSecAttrAuthenticationTypeRPA) case .httpBasic: return String(kSecAttrAuthenticationTypeHTTPBasic) case .httpDigest: return String(kSecAttrAuthenticationTypeHTTPDigest) case .htmlForm: return String(kSecAttrAuthenticationTypeHTMLForm) case .`default`: return String(kSecAttrAuthenticationTypeDefault) } } public var description: String { switch self { case .ntlm: return "NTLM" case .msn: return "MSN" case .dpa: return "DPA" case .rpa: return "RPA" case .httpBasic: return "HTTPBasic" case .httpDigest: return "HTTPDigest" case .htmlForm: return "HTMLForm" case .`default`: return "Default" } } } extension Accessibility: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { if #available(macOS 10.10, *) { switch rawValue { case String(kSecAttrAccessibleWhenUnlocked): self = .whenUnlocked case String(kSecAttrAccessibleAfterFirstUnlock): self = .afterFirstUnlock #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlways): self = .always #endif case String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly): self = .whenPasscodeSetThisDeviceOnly case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly): self = .whenUnlockedThisDeviceOnly case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly): self = .afterFirstUnlockThisDeviceOnly #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlwaysThisDeviceOnly): self = .alwaysThisDeviceOnly #endif default: return nil } } else { switch rawValue { case String(kSecAttrAccessibleWhenUnlocked): self = .whenUnlocked case String(kSecAttrAccessibleAfterFirstUnlock): self = .afterFirstUnlock #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlways): self = .always #endif case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly): self = .whenUnlockedThisDeviceOnly case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly): self = .afterFirstUnlockThisDeviceOnly #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlwaysThisDeviceOnly): self = .alwaysThisDeviceOnly #endif default: return nil } } } public var rawValue: String { switch self { case .whenUnlocked: return String(kSecAttrAccessibleWhenUnlocked) case .afterFirstUnlock: return String(kSecAttrAccessibleAfterFirstUnlock) #if !targetEnvironment(macCatalyst) case .always: return String(kSecAttrAccessibleAlways) #endif case .whenPasscodeSetThisDeviceOnly: if #available(macOS 10.10, *) { return String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) } else { fatalError("'Accessibility.WhenPasscodeSetThisDeviceOnly' is not available on this version of OS.") } case .whenUnlockedThisDeviceOnly: return String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) case .afterFirstUnlockThisDeviceOnly: return String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) #if !targetEnvironment(macCatalyst) case .alwaysThisDeviceOnly: return String(kSecAttrAccessibleAlwaysThisDeviceOnly) #endif } } public var description: String { switch self { case .whenUnlocked: return "WhenUnlocked" case .afterFirstUnlock: return "AfterFirstUnlock" #if !targetEnvironment(macCatalyst) case .always: return "Always" #endif case .whenPasscodeSetThisDeviceOnly: return "WhenPasscodeSetThisDeviceOnly" case .whenUnlockedThisDeviceOnly: return "WhenUnlockedThisDeviceOnly" case .afterFirstUnlockThisDeviceOnly: return "AfterFirstUnlockThisDeviceOnly" #if !targetEnvironment(macCatalyst) case .alwaysThisDeviceOnly: return "AlwaysThisDeviceOnly" #endif } } } extension CFError { var error: NSError { let domain = CFErrorGetDomain(self) as String let code = CFErrorGetCode(self) let userInfo = CFErrorCopyUserInfo(self) as! [String: Any] return NSError(domain: domain, code: code, userInfo: userInfo) } } public enum Status: OSStatus, Error { case success = 0 case unimplemented = -4 case diskFull = -34 case io = -36 case opWr = -49 case param = -50 case wrPerm = -61 case allocate = -108 case userCanceled = -128 case badReq = -909 case internalComponent = -2070 case notAvailable = -25291 case readOnly = -25292 case authFailed = -25293 case noSuchKeychain = -25294 case invalidKeychain = -25295 case duplicateKeychain = -25296 case duplicateCallback = -25297 case invalidCallback = -25298 case duplicateItem = -25299 case itemNotFound = -25300 case bufferTooSmall = -25301 case dataTooLarge = -25302 case noSuchAttr = -25303 case invalidItemRef = -25304 case invalidSearchRef = -25305 case noSuchClass = -25306 case noDefaultKeychain = -25307 case interactionNotAllowed = -25308 case readOnlyAttr = -25309 case wrongSecVersion = -25310 case keySizeNotAllowed = -25311 case noStorageModule = -25312 case noCertificateModule = -25313 case noPolicyModule = -25314 case interactionRequired = -25315 case dataNotAvailable = -25316 case dataNotModifiable = -25317 case createChainFailed = -25318 case invalidPrefsDomain = -25319 case inDarkWake = -25320 case aclNotSimple = -25240 case policyNotFound = -25241 case invalidTrustSetting = -25242 case noAccessForItem = -25243 case invalidOwnerEdit = -25244 case trustNotAvailable = -25245 case unsupportedFormat = -25256 case unknownFormat = -25257 case keyIsSensitive = -25258 case multiplePrivKeys = -25259 case passphraseRequired = -25260 case invalidPasswordRef = -25261 case invalidTrustSettings = -25262 case noTrustSettings = -25263 case pkcs12VerifyFailure = -25264 case invalidCertificate = -26265 case notSigner = -26267 case policyDenied = -26270 case invalidKey = -26274 case decode = -26275 case `internal` = -26276 case unsupportedAlgorithm = -26268 case unsupportedOperation = -26271 case unsupportedPadding = -26273 case itemInvalidKey = -34000 case itemInvalidKeyType = -34001 case itemInvalidValue = -34002 case itemClassMissing = -34003 case itemMatchUnsupported = -34004 case useItemListUnsupported = -34005 case useKeychainUnsupported = -34006 case useKeychainListUnsupported = -34007 case returnDataUnsupported = -34008 case returnAttributesUnsupported = -34009 case returnRefUnsupported = -34010 case returnPersitentRefUnsupported = -34011 case valueRefUnsupported = -34012 case valuePersistentRefUnsupported = -34013 case returnMissingPointer = -34014 case matchLimitUnsupported = -34015 case itemIllegalQuery = -34016 case waitForCallback = -34017 case missingEntitlement = -34018 case upgradePending = -34019 case mpSignatureInvalid = -25327 case otrTooOld = -25328 case otrIDTooNew = -25329 case serviceNotAvailable = -67585 case insufficientClientID = -67586 case deviceReset = -67587 case deviceFailed = -67588 case appleAddAppACLSubject = -67589 case applePublicKeyIncomplete = -67590 case appleSignatureMismatch = -67591 case appleInvalidKeyStartDate = -67592 case appleInvalidKeyEndDate = -67593 case conversionError = -67594 case appleSSLv2Rollback = -67595 case quotaExceeded = -67596 case fileTooBig = -67597 case invalidDatabaseBlob = -67598 case invalidKeyBlob = -67599 case incompatibleDatabaseBlob = -67600 case incompatibleKeyBlob = -67601 case hostNameMismatch = -67602 case unknownCriticalExtensionFlag = -67603 case noBasicConstraints = -67604 case noBasicConstraintsCA = -67605 case invalidAuthorityKeyID = -67606 case invalidSubjectKeyID = -67607 case invalidKeyUsageForPolicy = -67608 case invalidExtendedKeyUsage = -67609 case invalidIDLinkage = -67610 case pathLengthConstraintExceeded = -67611 case invalidRoot = -67612 case crlExpired = -67613 case crlNotValidYet = -67614 case crlNotFound = -67615 case crlServerDown = -67616 case crlBadURI = -67617 case unknownCertExtension = -67618 case unknownCRLExtension = -67619 case crlNotTrusted = -67620 case crlPolicyFailed = -67621 case idpFailure = -67622 case smimeEmailAddressesNotFound = -67623 case smimeBadExtendedKeyUsage = -67624 case smimeBadKeyUsage = -67625 case smimeKeyUsageNotCritical = -67626 case smimeNoEmailAddress = -67627 case smimeSubjAltNameNotCritical = -67628 case sslBadExtendedKeyUsage = -67629 case ocspBadResponse = -67630 case ocspBadRequest = -67631 case ocspUnavailable = -67632 case ocspStatusUnrecognized = -67633 case endOfData = -67634 case incompleteCertRevocationCheck = -67635 case networkFailure = -67636 case ocspNotTrustedToAnchor = -67637 case recordModified = -67638 case ocspSignatureError = -67639 case ocspNoSigner = -67640 case ocspResponderMalformedReq = -67641 case ocspResponderInternalError = -67642 case ocspResponderTryLater = -67643 case ocspResponderSignatureRequired = -67644 case ocspResponderUnauthorized = -67645 case ocspResponseNonceMismatch = -67646 case codeSigningBadCertChainLength = -67647 case codeSigningNoBasicConstraints = -67648 case codeSigningBadPathLengthConstraint = -67649 case codeSigningNoExtendedKeyUsage = -67650 case codeSigningDevelopment = -67651 case resourceSignBadCertChainLength = -67652 case resourceSignBadExtKeyUsage = -67653 case trustSettingDeny = -67654 case invalidSubjectName = -67655 case unknownQualifiedCertStatement = -67656 case mobileMeRequestQueued = -67657 case mobileMeRequestRedirected = -67658 case mobileMeServerError = -67659 case mobileMeServerNotAvailable = -67660 case mobileMeServerAlreadyExists = -67661 case mobileMeServerServiceErr = -67662 case mobileMeRequestAlreadyPending = -67663 case mobileMeNoRequestPending = -67664 case mobileMeCSRVerifyFailure = -67665 case mobileMeFailedConsistencyCheck = -67666 case notInitialized = -67667 case invalidHandleUsage = -67668 case pvcReferentNotFound = -67669 case functionIntegrityFail = -67670 case internalError = -67671 case memoryError = -67672 case invalidData = -67673 case mdsError = -67674 case invalidPointer = -67675 case selfCheckFailed = -67676 case functionFailed = -67677 case moduleManifestVerifyFailed = -67678 case invalidGUID = -67679 case invalidHandle = -67680 case invalidDBList = -67681 case invalidPassthroughID = -67682 case invalidNetworkAddress = -67683 case crlAlreadySigned = -67684 case invalidNumberOfFields = -67685 case verificationFailure = -67686 case unknownTag = -67687 case invalidSignature = -67688 case invalidName = -67689 case invalidCertificateRef = -67690 case invalidCertificateGroup = -67691 case tagNotFound = -67692 case invalidQuery = -67693 case invalidValue = -67694 case callbackFailed = -67695 case aclDeleteFailed = -67696 case aclReplaceFailed = -67697 case aclAddFailed = -67698 case aclChangeFailed = -67699 case invalidAccessCredentials = -67700 case invalidRecord = -67701 case invalidACL = -67702 case invalidSampleValue = -67703 case incompatibleVersion = -67704 case privilegeNotGranted = -67705 case invalidScope = -67706 case pvcAlreadyConfigured = -67707 case invalidPVC = -67708 case emmLoadFailed = -67709 case emmUnloadFailed = -67710 case addinLoadFailed = -67711 case invalidKeyRef = -67712 case invalidKeyHierarchy = -67713 case addinUnloadFailed = -67714 case libraryReferenceNotFound = -67715 case invalidAddinFunctionTable = -67716 case invalidServiceMask = -67717 case moduleNotLoaded = -67718 case invalidSubServiceID = -67719 case attributeNotInContext = -67720 case moduleManagerInitializeFailed = -67721 case moduleManagerNotFound = -67722 case eventNotificationCallbackNotFound = -67723 case inputLengthError = -67724 case outputLengthError = -67725 case privilegeNotSupported = -67726 case deviceError = -67727 case attachHandleBusy = -67728 case notLoggedIn = -67729 case algorithmMismatch = -67730 case keyUsageIncorrect = -67731 case keyBlobTypeIncorrect = -67732 case keyHeaderInconsistent = -67733 case unsupportedKeyFormat = -67734 case unsupportedKeySize = -67735 case invalidKeyUsageMask = -67736 case unsupportedKeyUsageMask = -67737 case invalidKeyAttributeMask = -67738 case unsupportedKeyAttributeMask = -67739 case invalidKeyLabel = -67740 case unsupportedKeyLabel = -67741 case invalidKeyFormat = -67742 case unsupportedVectorOfBuffers = -67743 case invalidInputVector = -67744 case invalidOutputVector = -67745 case invalidContext = -67746 case invalidAlgorithm = -67747 case invalidAttributeKey = -67748 case missingAttributeKey = -67749 case invalidAttributeInitVector = -67750 case missingAttributeInitVector = -67751 case invalidAttributeSalt = -67752 case missingAttributeSalt = -67753 case invalidAttributePadding = -67754 case missingAttributePadding = -67755 case invalidAttributeRandom = -67756 case missingAttributeRandom = -67757 case invalidAttributeSeed = -67758 case missingAttributeSeed = -67759 case invalidAttributePassphrase = -67760 case missingAttributePassphrase = -67761 case invalidAttributeKeyLength = -67762 case missingAttributeKeyLength = -67763 case invalidAttributeBlockSize = -67764 case missingAttributeBlockSize = -67765 case invalidAttributeOutputSize = -67766 case missingAttributeOutputSize = -67767 case invalidAttributeRounds = -67768 case missingAttributeRounds = -67769 case invalidAlgorithmParms = -67770 case missingAlgorithmParms = -67771 case invalidAttributeLabel = -67772 case missingAttributeLabel = -67773 case invalidAttributeKeyType = -67774 case missingAttributeKeyType = -67775 case invalidAttributeMode = -67776 case missingAttributeMode = -67777 case invalidAttributeEffectiveBits = -67778 case missingAttributeEffectiveBits = -67779 case invalidAttributeStartDate = -67780 case missingAttributeStartDate = -67781 case invalidAttributeEndDate = -67782 case missingAttributeEndDate = -67783 case invalidAttributeVersion = -67784 case missingAttributeVersion = -67785 case invalidAttributePrime = -67786 case missingAttributePrime = -67787 case invalidAttributeBase = -67788 case missingAttributeBase = -67789 case invalidAttributeSubprime = -67790 case missingAttributeSubprime = -67791 case invalidAttributeIterationCount = -67792 case missingAttributeIterationCount = -67793 case invalidAttributeDLDBHandle = -67794 case missingAttributeDLDBHandle = -67795 case invalidAttributeAccessCredentials = -67796 case missingAttributeAccessCredentials = -67797 case invalidAttributePublicKeyFormat = -67798 case missingAttributePublicKeyFormat = -67799 case invalidAttributePrivateKeyFormat = -67800 case missingAttributePrivateKeyFormat = -67801 case invalidAttributeSymmetricKeyFormat = -67802 case missingAttributeSymmetricKeyFormat = -67803 case invalidAttributeWrappedKeyFormat = -67804 case missingAttributeWrappedKeyFormat = -67805 case stagedOperationInProgress = -67806 case stagedOperationNotStarted = -67807 case verifyFailed = -67808 case querySizeUnknown = -67809 case blockSizeMismatch = -67810 case publicKeyInconsistent = -67811 case deviceVerifyFailed = -67812 case invalidLoginName = -67813 case alreadyLoggedIn = -67814 case invalidDigestAlgorithm = -67815 case invalidCRLGroup = -67816 case certificateCannotOperate = -67817 case certificateExpired = -67818 case certificateNotValidYet = -67819 case certificateRevoked = -67820 case certificateSuspended = -67821 case insufficientCredentials = -67822 case invalidAction = -67823 case invalidAuthority = -67824 case verifyActionFailed = -67825 case invalidCertAuthority = -67826 case invaldCRLAuthority = -67827 case invalidCRLEncoding = -67828 case invalidCRLType = -67829 case invalidCRL = -67830 case invalidFormType = -67831 case invalidID = -67832 case invalidIdentifier = -67833 case invalidIndex = -67834 case invalidPolicyIdentifiers = -67835 case invalidTimeString = -67836 case invalidReason = -67837 case invalidRequestInputs = -67838 case invalidResponseVector = -67839 case invalidStopOnPolicy = -67840 case invalidTuple = -67841 case multipleValuesUnsupported = -67842 case notTrusted = -67843 case noDefaultAuthority = -67844 case rejectedForm = -67845 case requestLost = -67846 case requestRejected = -67847 case unsupportedAddressType = -67848 case unsupportedService = -67849 case invalidTupleGroup = -67850 case invalidBaseACLs = -67851 case invalidTupleCredendtials = -67852 case invalidEncoding = -67853 case invalidValidityPeriod = -67854 case invalidRequestor = -67855 case requestDescriptor = -67856 case invalidBundleInfo = -67857 case invalidCRLIndex = -67858 case noFieldValues = -67859 case unsupportedFieldFormat = -67860 case unsupportedIndexInfo = -67861 case unsupportedLocality = -67862 case unsupportedNumAttributes = -67863 case unsupportedNumIndexes = -67864 case unsupportedNumRecordTypes = -67865 case fieldSpecifiedMultiple = -67866 case incompatibleFieldFormat = -67867 case invalidParsingModule = -67868 case databaseLocked = -67869 case datastoreIsOpen = -67870 case missingValue = -67871 case unsupportedQueryLimits = -67872 case unsupportedNumSelectionPreds = -67873 case unsupportedOperator = -67874 case invalidDBLocation = -67875 case invalidAccessRequest = -67876 case invalidIndexInfo = -67877 case invalidNewOwner = -67878 case invalidModifyMode = -67879 case missingRequiredExtension = -67880 case extendedKeyUsageNotCritical = -67881 case timestampMissing = -67882 case timestampInvalid = -67883 case timestampNotTrusted = -67884 case timestampServiceNotAvailable = -67885 case timestampBadAlg = -67886 case timestampBadRequest = -67887 case timestampBadDataFormat = -67888 case timestampTimeNotAvailable = -67889 case timestampUnacceptedPolicy = -67890 case timestampUnacceptedExtension = -67891 case timestampAddInfoNotAvailable = -67892 case timestampSystemFailure = -67893 case signingTimeMissing = -67894 case timestampRejection = -67895 case timestampWaiting = -67896 case timestampRevocationWarning = -67897 case timestampRevocationNotification = -67898 case unexpectedError = -99999 } extension Status: RawRepresentable, CustomStringConvertible { public init(status: OSStatus) { if let mappedStatus = Status(rawValue: status) { self = mappedStatus } else { self = .unexpectedError } } public var description: String { switch self { case .success: return "No error." case .unimplemented: return "Function or operation not implemented." case .diskFull: return "The disk is full." case .io: return "I/O error (bummers)" case .opWr: return "file already open with with write permission" case .param: return "One or more parameters passed to a function were not valid." case .wrPerm: return "write permissions error" case .allocate: return "Failed to allocate memory." case .userCanceled: return "User canceled the operation." case .badReq: return "Bad parameter or invalid state for operation." case .internalComponent: return "" case .notAvailable: return "No keychain is available. You may need to restart your computer." case .readOnly: return "This keychain cannot be modified." case .authFailed: return "The user name or passphrase you entered is not correct." case .noSuchKeychain: return "The specified keychain could not be found." case .invalidKeychain: return "The specified keychain is not a valid keychain file." case .duplicateKeychain: return "A keychain with the same name already exists." case .duplicateCallback: return "The specified callback function is already installed." case .invalidCallback: return "The specified callback function is not valid." case .duplicateItem: return "The specified item already exists in the keychain." case .itemNotFound: return "The specified item could not be found in the keychain." case .bufferTooSmall: return "There is not enough memory available to use the specified item." case .dataTooLarge: return "This item contains information which is too large or in a format that cannot be displayed." case .noSuchAttr: return "The specified attribute does not exist." case .invalidItemRef: return "The specified item is no longer valid. It may have been deleted from the keychain." case .invalidSearchRef: return "Unable to search the current keychain." case .noSuchClass: return "The specified item does not appear to be a valid keychain item." case .noDefaultKeychain: return "A default keychain could not be found." case .interactionNotAllowed: return "User interaction is not allowed." case .readOnlyAttr: return "The specified attribute could not be modified." case .wrongSecVersion: return "This keychain was created by a different version of the system software and cannot be opened." case .keySizeNotAllowed: return "This item specifies a key size which is too large." case .noStorageModule: return "A required component (data storage module) could not be loaded. You may need to restart your computer." case .noCertificateModule: return "A required component (certificate module) could not be loaded. You may need to restart your computer." case .noPolicyModule: return "A required component (policy module) could not be loaded. You may need to restart your computer." case .interactionRequired: return "User interaction is required, but is currently not allowed." case .dataNotAvailable: return "The contents of this item cannot be retrieved." case .dataNotModifiable: return "The contents of this item cannot be modified." case .createChainFailed: return "One or more certificates required to validate this certificate cannot be found." case .invalidPrefsDomain: return "The specified preferences domain is not valid." case .inDarkWake: return "In dark wake, no UI possible" case .aclNotSimple: return "The specified access control list is not in standard (simple) form." case .policyNotFound: return "The specified policy cannot be found." case .invalidTrustSetting: return "The specified trust setting is invalid." case .noAccessForItem: return "The specified item has no access control." case .invalidOwnerEdit: return "Invalid attempt to change the owner of this item." case .trustNotAvailable: return "No trust results are available." case .unsupportedFormat: return "Import/Export format unsupported." case .unknownFormat: return "Unknown format in import." case .keyIsSensitive: return "Key material must be wrapped for export." case .multiplePrivKeys: return "An attempt was made to import multiple private keys." case .passphraseRequired: return "Passphrase is required for import/export." case .invalidPasswordRef: return "The password reference was invalid." case .invalidTrustSettings: return "The Trust Settings Record was corrupted." case .noTrustSettings: return "No Trust Settings were found." case .pkcs12VerifyFailure: return "MAC verification failed during PKCS12 import (wrong password?)" case .invalidCertificate: return "This certificate could not be decoded." case .notSigner: return "A certificate was not signed by its proposed parent." case .policyDenied: return "The certificate chain was not trusted due to a policy not accepting it." case .invalidKey: return "The provided key material was not valid." case .decode: return "Unable to decode the provided data." case .`internal`: return "An internal error occurred in the Security framework." case .unsupportedAlgorithm: return "An unsupported algorithm was encountered." case .unsupportedOperation: return "The operation you requested is not supported by this key." case .unsupportedPadding: return "The padding you requested is not supported." case .itemInvalidKey: return "A string key in dictionary is not one of the supported keys." case .itemInvalidKeyType: return "A key in a dictionary is neither a CFStringRef nor a CFNumberRef." case .itemInvalidValue: return "A value in a dictionary is an invalid (or unsupported) CF type." case .itemClassMissing: return "No kSecItemClass key was specified in a dictionary." case .itemMatchUnsupported: return "The caller passed one or more kSecMatch keys to a function which does not support matches." case .useItemListUnsupported: return "The caller passed in a kSecUseItemList key to a function which does not support it." case .useKeychainUnsupported: return "The caller passed in a kSecUseKeychain key to a function which does not support it." case .useKeychainListUnsupported: return "The caller passed in a kSecUseKeychainList key to a function which does not support it." case .returnDataUnsupported: return "The caller passed in a kSecReturnData key to a function which does not support it." case .returnAttributesUnsupported: return "The caller passed in a kSecReturnAttributes key to a function which does not support it." case .returnRefUnsupported: return "The caller passed in a kSecReturnRef key to a function which does not support it." case .returnPersitentRefUnsupported: return "The caller passed in a kSecReturnPersistentRef key to a function which does not support it." case .valueRefUnsupported: return "The caller passed in a kSecValueRef key to a function which does not support it." case .valuePersistentRefUnsupported: return "The caller passed in a kSecValuePersistentRef key to a function which does not support it." case .returnMissingPointer: return "The caller passed asked for something to be returned but did not pass in a result pointer." case .matchLimitUnsupported: return "The caller passed in a kSecMatchLimit key to a call which does not support limits." case .itemIllegalQuery: return "The caller passed in a query which contained too many keys." case .waitForCallback: return "This operation is incomplete, until the callback is invoked (not an error)." case .missingEntitlement: return "Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements." case .upgradePending: return "Error returned if keychain database needs a schema migration but the device is locked, clients should wait for a device unlock notification and retry the command." case .mpSignatureInvalid: return "Signature invalid on MP message" case .otrTooOld: return "Message is too old to use" case .otrIDTooNew: return "Key ID is too new to use! Message from the future?" case .serviceNotAvailable: return "The required service is not available." case .insufficientClientID: return "The client ID is not correct." case .deviceReset: return "A device reset has occurred." case .deviceFailed: return "A device failure has occurred." case .appleAddAppACLSubject: return "Adding an application ACL subject failed." case .applePublicKeyIncomplete: return "The public key is incomplete." case .appleSignatureMismatch: return "A signature mismatch has occurred." case .appleInvalidKeyStartDate: return "The specified key has an invalid start date." case .appleInvalidKeyEndDate: return "The specified key has an invalid end date." case .conversionError: return "A conversion error has occurred." case .appleSSLv2Rollback: return "A SSLv2 rollback error has occurred." case .quotaExceeded: return "The quota was exceeded." case .fileTooBig: return "The file is too big." case .invalidDatabaseBlob: return "The specified database has an invalid blob." case .invalidKeyBlob: return "The specified database has an invalid key blob." case .incompatibleDatabaseBlob: return "The specified database has an incompatible blob." case .incompatibleKeyBlob: return "The specified database has an incompatible key blob." case .hostNameMismatch: return "A host name mismatch has occurred." case .unknownCriticalExtensionFlag: return "There is an unknown critical extension flag." case .noBasicConstraints: return "No basic constraints were found." case .noBasicConstraintsCA: return "No basic CA constraints were found." case .invalidAuthorityKeyID: return "The authority key ID is not valid." case .invalidSubjectKeyID: return "The subject key ID is not valid." case .invalidKeyUsageForPolicy: return "The key usage is not valid for the specified policy." case .invalidExtendedKeyUsage: return "The extended key usage is not valid." case .invalidIDLinkage: return "The ID linkage is not valid." case .pathLengthConstraintExceeded: return "The path length constraint was exceeded." case .invalidRoot: return "The root or anchor certificate is not valid." case .crlExpired: return "The CRL has expired." case .crlNotValidYet: return "The CRL is not yet valid." case .crlNotFound: return "The CRL was not found." case .crlServerDown: return "The CRL server is down." case .crlBadURI: return "The CRL has a bad Uniform Resource Identifier." case .unknownCertExtension: return "An unknown certificate extension was encountered." case .unknownCRLExtension: return "An unknown CRL extension was encountered." case .crlNotTrusted: return "The CRL is not trusted." case .crlPolicyFailed: return "The CRL policy failed." case .idpFailure: return "The issuing distribution point was not valid." case .smimeEmailAddressesNotFound: return "An email address mismatch was encountered." case .smimeBadExtendedKeyUsage: return "The appropriate extended key usage for SMIME was not found." case .smimeBadKeyUsage: return "The key usage is not compatible with SMIME." case .smimeKeyUsageNotCritical: return "The key usage extension is not marked as critical." case .smimeNoEmailAddress: return "No email address was found in the certificate." case .smimeSubjAltNameNotCritical: return "The subject alternative name extension is not marked as critical." case .sslBadExtendedKeyUsage: return "The appropriate extended key usage for SSL was not found." case .ocspBadResponse: return "The OCSP response was incorrect or could not be parsed." case .ocspBadRequest: return "The OCSP request was incorrect or could not be parsed." case .ocspUnavailable: return "OCSP service is unavailable." case .ocspStatusUnrecognized: return "The OCSP server did not recognize this certificate." case .endOfData: return "An end-of-data was detected." case .incompleteCertRevocationCheck: return "An incomplete certificate revocation check occurred." case .networkFailure: return "A network failure occurred." case .ocspNotTrustedToAnchor: return "The OCSP response was not trusted to a root or anchor certificate." case .recordModified: return "The record was modified." case .ocspSignatureError: return "The OCSP response had an invalid signature." case .ocspNoSigner: return "The OCSP response had no signer." case .ocspResponderMalformedReq: return "The OCSP responder was given a malformed request." case .ocspResponderInternalError: return "The OCSP responder encountered an internal error." case .ocspResponderTryLater: return "The OCSP responder is busy, try again later." case .ocspResponderSignatureRequired: return "The OCSP responder requires a signature." case .ocspResponderUnauthorized: return "The OCSP responder rejected this request as unauthorized." case .ocspResponseNonceMismatch: return "The OCSP response nonce did not match the request." case .codeSigningBadCertChainLength: return "Code signing encountered an incorrect certificate chain length." case .codeSigningNoBasicConstraints: return "Code signing found no basic constraints." case .codeSigningBadPathLengthConstraint: return "Code signing encountered an incorrect path length constraint." case .codeSigningNoExtendedKeyUsage: return "Code signing found no extended key usage." case .codeSigningDevelopment: return "Code signing indicated use of a development-only certificate." case .resourceSignBadCertChainLength: return "Resource signing has encountered an incorrect certificate chain length." case .resourceSignBadExtKeyUsage: return "Resource signing has encountered an error in the extended key usage." case .trustSettingDeny: return "The trust setting for this policy was set to Deny." case .invalidSubjectName: return "An invalid certificate subject name was encountered." case .unknownQualifiedCertStatement: return "An unknown qualified certificate statement was encountered." case .mobileMeRequestQueued: return "The MobileMe request will be sent during the next connection." case .mobileMeRequestRedirected: return "The MobileMe request was redirected." case .mobileMeServerError: return "A MobileMe server error occurred." case .mobileMeServerNotAvailable: return "The MobileMe server is not available." case .mobileMeServerAlreadyExists: return "The MobileMe server reported that the item already exists." case .mobileMeServerServiceErr: return "A MobileMe service error has occurred." case .mobileMeRequestAlreadyPending: return "A MobileMe request is already pending." case .mobileMeNoRequestPending: return "MobileMe has no request pending." case .mobileMeCSRVerifyFailure: return "A MobileMe CSR verification failure has occurred." case .mobileMeFailedConsistencyCheck: return "MobileMe has found a failed consistency check." case .notInitialized: return "A function was called without initializing CSSM." case .invalidHandleUsage: return "The CSSM handle does not match with the service type." case .pvcReferentNotFound: return "A reference to the calling module was not found in the list of authorized callers." case .functionIntegrityFail: return "A function address was not within the verified module." case .internalError: return "An internal error has occurred." case .memoryError: return "A memory error has occurred." case .invalidData: return "Invalid data was encountered." case .mdsError: return "A Module Directory Service error has occurred." case .invalidPointer: return "An invalid pointer was encountered." case .selfCheckFailed: return "Self-check has failed." case .functionFailed: return "A function has failed." case .moduleManifestVerifyFailed: return "A module manifest verification failure has occurred." case .invalidGUID: return "An invalid GUID was encountered." case .invalidHandle: return "An invalid handle was encountered." case .invalidDBList: return "An invalid DB list was encountered." case .invalidPassthroughID: return "An invalid passthrough ID was encountered." case .invalidNetworkAddress: return "An invalid network address was encountered." case .crlAlreadySigned: return "The certificate revocation list is already signed." case .invalidNumberOfFields: return "An invalid number of fields were encountered." case .verificationFailure: return "A verification failure occurred." case .unknownTag: return "An unknown tag was encountered." case .invalidSignature: return "An invalid signature was encountered." case .invalidName: return "An invalid name was encountered." case .invalidCertificateRef: return "An invalid certificate reference was encountered." case .invalidCertificateGroup: return "An invalid certificate group was encountered." case .tagNotFound: return "The specified tag was not found." case .invalidQuery: return "The specified query was not valid." case .invalidValue: return "An invalid value was detected." case .callbackFailed: return "A callback has failed." case .aclDeleteFailed: return "An ACL delete operation has failed." case .aclReplaceFailed: return "An ACL replace operation has failed." case .aclAddFailed: return "An ACL add operation has failed." case .aclChangeFailed: return "An ACL change operation has failed." case .invalidAccessCredentials: return "Invalid access credentials were encountered." case .invalidRecord: return "An invalid record was encountered." case .invalidACL: return "An invalid ACL was encountered." case .invalidSampleValue: return "An invalid sample value was encountered." case .incompatibleVersion: return "An incompatible version was encountered." case .privilegeNotGranted: return "The privilege was not granted." case .invalidScope: return "An invalid scope was encountered." case .pvcAlreadyConfigured: return "The PVC is already configured." case .invalidPVC: return "An invalid PVC was encountered." case .emmLoadFailed: return "The EMM load has failed." case .emmUnloadFailed: return "The EMM unload has failed." case .addinLoadFailed: return "The add-in load operation has failed." case .invalidKeyRef: return "An invalid key was encountered." case .invalidKeyHierarchy: return "An invalid key hierarchy was encountered." case .addinUnloadFailed: return "The add-in unload operation has failed." case .libraryReferenceNotFound: return "A library reference was not found." case .invalidAddinFunctionTable: return "An invalid add-in function table was encountered." case .invalidServiceMask: return "An invalid service mask was encountered." case .moduleNotLoaded: return "A module was not loaded." case .invalidSubServiceID: return "An invalid subservice ID was encountered." case .attributeNotInContext: return "An attribute was not in the context." case .moduleManagerInitializeFailed: return "A module failed to initialize." case .moduleManagerNotFound: return "A module was not found." case .eventNotificationCallbackNotFound: return "An event notification callback was not found." case .inputLengthError: return "An input length error was encountered." case .outputLengthError: return "An output length error was encountered." case .privilegeNotSupported: return "The privilege is not supported." case .deviceError: return "A device error was encountered." case .attachHandleBusy: return "The CSP handle was busy." case .notLoggedIn: return "You are not logged in." case .algorithmMismatch: return "An algorithm mismatch was encountered." case .keyUsageIncorrect: return "The key usage is incorrect." case .keyBlobTypeIncorrect: return "The key blob type is incorrect." case .keyHeaderInconsistent: return "The key header is inconsistent." case .unsupportedKeyFormat: return "The key header format is not supported." case .unsupportedKeySize: return "The key size is not supported." case .invalidKeyUsageMask: return "The key usage mask is not valid." case .unsupportedKeyUsageMask: return "The key usage mask is not supported." case .invalidKeyAttributeMask: return "The key attribute mask is not valid." case .unsupportedKeyAttributeMask: return "The key attribute mask is not supported." case .invalidKeyLabel: return "The key label is not valid." case .unsupportedKeyLabel: return "The key label is not supported." case .invalidKeyFormat: return "The key format is not valid." case .unsupportedVectorOfBuffers: return "The vector of buffers is not supported." case .invalidInputVector: return "The input vector is not valid." case .invalidOutputVector: return "The output vector is not valid." case .invalidContext: return "An invalid context was encountered." case .invalidAlgorithm: return "An invalid algorithm was encountered." case .invalidAttributeKey: return "A key attribute was not valid." case .missingAttributeKey: return "A key attribute was missing." case .invalidAttributeInitVector: return "An init vector attribute was not valid." case .missingAttributeInitVector: return "An init vector attribute was missing." case .invalidAttributeSalt: return "A salt attribute was not valid." case .missingAttributeSalt: return "A salt attribute was missing." case .invalidAttributePadding: return "A padding attribute was not valid." case .missingAttributePadding: return "A padding attribute was missing." case .invalidAttributeRandom: return "A random number attribute was not valid." case .missingAttributeRandom: return "A random number attribute was missing." case .invalidAttributeSeed: return "A seed attribute was not valid." case .missingAttributeSeed: return "A seed attribute was missing." case .invalidAttributePassphrase: return "A passphrase attribute was not valid." case .missingAttributePassphrase: return "A passphrase attribute was missing." case .invalidAttributeKeyLength: return "A key length attribute was not valid." case .missingAttributeKeyLength: return "A key length attribute was missing." case .invalidAttributeBlockSize: return "A block size attribute was not valid." case .missingAttributeBlockSize: return "A block size attribute was missing." case .invalidAttributeOutputSize: return "An output size attribute was not valid." case .missingAttributeOutputSize: return "An output size attribute was missing." case .invalidAttributeRounds: return "The number of rounds attribute was not valid." case .missingAttributeRounds: return "The number of rounds attribute was missing." case .invalidAlgorithmParms: return "An algorithm parameters attribute was not valid." case .missingAlgorithmParms: return "An algorithm parameters attribute was missing." case .invalidAttributeLabel: return "A label attribute was not valid." case .missingAttributeLabel: return "A label attribute was missing." case .invalidAttributeKeyType: return "A key type attribute was not valid." case .missingAttributeKeyType: return "A key type attribute was missing." case .invalidAttributeMode: return "A mode attribute was not valid." case .missingAttributeMode: return "A mode attribute was missing." case .invalidAttributeEffectiveBits: return "An effective bits attribute was not valid." case .missingAttributeEffectiveBits: return "An effective bits attribute was missing." case .invalidAttributeStartDate: return "A start date attribute was not valid." case .missingAttributeStartDate: return "A start date attribute was missing." case .invalidAttributeEndDate: return "An end date attribute was not valid." case .missingAttributeEndDate: return "An end date attribute was missing." case .invalidAttributeVersion: return "A version attribute was not valid." case .missingAttributeVersion: return "A version attribute was missing." case .invalidAttributePrime: return "A prime attribute was not valid." case .missingAttributePrime: return "A prime attribute was missing." case .invalidAttributeBase: return "A base attribute was not valid." case .missingAttributeBase: return "A base attribute was missing." case .invalidAttributeSubprime: return "A subprime attribute was not valid." case .missingAttributeSubprime: return "A subprime attribute was missing." case .invalidAttributeIterationCount: return "An iteration count attribute was not valid." case .missingAttributeIterationCount: return "An iteration count attribute was missing." case .invalidAttributeDLDBHandle: return "A database handle attribute was not valid." case .missingAttributeDLDBHandle: return "A database handle attribute was missing." case .invalidAttributeAccessCredentials: return "An access credentials attribute was not valid." case .missingAttributeAccessCredentials: return "An access credentials attribute was missing." case .invalidAttributePublicKeyFormat: return "A public key format attribute was not valid." case .missingAttributePublicKeyFormat: return "A public key format attribute was missing." case .invalidAttributePrivateKeyFormat: return "A private key format attribute was not valid." case .missingAttributePrivateKeyFormat: return "A private key format attribute was missing." case .invalidAttributeSymmetricKeyFormat: return "A symmetric key format attribute was not valid." case .missingAttributeSymmetricKeyFormat: return "A symmetric key format attribute was missing." case .invalidAttributeWrappedKeyFormat: return "A wrapped key format attribute was not valid." case .missingAttributeWrappedKeyFormat: return "A wrapped key format attribute was missing." case .stagedOperationInProgress: return "A staged operation is in progress." case .stagedOperationNotStarted: return "A staged operation was not started." case .verifyFailed: return "A cryptographic verification failure has occurred." case .querySizeUnknown: return "The query size is unknown." case .blockSizeMismatch: return "A block size mismatch occurred." case .publicKeyInconsistent: return "The public key was inconsistent." case .deviceVerifyFailed: return "A device verification failure has occurred." case .invalidLoginName: return "An invalid login name was detected." case .alreadyLoggedIn: return "The user is already logged in." case .invalidDigestAlgorithm: return "An invalid digest algorithm was detected." case .invalidCRLGroup: return "An invalid CRL group was detected." case .certificateCannotOperate: return "The certificate cannot operate." case .certificateExpired: return "An expired certificate was detected." case .certificateNotValidYet: return "The certificate is not yet valid." case .certificateRevoked: return "The certificate was revoked." case .certificateSuspended: return "The certificate was suspended." case .insufficientCredentials: return "Insufficient credentials were detected." case .invalidAction: return "The action was not valid." case .invalidAuthority: return "The authority was not valid." case .verifyActionFailed: return "A verify action has failed." case .invalidCertAuthority: return "The certificate authority was not valid." case .invaldCRLAuthority: return "The CRL authority was not valid." case .invalidCRLEncoding: return "The CRL encoding was not valid." case .invalidCRLType: return "The CRL type was not valid." case .invalidCRL: return "The CRL was not valid." case .invalidFormType: return "The form type was not valid." case .invalidID: return "The ID was not valid." case .invalidIdentifier: return "The identifier was not valid." case .invalidIndex: return "The index was not valid." case .invalidPolicyIdentifiers: return "The policy identifiers are not valid." case .invalidTimeString: return "The time specified was not valid." case .invalidReason: return "The trust policy reason was not valid." case .invalidRequestInputs: return "The request inputs are not valid." case .invalidResponseVector: return "The response vector was not valid." case .invalidStopOnPolicy: return "The stop-on policy was not valid." case .invalidTuple: return "The tuple was not valid." case .multipleValuesUnsupported: return "Multiple values are not supported." case .notTrusted: return "The trust policy was not trusted." case .noDefaultAuthority: return "No default authority was detected." case .rejectedForm: return "The trust policy had a rejected form." case .requestLost: return "The request was lost." case .requestRejected: return "The request was rejected." case .unsupportedAddressType: return "The address type is not supported." case .unsupportedService: return "The service is not supported." case .invalidTupleGroup: return "The tuple group was not valid." case .invalidBaseACLs: return "The base ACLs are not valid." case .invalidTupleCredendtials: return "The tuple credentials are not valid." case .invalidEncoding: return "The encoding was not valid." case .invalidValidityPeriod: return "The validity period was not valid." case .invalidRequestor: return "The requestor was not valid." case .requestDescriptor: return "The request descriptor was not valid." case .invalidBundleInfo: return "The bundle information was not valid." case .invalidCRLIndex: return "The CRL index was not valid." case .noFieldValues: return "No field values were detected." case .unsupportedFieldFormat: return "The field format is not supported." case .unsupportedIndexInfo: return "The index information is not supported." case .unsupportedLocality: return "The locality is not supported." case .unsupportedNumAttributes: return "The number of attributes is not supported." case .unsupportedNumIndexes: return "The number of indexes is not supported." case .unsupportedNumRecordTypes: return "The number of record types is not supported." case .fieldSpecifiedMultiple: return "Too many fields were specified." case .incompatibleFieldFormat: return "The field format was incompatible." case .invalidParsingModule: return "The parsing module was not valid." case .databaseLocked: return "The database is locked." case .datastoreIsOpen: return "The data store is open." case .missingValue: return "A missing value was detected." case .unsupportedQueryLimits: return "The query limits are not supported." case .unsupportedNumSelectionPreds: return "The number of selection predicates is not supported." case .unsupportedOperator: return "The operator is not supported." case .invalidDBLocation: return "The database location is not valid." case .invalidAccessRequest: return "The access request is not valid." case .invalidIndexInfo: return "The index information is not valid." case .invalidNewOwner: return "The new owner is not valid." case .invalidModifyMode: return "The modify mode is not valid." case .missingRequiredExtension: return "A required certificate extension is missing." case .extendedKeyUsageNotCritical: return "The extended key usage extension was not marked critical." case .timestampMissing: return "A timestamp was expected but was not found." case .timestampInvalid: return "The timestamp was not valid." case .timestampNotTrusted: return "The timestamp was not trusted." case .timestampServiceNotAvailable: return "The timestamp service is not available." case .timestampBadAlg: return "An unrecognized or unsupported Algorithm Identifier in timestamp." case .timestampBadRequest: return "The timestamp transaction is not permitted or supported." case .timestampBadDataFormat: return "The timestamp data submitted has the wrong format." case .timestampTimeNotAvailable: return "The time source for the Timestamp Authority is not available." case .timestampUnacceptedPolicy: return "The requested policy is not supported by the Timestamp Authority." case .timestampUnacceptedExtension: return "The requested extension is not supported by the Timestamp Authority." case .timestampAddInfoNotAvailable: return "The additional information requested is not available." case .timestampSystemFailure: return "The timestamp request cannot be handled due to system failure." case .signingTimeMissing: return "A signing time was expected but was not found." case .timestampRejection: return "A timestamp transaction was rejected." case .timestampWaiting: return "A timestamp transaction is waiting." case .timestampRevocationWarning: return "A timestamp authority revocation warning was issued." case .timestampRevocationNotification: return "A timestamp authority revocation notification was issued." case .unexpectedError: return "Unexpected error has occurred." } } } extension Status: CustomNSError { public static let errorDomain = KeychainAccessErrorDomain public var errorCode: Int { return Int(rawValue) } public var errorUserInfo: [String : Any] { return [NSLocalizedDescriptionKey: description] } } ================================================ FILE: Lib/KeychainAccess/KeychainAccess.h ================================================ // // KeychainAccess.h // KeychainAccess // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import FOUNDATION_EXPORT double KeychainAccessVersionNumber; FOUNDATION_EXPORT const unsigned char KeychainAccessVersionString[]; ================================================ FILE: Lib/KeychainAccess.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 140F19621A49D79400B0016A /* KeychainAccess.h in Headers */ = {isa = PBXBuildFile; fileRef = 140F19611A49D79400B0016A /* KeychainAccess.h */; settings = {ATTRIBUTES = (Public, ); }; }; 140F196F1A49D79500B0016A /* KeychainAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140F196E1A49D79500B0016A /* KeychainAccessTests.swift */; }; 140F197B1A49D89200B0016A /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140F197A1A49D89200B0016A /* Keychain.swift */; }; 142EDA851BCB505F00A32149 /* ErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */; }; 142EDB041BCBB0DD00A32149 /* SharedCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */; }; 148F9D4A1BCB4118006EDF48 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148F9D491BCB4118006EDF48 /* EnumTests.swift */; }; 14A630181D3293C700809B3F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A630171D3293C700809B3F /* AppDelegate.swift */; }; 14A6301F1D3293C700809B3F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14A6301E1D3293C700809B3F /* Assets.xcassets */; }; 14C3A6781D32BF9C00349459 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; }; 14C3A6791D32BF9C00349459 /* KeychainAccess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 140F19691A49D79500B0016A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 140F19531A49D79400B0016A /* Project object */; proxyType = 1; remoteGlobalIDString = 140F195B1A49D79400B0016A; remoteInfo = KeychainAccess; }; 14C3A67A1D32BF9C00349459 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 140F19531A49D79400B0016A /* Project object */; proxyType = 1; remoteGlobalIDString = 140F195B1A49D79400B0016A; remoteInfo = KeychainAccess; }; 14F0C1991D32A160007DCDDB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 140F19531A49D79400B0016A /* Project object */; proxyType = 1; remoteGlobalIDString = 14A630141D3293C700809B3F; remoteInfo = TestHost; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 14C3A67C1D32BF9D00349459 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 14C3A6791D32BF9C00349459 /* KeychainAccess.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 140F195C1A49D79400B0016A /* KeychainAccess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainAccess.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 140F19601A49D79400B0016A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140F19611A49D79400B0016A /* KeychainAccess.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeychainAccess.h; sourceTree = ""; }; 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainAccessTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 140F196D1A49D79500B0016A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140F196E1A49D79500B0016A /* KeychainAccessTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessTests.swift; sourceTree = ""; }; 140F197A1A49D89200B0016A /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTypeTests.swift; sourceTree = ""; }; 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedCredentialTests.swift; sourceTree = ""; }; 148E44E51BF9EDCB004FFEC1 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Base.xcconfig; path = Configurations/Base.xcconfig; sourceTree = ""; }; 148E44E61BF9EDCB004FFEC1 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configurations/Debug.xcconfig; sourceTree = ""; }; 148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configurations/Release.xcconfig; sourceTree = ""; }; 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Tests.xcconfig; path = Configurations/Tests.xcconfig; sourceTree = ""; }; 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = KeychainAccess.xcconfig; path = Configurations/KeychainAccess.xcconfig; sourceTree = ""; }; 148F9D491BCB4118006EDF48 /* EnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = ""; }; 14A630151D3293C700809B3F /* TestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; 14A630171D3293C700809B3F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 14A6301E1D3293C700809B3F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 14A630231D3293C700809B3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14F0C1961D3295C4007DCDDB /* TestHost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = TestHost.entitlements; sourceTree = ""; }; 14F0C1981D329832007DCDDB /* TestHost.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = TestHost.xcconfig; path = Configurations/TestHost.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 140F19581A49D79400B0016A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 140F19641A49D79500B0016A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 14A630121D3293C700809B3F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14C3A6781D32BF9C00349459 /* KeychainAccess.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 140F19521A49D79400B0016A = { isa = PBXGroup; children = ( 140F195E1A49D79400B0016A /* KeychainAccess */, 140F196B1A49D79500B0016A /* KeychainAccessTests */, 148E44E41BF9ED6D004FFEC1 /* Cofigurations */, 14A630161D3293C700809B3F /* TestHost */, 140F195D1A49D79400B0016A /* Products */, ); sourceTree = ""; }; 140F195D1A49D79400B0016A /* Products */ = { isa = PBXGroup; children = ( 140F195C1A49D79400B0016A /* KeychainAccess.framework */, 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */, 14A630151D3293C700809B3F /* TestHost.app */, ); name = Products; sourceTree = ""; }; 140F195E1A49D79400B0016A /* KeychainAccess */ = { isa = PBXGroup; children = ( 140F19611A49D79400B0016A /* KeychainAccess.h */, 140F197A1A49D89200B0016A /* Keychain.swift */, 140F195F1A49D79400B0016A /* Supporting Files */, ); path = KeychainAccess; sourceTree = ""; }; 140F195F1A49D79400B0016A /* Supporting Files */ = { isa = PBXGroup; children = ( 140F19601A49D79400B0016A /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 140F196B1A49D79500B0016A /* KeychainAccessTests */ = { isa = PBXGroup; children = ( 140F196E1A49D79500B0016A /* KeychainAccessTests.swift */, 148F9D491BCB4118006EDF48 /* EnumTests.swift */, 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */, 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */, 140F196C1A49D79500B0016A /* Supporting Files */, ); path = KeychainAccessTests; sourceTree = ""; }; 140F196C1A49D79500B0016A /* Supporting Files */ = { isa = PBXGroup; children = ( 140F196D1A49D79500B0016A /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 148E44E41BF9ED6D004FFEC1 /* Cofigurations */ = { isa = PBXGroup; children = ( 148E44E51BF9EDCB004FFEC1 /* Base.xcconfig */, 148E44E61BF9EDCB004FFEC1 /* Debug.xcconfig */, 148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */, 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */, 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */, 14F0C1981D329832007DCDDB /* TestHost.xcconfig */, ); name = Cofigurations; sourceTree = ""; }; 14A630161D3293C700809B3F /* TestHost */ = { isa = PBXGroup; children = ( 14A630171D3293C700809B3F /* AppDelegate.swift */, 14A6301E1D3293C700809B3F /* Assets.xcassets */, 14A630231D3293C700809B3F /* Info.plist */, 14F0C1961D3295C4007DCDDB /* TestHost.entitlements */, ); path = TestHost; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 140F19591A49D79400B0016A /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 140F19621A49D79400B0016A /* KeychainAccess.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 140F195B1A49D79400B0016A /* KeychainAccess */ = { isa = PBXNativeTarget; buildConfigurationList = 140F19721A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccess" */; buildPhases = ( 140F19571A49D79400B0016A /* Sources */, 140F19581A49D79400B0016A /* Frameworks */, 140F19591A49D79400B0016A /* Headers */, 140F195A1A49D79400B0016A /* Resources */, ); buildRules = ( ); dependencies = ( ); name = KeychainAccess; productName = KeychainAccess; productReference = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; productType = "com.apple.product-type.framework"; }; 140F19661A49D79500B0016A /* KeychainAccessTests */ = { isa = PBXNativeTarget; buildConfigurationList = 140F19751A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccessTests" */; buildPhases = ( 140F19631A49D79500B0016A /* Sources */, 140F19641A49D79500B0016A /* Frameworks */, 140F19651A49D79500B0016A /* Resources */, ); buildRules = ( ); dependencies = ( 140F196A1A49D79500B0016A /* PBXTargetDependency */, 14F0C19A1D32A160007DCDDB /* PBXTargetDependency */, ); name = KeychainAccessTests; productName = KeychainAccessTests; productReference = 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 14A630141D3293C700809B3F /* TestHost */ = { isa = PBXNativeTarget; buildConfigurationList = 14A630241D3293C700809B3F /* Build configuration list for PBXNativeTarget "TestHost" */; buildPhases = ( 14A630111D3293C700809B3F /* Sources */, 14A630121D3293C700809B3F /* Frameworks */, 14A630131D3293C700809B3F /* Resources */, 14C3A67C1D32BF9D00349459 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 14C3A67B1D32BF9C00349459 /* PBXTargetDependency */, ); name = TestHost; productName = TestHost; productReference = 14A630151D3293C700809B3F /* TestHost.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 140F19531A49D79400B0016A /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 1200; ORGANIZATIONNAME = "kishikawa katsumi"; TargetAttributes = { 140F195B1A49D79400B0016A = { CreatedOnToolsVersion = 6.1.1; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; 140F19661A49D79500B0016A = { CreatedOnToolsVersion = 6.1.1; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; TestTargetID = 14A62FFC1D32922C00809B3F; }; 14A630141D3293C700809B3F = { CreatedOnToolsVersion = 7.3.1; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1020; SystemCapabilities = { com.apple.Keychain = { enabled = 1; }; }; }; }; }; buildConfigurationList = 140F19561A49D79400B0016A /* Build configuration list for PBXProject "KeychainAccess" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 140F19521A49D79400B0016A; productRefGroup = 140F195D1A49D79400B0016A /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 140F195B1A49D79400B0016A /* KeychainAccess */, 140F19661A49D79500B0016A /* KeychainAccessTests */, 14A630141D3293C700809B3F /* TestHost */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 140F195A1A49D79400B0016A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 140F19651A49D79500B0016A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 14A630131D3293C700809B3F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A6301F1D3293C700809B3F /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 140F19571A49D79400B0016A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 140F197B1A49D89200B0016A /* Keychain.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 140F19631A49D79500B0016A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 140F196F1A49D79500B0016A /* KeychainAccessTests.swift in Sources */, 148F9D4A1BCB4118006EDF48 /* EnumTests.swift in Sources */, 142EDA851BCB505F00A32149 /* ErrorTypeTests.swift in Sources */, 142EDB041BCBB0DD00A32149 /* SharedCredentialTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A630111D3293C700809B3F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A630181D3293C700809B3F /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 140F196A1A49D79500B0016A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 140F195B1A49D79400B0016A /* KeychainAccess */; targetProxy = 140F19691A49D79500B0016A /* PBXContainerItemProxy */; }; 14C3A67B1D32BF9C00349459 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 140F195B1A49D79400B0016A /* KeychainAccess */; targetProxy = 14C3A67A1D32BF9C00349459 /* PBXContainerItemProxy */; }; 14F0C19A1D32A160007DCDDB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 14A630141D3293C700809B3F /* TestHost */; targetProxy = 14F0C1991D32A160007DCDDB /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 140F19701A49D79500B0016A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E61BF9EDCB004FFEC1 /* Debug.xcconfig */; buildSettings = { }; name = Debug; }; 140F19711A49D79500B0016A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */; buildSettings = { }; name = Release; }; 140F19731A49D79500B0016A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */; buildSettings = { }; name = Debug; }; 140F19741A49D79500B0016A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */; buildSettings = { }; name = Release; }; 140F19761A49D79500B0016A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */; buildSettings = { }; name = Debug; }; 140F19771A49D79500B0016A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */; buildSettings = { }; name = Release; }; 14A630251D3293C700809B3F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 14F0C1981D329832007DCDDB /* TestHost.xcconfig */; buildSettings = { }; name = Debug; }; 14A630261D3293C700809B3F /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 14F0C1981D329832007DCDDB /* TestHost.xcconfig */; buildSettings = { }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 140F19561A49D79400B0016A /* Build configuration list for PBXProject "KeychainAccess" */ = { isa = XCConfigurationList; buildConfigurations = ( 140F19701A49D79500B0016A /* Debug */, 140F19711A49D79500B0016A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 140F19721A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccess" */ = { isa = XCConfigurationList; buildConfigurations = ( 140F19731A49D79500B0016A /* Debug */, 140F19741A49D79500B0016A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 140F19751A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccessTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 140F19761A49D79500B0016A /* Debug */, 140F19771A49D79500B0016A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14A630241D3293C700809B3F /* Build configuration list for PBXNativeTarget "TestHost" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A630251D3293C700809B3F /* Debug */, 14A630261D3293C700809B3F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 140F19531A49D79400B0016A /* Project object */; } ================================================ FILE: Lib/KeychainAccess.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/KeychainAccess.xcscheme ================================================ ================================================ FILE: Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme ================================================ ================================================ FILE: Lib/KeychainAccessTests/EnumTests.swift ================================================ // // EnumTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class EnumTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testItemClass() { do { let itemClass = ItemClass(rawValue: kSecClassGenericPassword as String) XCTAssertEqual(itemClass, .genericPassword) XCTAssertEqual(itemClass?.description, "GenericPassword") } do { let itemClass = ItemClass(rawValue: kSecClassInternetPassword as String) XCTAssertEqual(itemClass, .internetPassword) XCTAssertEqual(itemClass?.description, "InternetPassword") } do { let itemClass = ItemClass(rawValue: kSecClassCertificate as String) XCTAssertNil(itemClass) } do { let itemClass = ItemClass(rawValue: kSecClassKey as String) XCTAssertNil(itemClass) } do { let itemClass = ItemClass(rawValue: kSecClassIdentity as String) XCTAssertNil(itemClass) } } func testProtocolType() { do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTP as String) XCTAssertEqual(protocolType, .ftp) XCTAssertEqual(protocolType?.description, "FTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPAccount as String) XCTAssertEqual(protocolType, .ftpAccount) XCTAssertEqual(protocolType?.description, "FTPAccount") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTP as String) XCTAssertEqual(protocolType, .http) XCTAssertEqual(protocolType?.description, "HTTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIRC as String) XCTAssertEqual(protocolType, .irc) XCTAssertEqual(protocolType?.description, "IRC") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolNNTP as String) XCTAssertEqual(protocolType, .nntp) XCTAssertEqual(protocolType?.description, "NNTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolPOP3 as String) XCTAssertEqual(protocolType, .pop3) XCTAssertEqual(protocolType?.description, "POP3") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSMTP as String) XCTAssertEqual(protocolType, .smtp) XCTAssertEqual(protocolType?.description, "SMTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSOCKS as String) XCTAssertEqual(protocolType, .socks) XCTAssertEqual(protocolType?.description, "SOCKS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIMAP as String) XCTAssertEqual(protocolType, .imap) XCTAssertEqual(protocolType?.description, "IMAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolLDAP as String) XCTAssertEqual(protocolType, .ldap) XCTAssertEqual(protocolType?.description, "LDAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolAppleTalk as String) XCTAssertEqual(protocolType, .appleTalk) XCTAssertEqual(protocolType?.description, "AppleTalk") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolAFP as String) XCTAssertEqual(protocolType, .afp) XCTAssertEqual(protocolType?.description, "AFP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolTelnet as String) XCTAssertEqual(protocolType, .telnet) XCTAssertEqual(protocolType?.description, "Telnet") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSSH as String) XCTAssertEqual(protocolType, .ssh) XCTAssertEqual(protocolType?.description, "SSH") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPS as String) XCTAssertEqual(protocolType, .ftps) XCTAssertEqual(protocolType?.description, "FTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPS as String) XCTAssertEqual(protocolType, .https) XCTAssertEqual(protocolType?.description, "HTTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPProxy as String) XCTAssertEqual(protocolType, .httpProxy) XCTAssertEqual(protocolType?.description, "HTTPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPSProxy as String) XCTAssertEqual(protocolType, .httpsProxy) XCTAssertEqual(protocolType?.description, "HTTPSProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPProxy as String) XCTAssertEqual(protocolType, .ftpProxy) XCTAssertEqual(protocolType?.description, "FTPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSMB as String) XCTAssertEqual(protocolType, .smb) XCTAssertEqual(protocolType?.description, "SMB") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolRTSP as String) XCTAssertEqual(protocolType, .rtsp) XCTAssertEqual(protocolType?.description, "RTSP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolRTSPProxy as String) XCTAssertEqual(protocolType, .rtspProxy) XCTAssertEqual(protocolType?.description, "RTSPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolDAAP as String) XCTAssertEqual(protocolType, .daap) XCTAssertEqual(protocolType?.description, "DAAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolEPPC as String) XCTAssertEqual(protocolType, .eppc) XCTAssertEqual(protocolType?.description, "EPPC") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIPP as String) XCTAssertEqual(protocolType, .ipp) XCTAssertEqual(protocolType?.description, "IPP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolNNTPS as String) XCTAssertEqual(protocolType, .nntps) XCTAssertEqual(protocolType?.description, "NNTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolLDAPS as String) XCTAssertEqual(protocolType, .ldaps) XCTAssertEqual(protocolType?.description, "LDAPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolTelnetS as String) XCTAssertEqual(protocolType, .telnetS) XCTAssertEqual(protocolType?.description, "TelnetS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIMAPS as String) XCTAssertEqual(protocolType, .imaps) XCTAssertEqual(protocolType?.description, "IMAPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIRCS as String) XCTAssertEqual(protocolType, .ircs) XCTAssertEqual(protocolType?.description, "IRCS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolPOP3S as String) XCTAssertEqual(protocolType, .pop3S) XCTAssertEqual(protocolType?.description, "POP3S") } } func testAuthenticationType() { do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeNTLM as String) XCTAssertEqual(authenticationType, .ntlm) XCTAssertEqual(authenticationType?.description, "NTLM") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeMSN as String) XCTAssertEqual(authenticationType, .msn) XCTAssertEqual(authenticationType?.description, "MSN") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeDPA as String) XCTAssertEqual(authenticationType, .dpa) XCTAssertEqual(authenticationType?.description, "DPA") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeRPA as String) XCTAssertEqual(authenticationType, .rpa) XCTAssertEqual(authenticationType?.description, "RPA") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTTPBasic as String) XCTAssertEqual(authenticationType, .httpBasic) XCTAssertEqual(authenticationType?.description, "HTTPBasic") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTTPDigest as String) XCTAssertEqual(authenticationType, .httpDigest) XCTAssertEqual(authenticationType?.description, "HTTPDigest") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTMLForm as String) XCTAssertEqual(authenticationType, .htmlForm) XCTAssertEqual(authenticationType?.description, "HTMLForm") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeDefault as String) XCTAssertEqual(authenticationType, .default) XCTAssertEqual(authenticationType?.description, "Default") } } func testAccessibility() { guard #available(macOS 10.10, *) else { return } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenUnlocked as String) XCTAssertEqual(accessibility, .whenUnlocked) XCTAssertEqual(accessibility?.description, "WhenUnlocked") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAfterFirstUnlock as String) XCTAssertEqual(accessibility, .afterFirstUnlock) XCTAssertEqual(accessibility?.description, "AfterFirstUnlock") } #if !targetEnvironment(macCatalyst) do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlways as String) XCTAssertEqual(accessibility, .always) XCTAssertEqual(accessibility?.description, "Always") } #endif do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as String) XCTAssertEqual(accessibility, .whenPasscodeSetThisDeviceOnly) XCTAssertEqual(accessibility?.description, "WhenPasscodeSetThisDeviceOnly") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenUnlockedThisDeviceOnly as String) XCTAssertEqual(accessibility, .whenUnlockedThisDeviceOnly) XCTAssertEqual(accessibility?.description, "WhenUnlockedThisDeviceOnly") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly as String) XCTAssertEqual(accessibility, .afterFirstUnlockThisDeviceOnly) XCTAssertEqual(accessibility?.description, "AfterFirstUnlockThisDeviceOnly") } #if !targetEnvironment(macCatalyst) do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlwaysThisDeviceOnly as String) XCTAssertEqual(accessibility, .alwaysThisDeviceOnly) XCTAssertEqual(accessibility?.description, "AlwaysThisDeviceOnly") } #endif } } ================================================ FILE: Lib/KeychainAccessTests/ErrorTypeTests.swift ================================================ // // ErrorTypeTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class ErrorTypeTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testErrorType() { do { let status = Status(rawValue: errSecSuccess) XCTAssertEqual(status, .success) XCTAssertEqual(status?.description, "No error.") } do { let status = Status(rawValue: errSecUnimplemented) XCTAssertEqual(status, .unimplemented) XCTAssertEqual(status?.description, "Function or operation not implemented.") } #if os(macOS) do { let status = Status(rawValue: errSecDiskFull) XCTAssertEqual(status, .diskFull) XCTAssertEqual(status?.description, "The disk is full.") } #endif do { let status = Status(rawValue: errSecIO) XCTAssertEqual(status, .io) XCTAssertEqual(status?.description, "I/O error (bummers)") } #if os(iOS) do { let status = Status(rawValue: errSecOpWr) XCTAssertEqual(status, .opWr) XCTAssertEqual(status?.description, "file already open with with write permission") } #endif do { let status = Status(rawValue: errSecParam) XCTAssertEqual(status, .param) XCTAssertEqual(status?.description, "One or more parameters passed to a function were not valid.") } #if os(macOS) do { let status = Status(rawValue: errSecWrPerm) XCTAssertEqual(status, .wrPerm) XCTAssertEqual(status?.description, "write permissions error") } #endif do { let status = Status(rawValue: errSecAllocate) XCTAssertEqual(status, .allocate) XCTAssertEqual(status?.description, "Failed to allocate memory.") } do { let status = Status(rawValue: errSecUserCanceled) XCTAssertEqual(status, .userCanceled) XCTAssertEqual(status?.description, "User canceled the operation.") } do { let status = Status(rawValue: errSecBadReq) XCTAssertEqual(status, .badReq) XCTAssertEqual(status?.description, "Bad parameter or invalid state for operation.") } do { let status = Status(rawValue: errSecInternalComponent) XCTAssertEqual(status, .internalComponent) XCTAssertEqual(status?.description, "") } do { let status = Status(rawValue: errSecNotAvailable) XCTAssertEqual(status, .notAvailable) XCTAssertEqual(status?.description, "No keychain is available. You may need to restart your computer.") } #if os(macOS) do { let status = Status(rawValue: errSecReadOnly) XCTAssertEqual(status, .readOnly) XCTAssertEqual(status?.description, "This keychain cannot be modified.") } #endif do { let status = Status(rawValue: errSecAuthFailed) XCTAssertEqual(status, .authFailed) XCTAssertEqual(status?.description, "The user name or passphrase you entered is not correct.") } #if os(macOS) do { let status = Status(rawValue: errSecNoSuchKeychain) XCTAssertEqual(status, .noSuchKeychain) XCTAssertEqual(status?.description, "The specified keychain could not be found.") } do { let status = Status(rawValue: errSecInvalidKeychain) XCTAssertEqual(status, .invalidKeychain) XCTAssertEqual(status?.description, "The specified keychain is not a valid keychain file.") } do { let status = Status(rawValue: errSecDuplicateKeychain) XCTAssertEqual(status, .duplicateKeychain) XCTAssertEqual(status?.description, "A keychain with the same name already exists.") } do { let status = Status(rawValue: errSecDuplicateCallback) XCTAssertEqual(status, .duplicateCallback) XCTAssertEqual(status?.description, "The specified callback function is already installed.") } do { let status = Status(rawValue: errSecInvalidCallback) XCTAssertEqual(status, .invalidCallback) XCTAssertEqual(status?.description, "The specified callback function is not valid.") } #endif do { let status = Status(rawValue: errSecDuplicateItem) XCTAssertEqual(status, .duplicateItem) XCTAssertEqual(status?.description, "The specified item already exists in the keychain.") } do { let status = Status(rawValue: errSecItemNotFound) XCTAssertEqual(status, .itemNotFound) XCTAssertEqual(status?.description, "The specified item could not be found in the keychain.") } #if os(macOS) do { let status = Status(rawValue: errSecBufferTooSmall) XCTAssertEqual(status, .bufferTooSmall) XCTAssertEqual(status?.description, "There is not enough memory available to use the specified item.") } do { let status = Status(rawValue: errSecDataTooLarge) XCTAssertEqual(status, .dataTooLarge) XCTAssertEqual(status?.description, "This item contains information which is too large or in a format that cannot be displayed.") } do { let status = Status(rawValue: errSecNoSuchAttr) XCTAssertEqual(status, .noSuchAttr) XCTAssertEqual(status?.description, "The specified attribute does not exist.") } do { let status = Status(rawValue: errSecInvalidItemRef) XCTAssertEqual(status, .invalidItemRef) XCTAssertEqual(status?.description, "The specified item is no longer valid. It may have been deleted from the keychain.") } do { let status = Status(rawValue: errSecInvalidSearchRef) XCTAssertEqual(status, .invalidSearchRef) XCTAssertEqual(status?.description, "Unable to search the current keychain.") } do { let status = Status(rawValue: errSecNoSuchClass) XCTAssertEqual(status, .noSuchClass) XCTAssertEqual(status?.description, "The specified item does not appear to be a valid keychain item.") } do { let status = Status(rawValue: errSecNoDefaultKeychain) XCTAssertEqual(status, .noDefaultKeychain) XCTAssertEqual(status?.description, "A default keychain could not be found.") } #endif do { let status = Status(rawValue: errSecInteractionNotAllowed) XCTAssertEqual(status, .interactionNotAllowed) XCTAssertEqual(status?.description, "User interaction is not allowed.") } #if os(macOS) do { let status = Status(rawValue: errSecReadOnlyAttr) XCTAssertEqual(status, .readOnlyAttr) XCTAssertEqual(status?.description, "The specified attribute could not be modified.") } do { let status = Status(rawValue: errSecWrongSecVersion) XCTAssertEqual(status, .wrongSecVersion) XCTAssertEqual(status?.description, "This keychain was created by a different version of the system software and cannot be opened.") } do { let status = Status(rawValue: errSecKeySizeNotAllowed) XCTAssertEqual(status, .keySizeNotAllowed) XCTAssertEqual(status?.description, "This item specifies a key size which is too large.") } do { let status = Status(rawValue: errSecNoStorageModule) XCTAssertEqual(status, .noStorageModule) XCTAssertEqual(status?.description, "A required component (data storage module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecNoCertificateModule) XCTAssertEqual(status, .noCertificateModule) XCTAssertEqual(status?.description, "A required component (certificate module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecNoPolicyModule) XCTAssertEqual(status, .noPolicyModule) XCTAssertEqual(status?.description, "A required component (policy module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecInteractionRequired) XCTAssertEqual(status, .interactionRequired) XCTAssertEqual(status?.description, "User interaction is required, but is currently not allowed.") } do { let status = Status(rawValue: errSecDataNotAvailable) XCTAssertEqual(status, .dataNotAvailable) XCTAssertEqual(status?.description, "The contents of this item cannot be retrieved.") } do { let status = Status(rawValue: errSecDataNotModifiable) XCTAssertEqual(status, .dataNotModifiable) XCTAssertEqual(status?.description, "The contents of this item cannot be modified.") } do { let status = Status(rawValue: errSecCreateChainFailed) XCTAssertEqual(status, .createChainFailed) XCTAssertEqual(status?.description, "One or more certificates required to validate this certificate cannot be found.") } do { let status = Status(rawValue: errSecInvalidPrefsDomain) XCTAssertEqual(status, .invalidPrefsDomain) XCTAssertEqual(status?.description, "The specified preferences domain is not valid.") } do { let status = Status(rawValue: errSecInDarkWake) XCTAssertEqual(status, .inDarkWake) XCTAssertEqual(status?.description, "In dark wake, no UI possible") } do { let status = Status(rawValue: errSecACLNotSimple) XCTAssertEqual(status, .aclNotSimple) XCTAssertEqual(status?.description, "The specified access control list is not in standard (simple) form.") } do { let status = Status(rawValue: errSecPolicyNotFound) XCTAssertEqual(status, .policyNotFound) XCTAssertEqual(status?.description, "The specified policy cannot be found.") } do { let status = Status(rawValue: errSecInvalidTrustSetting) XCTAssertEqual(status, .invalidTrustSetting) XCTAssertEqual(status?.description, "The specified trust setting is invalid.") } do { let status = Status(rawValue: errSecNoAccessForItem) XCTAssertEqual(status, .noAccessForItem) XCTAssertEqual(status?.description, "The specified item has no access control.") } do { let status = Status(rawValue: errSecInvalidOwnerEdit) XCTAssertEqual(status, .invalidOwnerEdit) XCTAssertEqual(status?.description, "Invalid attempt to change the owner of this item.") } do { let status = Status(rawValue: errSecTrustNotAvailable) XCTAssertEqual(status, .trustNotAvailable) XCTAssertEqual(status?.description, "No trust results are available.") } do { let status = Status(rawValue: errSecUnsupportedFormat) XCTAssertEqual(status, .unsupportedFormat) XCTAssertEqual(status?.description, "Import/Export format unsupported.") } do { let status = Status(rawValue: errSecUnknownFormat) XCTAssertEqual(status, .unknownFormat) XCTAssertEqual(status?.description, "Unknown format in import.") } do { let status = Status(rawValue: errSecKeyIsSensitive) XCTAssertEqual(status, .keyIsSensitive) XCTAssertEqual(status?.description, "Key material must be wrapped for export.") } do { let status = Status(rawValue: errSecMultiplePrivKeys) XCTAssertEqual(status, .multiplePrivKeys) XCTAssertEqual(status?.description, "An attempt was made to import multiple private keys.") } do { let status = Status(rawValue: errSecPassphraseRequired) XCTAssertEqual(status, .passphraseRequired) XCTAssertEqual(status?.description, "Passphrase is required for import/export.") } do { let status = Status(rawValue: errSecInvalidPasswordRef) XCTAssertEqual(status, .invalidPasswordRef) XCTAssertEqual(status?.description, "The password reference was invalid.") } do { let status = Status(rawValue: errSecInvalidTrustSettings) XCTAssertEqual(status, .invalidTrustSettings) XCTAssertEqual(status?.description, "The Trust Settings Record was corrupted.") } do { let status = Status(rawValue: errSecNoTrustSettings) XCTAssertEqual(status, .noTrustSettings) XCTAssertEqual(status?.description, "No Trust Settings were found.") } do { let status = Status(rawValue: errSecPkcs12VerifyFailure) XCTAssertEqual(status, .pkcs12VerifyFailure) XCTAssertEqual(status?.description, "MAC verification failed during PKCS12 import (wrong password?)") } do { let errSecInvalidCertificate: OSStatus = -26265 let status = Status(rawValue: errSecInvalidCertificate) XCTAssertEqual(status, .invalidCertificate) XCTAssertEqual(status?.description, "This certificate could not be decoded.") } do { let status = Status(rawValue: errSecNotSigner) XCTAssertEqual(status, .notSigner) XCTAssertEqual(status?.description, "A certificate was not signed by its proposed parent.") } do { let errSecPolicyDenied: OSStatus = -26270 let status = Status(rawValue: errSecPolicyDenied) XCTAssertEqual(status, .policyDenied) XCTAssertEqual(status?.description, "The certificate chain was not trusted due to a policy not accepting it.") } do { let errSecInvalidKey: OSStatus = -26274 let status = Status(rawValue: errSecInvalidKey) XCTAssertEqual(status, .invalidKey) XCTAssertEqual(status?.description, "The provided key material was not valid.") } #endif do { let status = Status(rawValue: errSecDecode) XCTAssertEqual(status, .decode) XCTAssertEqual(status?.description, "Unable to decode the provided data.") } do { let errSecInternal: OSStatus = -26276 let status = Status(rawValue: errSecInternal) XCTAssertEqual(status, .internal) XCTAssertEqual(status?.description, "An internal error occurred in the Security framework.") } #if os(macOS) do { let status = Status(rawValue: errSecServiceNotAvailable) XCTAssertEqual(status, .serviceNotAvailable) XCTAssertEqual(status?.description, "The required service is not available.") } do { let errSecUnsupportedAlgorithm: OSStatus = -26268 let status = Status(rawValue: errSecUnsupportedAlgorithm) XCTAssertEqual(status, .unsupportedAlgorithm) XCTAssertEqual(status?.description, "An unsupported algorithm was encountered.") } do { let errSecUnsupportedOperation: OSStatus = -26271 let status = Status(rawValue: errSecUnsupportedOperation) XCTAssertEqual(status, .unsupportedOperation) XCTAssertEqual(status?.description, "The operation you requested is not supported by this key.") } do { let errSecUnsupportedPadding: OSStatus = -26273 let status = Status(rawValue: errSecUnsupportedPadding) XCTAssertEqual(status, .unsupportedPadding) XCTAssertEqual(status?.description, "The padding you requested is not supported.") } do { let errSecItemInvalidKey: OSStatus = -34000 let status = Status(rawValue: errSecItemInvalidKey) XCTAssertEqual(status, .itemInvalidKey) XCTAssertEqual(status?.description, "A string key in dictionary is not one of the supported keys.") } do { let errSecItemInvalidKeyType: OSStatus = -34001 let status = Status(rawValue: errSecItemInvalidKeyType) XCTAssertEqual(status, .itemInvalidKeyType) XCTAssertEqual(status?.description, "A key in a dictionary is neither a CFStringRef nor a CFNumberRef.") } do { let errSecItemInvalidValue: OSStatus = -34002 let status = Status(rawValue: errSecItemInvalidValue) XCTAssertEqual(status, .itemInvalidValue) XCTAssertEqual(status?.description, "A value in a dictionary is an invalid (or unsupported) CF type.") } do { let errSecItemClassMissing: OSStatus = -34003 let status = Status(rawValue: errSecItemClassMissing) XCTAssertEqual(status, .itemClassMissing) XCTAssertEqual(status?.description, "No kSecItemClass key was specified in a dictionary.") } do { let errSecItemMatchUnsupported: OSStatus = -34004 let status = Status(rawValue: errSecItemMatchUnsupported) XCTAssertEqual(status, .itemMatchUnsupported) XCTAssertEqual(status?.description, "The caller passed one or more kSecMatch keys to a function which does not support matches.") } do { let errSecUseItemListUnsupported: OSStatus = -34005 let status = Status(rawValue: errSecUseItemListUnsupported) XCTAssertEqual(status, .useItemListUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseItemList key to a function which does not support it.") } do { let errSecUseKeychainUnsupported: OSStatus = -34006 let status = Status(rawValue: errSecUseKeychainUnsupported) XCTAssertEqual(status, .useKeychainUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseKeychain key to a function which does not support it.") } do { let errSecUseKeychainListUnsupported: OSStatus = -34007 let status = Status(rawValue: errSecUseKeychainListUnsupported) XCTAssertEqual(status, .useKeychainListUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseKeychainList key to a function which does not support it.") } do { let errSecReturnDataUnsupported: OSStatus = -34008 let status = Status(rawValue: errSecReturnDataUnsupported) XCTAssertEqual(status, .returnDataUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnData key to a function which does not support it.") } do { let errSecReturnAttributesUnsupported: OSStatus = -34009 let status = Status(rawValue: errSecReturnAttributesUnsupported) XCTAssertEqual(status, .returnAttributesUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnAttributes key to a function which does not support it.") } do { let errSecReturnRefUnsupported: OSStatus = -34010 let status = Status(rawValue: errSecReturnRefUnsupported) XCTAssertEqual(status, .returnRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnRef key to a function which does not support it.") } do { let errSecReturnPersitentRefUnsupported: OSStatus = -34011 let status = Status(rawValue: errSecReturnPersitentRefUnsupported) XCTAssertEqual(status, .returnPersitentRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnPersistentRef key to a function which does not support it.") } do { let errSecValueRefUnsupported: OSStatus = -34012 let status = Status(rawValue: errSecValueRefUnsupported) XCTAssertEqual(status, .valueRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecValueRef key to a function which does not support it.") } do { let errSecValuePersistentRefUnsupported: OSStatus = -34013 let status = Status(rawValue: errSecValuePersistentRefUnsupported) XCTAssertEqual(status, .valuePersistentRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecValuePersistentRef key to a function which does not support it.") } do { let errSecReturnMissingPointer: OSStatus = -34014 let status = Status(rawValue: errSecReturnMissingPointer) XCTAssertEqual(status, .returnMissingPointer) XCTAssertEqual(status?.description, "The caller passed asked for something to be returned but did not pass in a result pointer.") } do { let errSecMatchLimitUnsupported: OSStatus = -34015 let status = Status(rawValue: errSecMatchLimitUnsupported) XCTAssertEqual(status, .matchLimitUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecMatchLimit key to a call which does not support limits.") } do { let errSecItemIllegalQuery: OSStatus = -34016 let status = Status(rawValue: errSecItemIllegalQuery) XCTAssertEqual(status, .itemIllegalQuery) XCTAssertEqual(status?.description, "The caller passed in a query which contained too many keys.") } do { let errSecWaitForCallback: OSStatus = -34017 let status = Status(rawValue: errSecWaitForCallback) XCTAssertEqual(status, .waitForCallback) XCTAssertEqual(status?.description, "This operation is incomplete, until the callback is invoked (not an error).") } do { let errSecMissingEntitlement: OSStatus = -34018 let status = Status(rawValue: errSecMissingEntitlement) XCTAssertEqual(status, .missingEntitlement) XCTAssertEqual(status?.description, "Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements.") } do { let errSecUpgradePending: OSStatus = -34019 let status = Status(rawValue: errSecUpgradePending) XCTAssertEqual(status, .upgradePending) XCTAssertEqual(status?.description, "Error returned if keychain database needs a schema migration but the device is locked, clients should wait for a device unlock notification and retry the command.") } do { let errSecMPSignatureInvalid: OSStatus = -25327 let status = Status(rawValue: errSecMPSignatureInvalid) XCTAssertEqual(status, .mpSignatureInvalid) XCTAssertEqual(status?.description, "Signature invalid on MP message") } do { let errSecOTRTooOld: OSStatus = -25328 let status = Status(rawValue: errSecOTRTooOld) XCTAssertEqual(status, .otrTooOld) XCTAssertEqual(status?.description, "Message is too old to use") } do { let errSecOTRIDTooNew: OSStatus = -25329 let status = Status(rawValue: errSecOTRIDTooNew) XCTAssertEqual(status, .otrIDTooNew) XCTAssertEqual(status?.description, "Key ID is too new to use! Message from the future?") } do { let status = Status(rawValue: errSecInsufficientClientID) XCTAssertEqual(status, .insufficientClientID) XCTAssertEqual(status?.description, "The client ID is not correct.") } do { let status = Status(rawValue: errSecDeviceReset) XCTAssertEqual(status, .deviceReset) XCTAssertEqual(status?.description, "A device reset has occurred.") } do { let status = Status(rawValue: errSecDeviceFailed) XCTAssertEqual(status, .deviceFailed) XCTAssertEqual(status?.description, "A device failure has occurred.") } do { let status = Status(rawValue: errSecAppleAddAppACLSubject) XCTAssertEqual(status, .appleAddAppACLSubject) XCTAssertEqual(status?.description, "Adding an application ACL subject failed.") } do { let status = Status(rawValue: errSecApplePublicKeyIncomplete) XCTAssertEqual(status, .applePublicKeyIncomplete) XCTAssertEqual(status?.description, "The public key is incomplete.") } do { let status = Status(rawValue: errSecAppleSignatureMismatch) XCTAssertEqual(status, .appleSignatureMismatch) XCTAssertEqual(status?.description, "A signature mismatch has occurred.") } do { let status = Status(rawValue: errSecAppleInvalidKeyStartDate) XCTAssertEqual(status, .appleInvalidKeyStartDate) XCTAssertEqual(status?.description, "The specified key has an invalid start date.") } do { let status = Status(rawValue: errSecAppleInvalidKeyEndDate) XCTAssertEqual(status, .appleInvalidKeyEndDate) XCTAssertEqual(status?.description, "The specified key has an invalid end date.") } do { let status = Status(rawValue: errSecConversionError) XCTAssertEqual(status, .conversionError) XCTAssertEqual(status?.description, "A conversion error has occurred.") } do { let status = Status(rawValue: errSecAppleSSLv2Rollback) XCTAssertEqual(status, .appleSSLv2Rollback) XCTAssertEqual(status?.description, "A SSLv2 rollback error has occurred.") } do { let status = Status(rawValue: errSecDiskFull) XCTAssertEqual(status, .diskFull) XCTAssertEqual(status?.description, "The disk is full.") } do { let status = Status(rawValue: errSecQuotaExceeded) XCTAssertEqual(status, .quotaExceeded) XCTAssertEqual(status?.description, "The quota was exceeded.") } do { let status = Status(rawValue: errSecFileTooBig) XCTAssertEqual(status, .fileTooBig) XCTAssertEqual(status?.description, "The file is too big.") } do { let status = Status(rawValue: errSecInvalidDatabaseBlob) XCTAssertEqual(status, .invalidDatabaseBlob) XCTAssertEqual(status?.description, "The specified database has an invalid blob.") } do { let status = Status(rawValue: errSecInvalidKeyBlob) XCTAssertEqual(status, .invalidKeyBlob) XCTAssertEqual(status?.description, "The specified database has an invalid key blob.") } do { let status = Status(rawValue: errSecIncompatibleDatabaseBlob) XCTAssertEqual(status, .incompatibleDatabaseBlob) XCTAssertEqual(status?.description, "The specified database has an incompatible blob.") } do { let status = Status(rawValue: errSecIncompatibleKeyBlob) XCTAssertEqual(status, .incompatibleKeyBlob) XCTAssertEqual(status?.description, "The specified database has an incompatible key blob.") } do { let status = Status(rawValue: errSecHostNameMismatch) XCTAssertEqual(status, .hostNameMismatch) XCTAssertEqual(status?.description, "A host name mismatch has occurred.") } do { let status = Status(rawValue: errSecUnknownCriticalExtensionFlag) XCTAssertEqual(status, .unknownCriticalExtensionFlag) XCTAssertEqual(status?.description, "There is an unknown critical extension flag.") } do { let status = Status(rawValue: errSecNoBasicConstraints) XCTAssertEqual(status, .noBasicConstraints) XCTAssertEqual(status?.description, "No basic constraints were found.") } do { let status = Status(rawValue: errSecNoBasicConstraintsCA) XCTAssertEqual(status, .noBasicConstraintsCA) XCTAssertEqual(status?.description, "No basic CA constraints were found.") } do { let status = Status(rawValue: errSecInvalidAuthorityKeyID) XCTAssertEqual(status, .invalidAuthorityKeyID) XCTAssertEqual(status?.description, "The authority key ID is not valid.") } do { let status = Status(rawValue: errSecInvalidSubjectKeyID) XCTAssertEqual(status, .invalidSubjectKeyID) XCTAssertEqual(status?.description, "The subject key ID is not valid.") } do { let status = Status(rawValue: errSecInvalidKeyUsageForPolicy) XCTAssertEqual(status, .invalidKeyUsageForPolicy) XCTAssertEqual(status?.description, "The key usage is not valid for the specified policy.") } do { let status = Status(rawValue: errSecInvalidExtendedKeyUsage) XCTAssertEqual(status, .invalidExtendedKeyUsage) XCTAssertEqual(status?.description, "The extended key usage is not valid.") } do { let status = Status(rawValue: errSecInvalidIDLinkage) XCTAssertEqual(status, .invalidIDLinkage) XCTAssertEqual(status?.description, "The ID linkage is not valid.") } do { let status = Status(rawValue: errSecPathLengthConstraintExceeded) XCTAssertEqual(status, .pathLengthConstraintExceeded) XCTAssertEqual(status?.description, "The path length constraint was exceeded.") } do { let status = Status(rawValue: errSecInvalidRoot) XCTAssertEqual(status, .invalidRoot) XCTAssertEqual(status?.description, "The root or anchor certificate is not valid.") } do { let status = Status(rawValue: errSecCRLExpired) XCTAssertEqual(status, .crlExpired) XCTAssertEqual(status?.description, "The CRL has expired.") } do { let status = Status(rawValue: errSecCRLNotValidYet) XCTAssertEqual(status, .crlNotValidYet) XCTAssertEqual(status?.description, "The CRL is not yet valid.") } do { let status = Status(rawValue: errSecCRLNotFound) XCTAssertEqual(status, .crlNotFound) XCTAssertEqual(status?.description, "The CRL was not found.") } do { let status = Status(rawValue: errSecCRLServerDown) XCTAssertEqual(status, .crlServerDown) XCTAssertEqual(status?.description, "The CRL server is down.") } do { let status = Status(rawValue: errSecCRLBadURI) XCTAssertEqual(status, .crlBadURI) XCTAssertEqual(status?.description, "The CRL has a bad Uniform Resource Identifier.") } do { let status = Status(rawValue: errSecUnknownCertExtension) XCTAssertEqual(status, .unknownCertExtension) XCTAssertEqual(status?.description, "An unknown certificate extension was encountered.") } do { let status = Status(rawValue: errSecUnknownCRLExtension) XCTAssertEqual(status, .unknownCRLExtension) XCTAssertEqual(status?.description, "An unknown CRL extension was encountered.") } do { let status = Status(rawValue: errSecCRLNotTrusted) XCTAssertEqual(status, .crlNotTrusted) XCTAssertEqual(status?.description, "The CRL is not trusted.") } do { let status = Status(rawValue: errSecCRLPolicyFailed) XCTAssertEqual(status, .crlPolicyFailed) XCTAssertEqual(status?.description, "The CRL policy failed.") } do { let status = Status(rawValue: errSecIDPFailure) XCTAssertEqual(status, .idpFailure) XCTAssertEqual(status?.description, "The issuing distribution point was not valid.") } do { let status = Status(rawValue: errSecSMIMEEmailAddressesNotFound) XCTAssertEqual(status, .smimeEmailAddressesNotFound) XCTAssertEqual(status?.description, "An email address mismatch was encountered.") } do { let status = Status(rawValue: errSecSMIMEBadExtendedKeyUsage) XCTAssertEqual(status, .smimeBadExtendedKeyUsage) XCTAssertEqual(status?.description, "The appropriate extended key usage for SMIME was not found.") } do { let status = Status(rawValue: errSecSMIMEBadKeyUsage) XCTAssertEqual(status, .smimeBadKeyUsage) XCTAssertEqual(status?.description, "The key usage is not compatible with SMIME.") } do { let status = Status(rawValue: errSecSMIMEKeyUsageNotCritical) XCTAssertEqual(status, .smimeKeyUsageNotCritical) XCTAssertEqual(status?.description, "The key usage extension is not marked as critical.") } do { let status = Status(rawValue: errSecSMIMENoEmailAddress) XCTAssertEqual(status, .smimeNoEmailAddress) XCTAssertEqual(status?.description, "No email address was found in the certificate.") } do { let status = Status(rawValue: errSecSMIMESubjAltNameNotCritical) XCTAssertEqual(status, .smimeSubjAltNameNotCritical) XCTAssertEqual(status?.description, "The subject alternative name extension is not marked as critical.") } do { let status = Status(rawValue: errSecSSLBadExtendedKeyUsage) XCTAssertEqual(status, .sslBadExtendedKeyUsage) XCTAssertEqual(status?.description, "The appropriate extended key usage for SSL was not found.") } do { let status = Status(rawValue: errSecOCSPBadResponse) XCTAssertEqual(status, .ocspBadResponse) XCTAssertEqual(status?.description, "The OCSP response was incorrect or could not be parsed.") } do { let status = Status(rawValue: errSecOCSPBadRequest) XCTAssertEqual(status, .ocspBadRequest) XCTAssertEqual(status?.description, "The OCSP request was incorrect or could not be parsed.") } do { let status = Status(rawValue: errSecOCSPUnavailable) XCTAssertEqual(status, .ocspUnavailable) XCTAssertEqual(status?.description, "OCSP service is unavailable.") } do { let status = Status(rawValue: errSecOCSPStatusUnrecognized) XCTAssertEqual(status, .ocspStatusUnrecognized) XCTAssertEqual(status?.description, "The OCSP server did not recognize this certificate.") } do { let status = Status(rawValue: errSecEndOfData) XCTAssertEqual(status, .endOfData) XCTAssertEqual(status?.description, "An end-of-data was detected.") } do { let status = Status(rawValue: errSecIncompleteCertRevocationCheck) XCTAssertEqual(status, .incompleteCertRevocationCheck) XCTAssertEqual(status?.description, "An incomplete certificate revocation check occurred.") } do { let status = Status(rawValue: errSecNetworkFailure) XCTAssertEqual(status, .networkFailure) XCTAssertEqual(status?.description, "A network failure occurred.") } do { let status = Status(rawValue: errSecOCSPNotTrustedToAnchor) XCTAssertEqual(status, .ocspNotTrustedToAnchor) XCTAssertEqual(status?.description, "The OCSP response was not trusted to a root or anchor certificate.") } do { let status = Status(rawValue: errSecRecordModified) XCTAssertEqual(status, .recordModified) XCTAssertEqual(status?.description, "The record was modified.") } do { let status = Status(rawValue: errSecOCSPSignatureError) XCTAssertEqual(status, .ocspSignatureError) XCTAssertEqual(status?.description, "The OCSP response had an invalid signature.") } do { let status = Status(rawValue: errSecOCSPNoSigner) XCTAssertEqual(status, .ocspNoSigner) XCTAssertEqual(status?.description, "The OCSP response had no signer.") } do { let status = Status(rawValue: errSecOCSPResponderMalformedReq) XCTAssertEqual(status, .ocspResponderMalformedReq) XCTAssertEqual(status?.description, "The OCSP responder was given a malformed request.") } do { let status = Status(rawValue: errSecOCSPResponderInternalError) XCTAssertEqual(status, .ocspResponderInternalError) XCTAssertEqual(status?.description, "The OCSP responder encountered an internal error.") } do { let status = Status(rawValue: errSecOCSPResponderTryLater) XCTAssertEqual(status, .ocspResponderTryLater) XCTAssertEqual(status?.description, "The OCSP responder is busy, try again later.") } do { let status = Status(rawValue: errSecOCSPResponderSignatureRequired) XCTAssertEqual(status, .ocspResponderSignatureRequired) XCTAssertEqual(status?.description, "The OCSP responder requires a signature.") } do { let status = Status(rawValue: errSecOCSPResponderUnauthorized) XCTAssertEqual(status, .ocspResponderUnauthorized) XCTAssertEqual(status?.description, "The OCSP responder rejected this request as unauthorized.") } do { let status = Status(rawValue: errSecOCSPResponseNonceMismatch) XCTAssertEqual(status, .ocspResponseNonceMismatch) XCTAssertEqual(status?.description, "The OCSP response nonce did not match the request.") } do { let status = Status(rawValue: errSecCodeSigningBadCertChainLength) XCTAssertEqual(status, .codeSigningBadCertChainLength) XCTAssertEqual(status?.description, "Code signing encountered an incorrect certificate chain length.") } do { let status = Status(rawValue: errSecCodeSigningNoBasicConstraints) XCTAssertEqual(status, .codeSigningNoBasicConstraints) XCTAssertEqual(status?.description, "Code signing found no basic constraints.") } do { let status = Status(rawValue: errSecCodeSigningBadPathLengthConstraint) XCTAssertEqual(status, .codeSigningBadPathLengthConstraint) XCTAssertEqual(status?.description, "Code signing encountered an incorrect path length constraint.") } do { let status = Status(rawValue: errSecCodeSigningNoExtendedKeyUsage) XCTAssertEqual(status, .codeSigningNoExtendedKeyUsage) XCTAssertEqual(status?.description, "Code signing found no extended key usage.") } do { let status = Status(rawValue: errSecCodeSigningDevelopment) XCTAssertEqual(status, .codeSigningDevelopment) XCTAssertEqual(status?.description, "Code signing indicated use of a development-only certificate.") } do { let status = Status(rawValue: errSecResourceSignBadCertChainLength) XCTAssertEqual(status, .resourceSignBadCertChainLength) XCTAssertEqual(status?.description, "Resource signing has encountered an incorrect certificate chain length.") } do { let status = Status(rawValue: errSecResourceSignBadExtKeyUsage) XCTAssertEqual(status, .resourceSignBadExtKeyUsage) XCTAssertEqual(status?.description, "Resource signing has encountered an error in the extended key usage.") } do { let status = Status(rawValue: errSecTrustSettingDeny) XCTAssertEqual(status, .trustSettingDeny) XCTAssertEqual(status?.description, "The trust setting for this policy was set to Deny.") } do { let status = Status(rawValue: errSecInvalidSubjectName) XCTAssertEqual(status, .invalidSubjectName) XCTAssertEqual(status?.description, "An invalid certificate subject name was encountered.") } do { let status = Status(rawValue: errSecUnknownQualifiedCertStatement) XCTAssertEqual(status, .unknownQualifiedCertStatement) XCTAssertEqual(status?.description, "An unknown qualified certificate statement was encountered.") } do { let status = Status(rawValue: errSecMobileMeRequestQueued) XCTAssertEqual(status, .mobileMeRequestQueued) XCTAssertEqual(status?.description, "The MobileMe request will be sent during the next connection.") } do { let status = Status(rawValue: errSecMobileMeRequestRedirected) XCTAssertEqual(status, .mobileMeRequestRedirected) XCTAssertEqual(status?.description, "The MobileMe request was redirected.") } do { let status = Status(rawValue: errSecMobileMeServerError) XCTAssertEqual(status, .mobileMeServerError) XCTAssertEqual(status?.description, "A MobileMe server error occurred.") } do { let status = Status(rawValue: errSecMobileMeServerNotAvailable) XCTAssertEqual(status, .mobileMeServerNotAvailable) XCTAssertEqual(status?.description, "The MobileMe server is not available.") } do { let status = Status(rawValue: errSecMobileMeServerAlreadyExists) XCTAssertEqual(status, .mobileMeServerAlreadyExists) XCTAssertEqual(status?.description, "The MobileMe server reported that the item already exists.") } do { let status = Status(rawValue: errSecMobileMeServerServiceErr) XCTAssertEqual(status, .mobileMeServerServiceErr) XCTAssertEqual(status?.description, "A MobileMe service error has occurred.") } do { let status = Status(rawValue: errSecMobileMeRequestAlreadyPending) XCTAssertEqual(status, .mobileMeRequestAlreadyPending) XCTAssertEqual(status?.description, "A MobileMe request is already pending.") } do { let status = Status(rawValue: errSecMobileMeNoRequestPending) XCTAssertEqual(status, .mobileMeNoRequestPending) XCTAssertEqual(status?.description, "MobileMe has no request pending.") } do { let status = Status(rawValue: errSecMobileMeCSRVerifyFailure) XCTAssertEqual(status, .mobileMeCSRVerifyFailure) XCTAssertEqual(status?.description, "A MobileMe CSR verification failure has occurred.") } do { let status = Status(rawValue: errSecMobileMeFailedConsistencyCheck) XCTAssertEqual(status, .mobileMeFailedConsistencyCheck) XCTAssertEqual(status?.description, "MobileMe has found a failed consistency check.") } do { let status = Status(rawValue: errSecNotInitialized) XCTAssertEqual(status, .notInitialized) XCTAssertEqual(status?.description, "A function was called without initializing CSSM.") } do { let status = Status(rawValue: errSecInvalidHandleUsage) XCTAssertEqual(status, .invalidHandleUsage) XCTAssertEqual(status?.description, "The CSSM handle does not match with the service type.") } do { let status = Status(rawValue: errSecPVCReferentNotFound) XCTAssertEqual(status, .pvcReferentNotFound) XCTAssertEqual(status?.description, "A reference to the calling module was not found in the list of authorized callers.") } do { let status = Status(rawValue: errSecFunctionIntegrityFail) XCTAssertEqual(status, .functionIntegrityFail) XCTAssertEqual(status?.description, "A function address was not within the verified module.") } do { let status = Status(rawValue: errSecInternalError) XCTAssertEqual(status, .internalError) XCTAssertEqual(status?.description, "An internal error has occurred.") } do { let status = Status(rawValue: errSecMemoryError) XCTAssertEqual(status, .memoryError) XCTAssertEqual(status?.description, "A memory error has occurred.") } do { let status = Status(rawValue: errSecInvalidData) XCTAssertEqual(status, .invalidData) XCTAssertEqual(status?.description, "Invalid data was encountered.") } do { let status = Status(rawValue: errSecMDSError) XCTAssertEqual(status, .mdsError) XCTAssertEqual(status?.description, "A Module Directory Service error has occurred.") } do { let status = Status(rawValue: errSecInvalidPointer) XCTAssertEqual(status, .invalidPointer) XCTAssertEqual(status?.description, "An invalid pointer was encountered.") } do { let status = Status(rawValue: errSecSelfCheckFailed) XCTAssertEqual(status, .selfCheckFailed) XCTAssertEqual(status?.description, "Self-check has failed.") } do { let status = Status(rawValue: errSecFunctionFailed) XCTAssertEqual(status, .functionFailed) XCTAssertEqual(status?.description, "A function has failed.") } do { let status = Status(rawValue: errSecModuleManifestVerifyFailed) XCTAssertEqual(status, .moduleManifestVerifyFailed) XCTAssertEqual(status?.description, "A module manifest verification failure has occurred.") } do { let status = Status(rawValue: errSecInvalidGUID) XCTAssertEqual(status, .invalidGUID) XCTAssertEqual(status?.description, "An invalid GUID was encountered.") } do { let status = Status(rawValue: errSecInvalidHandle) XCTAssertEqual(status, .invalidHandle) XCTAssertEqual(status?.description, "An invalid handle was encountered.") } do { let status = Status(rawValue: errSecInvalidDBList) XCTAssertEqual(status, .invalidDBList) XCTAssertEqual(status?.description, "An invalid DB list was encountered.") } do { let status = Status(rawValue: errSecInvalidPassthroughID) XCTAssertEqual(status, .invalidPassthroughID) XCTAssertEqual(status?.description, "An invalid passthrough ID was encountered.") } do { let status = Status(rawValue: errSecInvalidNetworkAddress) XCTAssertEqual(status, .invalidNetworkAddress) XCTAssertEqual(status?.description, "An invalid network address was encountered.") } do { let status = Status(rawValue: errSecCRLAlreadySigned) XCTAssertEqual(status, .crlAlreadySigned) XCTAssertEqual(status?.description, "The certificate revocation list is already signed.") } do { let status = Status(rawValue: errSecInvalidNumberOfFields) XCTAssertEqual(status, .invalidNumberOfFields) XCTAssertEqual(status?.description, "An invalid number of fields were encountered.") } do { let status = Status(rawValue: errSecVerificationFailure) XCTAssertEqual(status, .verificationFailure) XCTAssertEqual(status?.description, "A verification failure occurred.") } do { let status = Status(rawValue: errSecUnknownTag) XCTAssertEqual(status, .unknownTag) XCTAssertEqual(status?.description, "An unknown tag was encountered.") } do { let status = Status(rawValue: errSecInvalidSignature) XCTAssertEqual(status, .invalidSignature) XCTAssertEqual(status?.description, "An invalid signature was encountered.") } do { let status = Status(rawValue: errSecInvalidName) XCTAssertEqual(status, .invalidName) XCTAssertEqual(status?.description, "An invalid name was encountered.") } do { let status = Status(rawValue: errSecInvalidCertificateRef) XCTAssertEqual(status, .invalidCertificateRef) XCTAssertEqual(status?.description, "An invalid certificate reference was encountered.") } do { let status = Status(rawValue: errSecInvalidCertificateGroup) XCTAssertEqual(status, .invalidCertificateGroup) XCTAssertEqual(status?.description, "An invalid certificate group was encountered.") } do { let status = Status(rawValue: errSecTagNotFound) XCTAssertEqual(status, .tagNotFound) XCTAssertEqual(status?.description, "The specified tag was not found.") } do { let status = Status(rawValue: errSecInvalidQuery) XCTAssertEqual(status, .invalidQuery) XCTAssertEqual(status?.description, "The specified query was not valid.") } do { let status = Status(rawValue: errSecInvalidValue) XCTAssertEqual(status, .invalidValue) XCTAssertEqual(status?.description, "An invalid value was detected.") } do { let status = Status(rawValue: errSecCallbackFailed) XCTAssertEqual(status, .callbackFailed) XCTAssertEqual(status?.description, "A callback has failed.") } do { let status = Status(rawValue: errSecACLDeleteFailed) XCTAssertEqual(status, .aclDeleteFailed) XCTAssertEqual(status?.description, "An ACL delete operation has failed.") } do { let status = Status(rawValue: errSecACLReplaceFailed) XCTAssertEqual(status, .aclReplaceFailed) XCTAssertEqual(status?.description, "An ACL replace operation has failed.") } do { let status = Status(rawValue: errSecACLAddFailed) XCTAssertEqual(status, .aclAddFailed) XCTAssertEqual(status?.description, "An ACL add operation has failed.") } do { let status = Status(rawValue: errSecACLChangeFailed) XCTAssertEqual(status, .aclChangeFailed) XCTAssertEqual(status?.description, "An ACL change operation has failed.") } do { let status = Status(rawValue: errSecInvalidAccessCredentials) XCTAssertEqual(status, .invalidAccessCredentials) XCTAssertEqual(status?.description, "Invalid access credentials were encountered.") } do { let status = Status(rawValue: errSecInvalidRecord) XCTAssertEqual(status, .invalidRecord) XCTAssertEqual(status?.description, "An invalid record was encountered.") } do { let status = Status(rawValue: errSecInvalidACL) XCTAssertEqual(status, .invalidACL) XCTAssertEqual(status?.description, "An invalid ACL was encountered.") } do { let status = Status(rawValue: errSecInvalidSampleValue) XCTAssertEqual(status, .invalidSampleValue) XCTAssertEqual(status?.description, "An invalid sample value was encountered.") } do { let status = Status(rawValue: errSecIncompatibleVersion) XCTAssertEqual(status, .incompatibleVersion) XCTAssertEqual(status?.description, "An incompatible version was encountered.") } do { let status = Status(rawValue: errSecPrivilegeNotGranted) XCTAssertEqual(status, .privilegeNotGranted) XCTAssertEqual(status?.description, "The privilege was not granted.") } do { let status = Status(rawValue: errSecInvalidScope) XCTAssertEqual(status, .invalidScope) XCTAssertEqual(status?.description, "An invalid scope was encountered.") } do { let status = Status(rawValue: errSecPVCAlreadyConfigured) XCTAssertEqual(status, .pvcAlreadyConfigured) XCTAssertEqual(status?.description, "The PVC is already configured.") } do { let status = Status(rawValue: errSecInvalidPVC) XCTAssertEqual(status, .invalidPVC) XCTAssertEqual(status?.description, "An invalid PVC was encountered.") } do { let status = Status(rawValue: errSecEMMLoadFailed) XCTAssertEqual(status, .emmLoadFailed) XCTAssertEqual(status?.description, "The EMM load has failed.") } do { let status = Status(rawValue: errSecEMMUnloadFailed) XCTAssertEqual(status, .emmUnloadFailed) XCTAssertEqual(status?.description, "The EMM unload has failed.") } do { let status = Status(rawValue: errSecAddinLoadFailed) XCTAssertEqual(status, .addinLoadFailed) XCTAssertEqual(status?.description, "The add-in load operation has failed.") } do { let status = Status(rawValue: errSecInvalidKeyRef) XCTAssertEqual(status, .invalidKeyRef) XCTAssertEqual(status?.description, "An invalid key was encountered.") } do { let status = Status(rawValue: errSecInvalidKeyHierarchy) XCTAssertEqual(status, .invalidKeyHierarchy) XCTAssertEqual(status?.description, "An invalid key hierarchy was encountered.") } do { let status = Status(rawValue: errSecAddinUnloadFailed) XCTAssertEqual(status, .addinUnloadFailed) XCTAssertEqual(status?.description, "The add-in unload operation has failed.") } do { let status = Status(rawValue: errSecLibraryReferenceNotFound) XCTAssertEqual(status, .libraryReferenceNotFound) XCTAssertEqual(status?.description, "A library reference was not found.") } do { let status = Status(rawValue: errSecInvalidAddinFunctionTable) XCTAssertEqual(status, .invalidAddinFunctionTable) XCTAssertEqual(status?.description, "An invalid add-in function table was encountered.") } do { let status = Status(rawValue: errSecInvalidServiceMask) XCTAssertEqual(status, .invalidServiceMask) XCTAssertEqual(status?.description, "An invalid service mask was encountered.") } do { let status = Status(rawValue: errSecModuleNotLoaded) XCTAssertEqual(status, .moduleNotLoaded) XCTAssertEqual(status?.description, "A module was not loaded.") } do { let status = Status(rawValue: errSecInvalidSubServiceID) XCTAssertEqual(status, .invalidSubServiceID) XCTAssertEqual(status?.description, "An invalid subservice ID was encountered.") } do { let status = Status(rawValue: errSecAttributeNotInContext) XCTAssertEqual(status, .attributeNotInContext) XCTAssertEqual(status?.description, "An attribute was not in the context.") } do { let status = Status(rawValue: errSecModuleManagerInitializeFailed) XCTAssertEqual(status, .moduleManagerInitializeFailed) XCTAssertEqual(status?.description, "A module failed to initialize.") } do { let status = Status(rawValue: errSecModuleManagerNotFound) XCTAssertEqual(status, .moduleManagerNotFound) XCTAssertEqual(status?.description, "A module was not found.") } do { let status = Status(rawValue: errSecEventNotificationCallbackNotFound) XCTAssertEqual(status, .eventNotificationCallbackNotFound) XCTAssertEqual(status?.description, "An event notification callback was not found.") } do { let status = Status(rawValue: errSecInputLengthError) XCTAssertEqual(status, .inputLengthError) XCTAssertEqual(status?.description, "An input length error was encountered.") } do { let status = Status(rawValue: errSecOutputLengthError) XCTAssertEqual(status, .outputLengthError) XCTAssertEqual(status?.description, "An output length error was encountered.") } do { let status = Status(rawValue: errSecPrivilegeNotSupported) XCTAssertEqual(status, .privilegeNotSupported) XCTAssertEqual(status?.description, "The privilege is not supported.") } do { let status = Status(rawValue: errSecDeviceError) XCTAssertEqual(status, .deviceError) XCTAssertEqual(status?.description, "A device error was encountered.") } do { let status = Status(rawValue: errSecAttachHandleBusy) XCTAssertEqual(status, .attachHandleBusy) XCTAssertEqual(status?.description, "The CSP handle was busy.") } do { let status = Status(rawValue: errSecNotLoggedIn) XCTAssertEqual(status, .notLoggedIn) XCTAssertEqual(status?.description, "You are not logged in.") } do { let status = Status(rawValue: errSecAlgorithmMismatch) XCTAssertEqual(status, .algorithmMismatch) XCTAssertEqual(status?.description, "An algorithm mismatch was encountered.") } do { let status = Status(rawValue: errSecKeyUsageIncorrect) XCTAssertEqual(status, .keyUsageIncorrect) XCTAssertEqual(status?.description, "The key usage is incorrect.") } do { let status = Status(rawValue: errSecKeyBlobTypeIncorrect) XCTAssertEqual(status, .keyBlobTypeIncorrect) XCTAssertEqual(status?.description, "The key blob type is incorrect.") } do { let status = Status(rawValue: errSecKeyHeaderInconsistent) XCTAssertEqual(status, .keyHeaderInconsistent) XCTAssertEqual(status?.description, "The key header is inconsistent.") } do { let status = Status(rawValue: errSecUnsupportedKeyFormat) XCTAssertEqual(status, .unsupportedKeyFormat) XCTAssertEqual(status?.description, "The key header format is not supported.") } do { let status = Status(rawValue: errSecUnsupportedKeySize) XCTAssertEqual(status, .unsupportedKeySize) XCTAssertEqual(status?.description, "The key size is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyUsageMask) XCTAssertEqual(status, .invalidKeyUsageMask) XCTAssertEqual(status?.description, "The key usage mask is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyUsageMask) XCTAssertEqual(status, .unsupportedKeyUsageMask) XCTAssertEqual(status?.description, "The key usage mask is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyAttributeMask) XCTAssertEqual(status, .invalidKeyAttributeMask) XCTAssertEqual(status?.description, "The key attribute mask is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyAttributeMask) XCTAssertEqual(status, .unsupportedKeyAttributeMask) XCTAssertEqual(status?.description, "The key attribute mask is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyLabel) XCTAssertEqual(status, .invalidKeyLabel) XCTAssertEqual(status?.description, "The key label is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyLabel) XCTAssertEqual(status, .unsupportedKeyLabel) XCTAssertEqual(status?.description, "The key label is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyFormat) XCTAssertEqual(status, .invalidKeyFormat) XCTAssertEqual(status?.description, "The key format is not valid.") } do { let status = Status(rawValue: errSecUnsupportedVectorOfBuffers) XCTAssertEqual(status, .unsupportedVectorOfBuffers) XCTAssertEqual(status?.description, "The vector of buffers is not supported.") } do { let status = Status(rawValue: errSecInvalidInputVector) XCTAssertEqual(status, .invalidInputVector) XCTAssertEqual(status?.description, "The input vector is not valid.") } do { let status = Status(rawValue: errSecInvalidOutputVector) XCTAssertEqual(status, .invalidOutputVector) XCTAssertEqual(status?.description, "The output vector is not valid.") } do { let status = Status(rawValue: errSecInvalidContext) XCTAssertEqual(status, .invalidContext) XCTAssertEqual(status?.description, "An invalid context was encountered.") } do { let status = Status(rawValue: errSecInvalidAlgorithm) XCTAssertEqual(status, .invalidAlgorithm) XCTAssertEqual(status?.description, "An invalid algorithm was encountered.") } do { let status = Status(rawValue: errSecInvalidAttributeKey) XCTAssertEqual(status, .invalidAttributeKey) XCTAssertEqual(status?.description, "A key attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKey) XCTAssertEqual(status, .missingAttributeKey) XCTAssertEqual(status?.description, "A key attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeInitVector) XCTAssertEqual(status, .invalidAttributeInitVector) XCTAssertEqual(status?.description, "An init vector attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeInitVector) XCTAssertEqual(status, .missingAttributeInitVector) XCTAssertEqual(status?.description, "An init vector attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSalt) XCTAssertEqual(status, .invalidAttributeSalt) XCTAssertEqual(status?.description, "A salt attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSalt) XCTAssertEqual(status, .missingAttributeSalt) XCTAssertEqual(status?.description, "A salt attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePadding) XCTAssertEqual(status, .invalidAttributePadding) XCTAssertEqual(status?.description, "A padding attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePadding) XCTAssertEqual(status, .missingAttributePadding) XCTAssertEqual(status?.description, "A padding attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeRandom) XCTAssertEqual(status, .invalidAttributeRandom) XCTAssertEqual(status?.description, "A random number attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeRandom) XCTAssertEqual(status, .missingAttributeRandom) XCTAssertEqual(status?.description, "A random number attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSeed) XCTAssertEqual(status, .invalidAttributeSeed) XCTAssertEqual(status?.description, "A seed attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSeed) XCTAssertEqual(status, .missingAttributeSeed) XCTAssertEqual(status?.description, "A seed attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePassphrase) XCTAssertEqual(status, .invalidAttributePassphrase) XCTAssertEqual(status?.description, "A passphrase attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePassphrase) XCTAssertEqual(status, .missingAttributePassphrase) XCTAssertEqual(status?.description, "A passphrase attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeKeyLength) XCTAssertEqual(status, .invalidAttributeKeyLength) XCTAssertEqual(status?.description, "A key length attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKeyLength) XCTAssertEqual(status, .missingAttributeKeyLength) XCTAssertEqual(status?.description, "A key length attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeBlockSize) XCTAssertEqual(status, .invalidAttributeBlockSize) XCTAssertEqual(status?.description, "A block size attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeBlockSize) XCTAssertEqual(status, .missingAttributeBlockSize) XCTAssertEqual(status?.description, "A block size attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeOutputSize) XCTAssertEqual(status, .invalidAttributeOutputSize) XCTAssertEqual(status?.description, "An output size attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeOutputSize) XCTAssertEqual(status, .missingAttributeOutputSize) XCTAssertEqual(status?.description, "An output size attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeRounds) XCTAssertEqual(status, .invalidAttributeRounds) XCTAssertEqual(status?.description, "The number of rounds attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeRounds) XCTAssertEqual(status, .missingAttributeRounds) XCTAssertEqual(status?.description, "The number of rounds attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAlgorithmParms) XCTAssertEqual(status, .invalidAlgorithmParms) XCTAssertEqual(status?.description, "An algorithm parameters attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAlgorithmParms) XCTAssertEqual(status, .missingAlgorithmParms) XCTAssertEqual(status?.description, "An algorithm parameters attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeLabel) XCTAssertEqual(status, .invalidAttributeLabel) XCTAssertEqual(status?.description, "A label attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeLabel) XCTAssertEqual(status, .missingAttributeLabel) XCTAssertEqual(status?.description, "A label attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeKeyType) XCTAssertEqual(status, .invalidAttributeKeyType) XCTAssertEqual(status?.description, "A key type attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKeyType) XCTAssertEqual(status, .missingAttributeKeyType) XCTAssertEqual(status?.description, "A key type attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeMode) XCTAssertEqual(status, .invalidAttributeMode) XCTAssertEqual(status?.description, "A mode attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeMode) XCTAssertEqual(status, .missingAttributeMode) XCTAssertEqual(status?.description, "A mode attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeEffectiveBits) XCTAssertEqual(status, .invalidAttributeEffectiveBits) XCTAssertEqual(status?.description, "An effective bits attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeEffectiveBits) XCTAssertEqual(status, .missingAttributeEffectiveBits) XCTAssertEqual(status?.description, "An effective bits attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeStartDate) XCTAssertEqual(status, .invalidAttributeStartDate) XCTAssertEqual(status?.description, "A start date attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeStartDate) XCTAssertEqual(status, .missingAttributeStartDate) XCTAssertEqual(status?.description, "A start date attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeEndDate) XCTAssertEqual(status, .invalidAttributeEndDate) XCTAssertEqual(status?.description, "An end date attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeEndDate) XCTAssertEqual(status, .missingAttributeEndDate) XCTAssertEqual(status?.description, "An end date attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeVersion) XCTAssertEqual(status, .invalidAttributeVersion) XCTAssertEqual(status?.description, "A version attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeVersion) XCTAssertEqual(status, .missingAttributeVersion) XCTAssertEqual(status?.description, "A version attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePrime) XCTAssertEqual(status, .invalidAttributePrime) XCTAssertEqual(status?.description, "A prime attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePrime) XCTAssertEqual(status, .missingAttributePrime) XCTAssertEqual(status?.description, "A prime attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeBase) XCTAssertEqual(status, .invalidAttributeBase) XCTAssertEqual(status?.description, "A base attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeBase) XCTAssertEqual(status, .missingAttributeBase) XCTAssertEqual(status?.description, "A base attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSubprime) XCTAssertEqual(status, .invalidAttributeSubprime) XCTAssertEqual(status?.description, "A subprime attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSubprime) XCTAssertEqual(status, .missingAttributeSubprime) XCTAssertEqual(status?.description, "A subprime attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeIterationCount) XCTAssertEqual(status, .invalidAttributeIterationCount) XCTAssertEqual(status?.description, "An iteration count attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeIterationCount) XCTAssertEqual(status, .missingAttributeIterationCount) XCTAssertEqual(status?.description, "An iteration count attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeDLDBHandle) XCTAssertEqual(status, .invalidAttributeDLDBHandle) XCTAssertEqual(status?.description, "A database handle attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeDLDBHandle) XCTAssertEqual(status, .missingAttributeDLDBHandle) XCTAssertEqual(status?.description, "A database handle attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeAccessCredentials) XCTAssertEqual(status, .invalidAttributeAccessCredentials) XCTAssertEqual(status?.description, "An access credentials attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeAccessCredentials) XCTAssertEqual(status, .missingAttributeAccessCredentials) XCTAssertEqual(status?.description, "An access credentials attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePublicKeyFormat) XCTAssertEqual(status, .invalidAttributePublicKeyFormat) XCTAssertEqual(status?.description, "A public key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePublicKeyFormat) XCTAssertEqual(status, .missingAttributePublicKeyFormat) XCTAssertEqual(status?.description, "A public key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePrivateKeyFormat) XCTAssertEqual(status, .invalidAttributePrivateKeyFormat) XCTAssertEqual(status?.description, "A private key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePrivateKeyFormat) XCTAssertEqual(status, .missingAttributePrivateKeyFormat) XCTAssertEqual(status?.description, "A private key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSymmetricKeyFormat) XCTAssertEqual(status, .invalidAttributeSymmetricKeyFormat) XCTAssertEqual(status?.description, "A symmetric key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSymmetricKeyFormat) XCTAssertEqual(status, .missingAttributeSymmetricKeyFormat) XCTAssertEqual(status?.description, "A symmetric key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeWrappedKeyFormat) XCTAssertEqual(status, .invalidAttributeWrappedKeyFormat) XCTAssertEqual(status?.description, "A wrapped key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeWrappedKeyFormat) XCTAssertEqual(status, .missingAttributeWrappedKeyFormat) XCTAssertEqual(status?.description, "A wrapped key format attribute was missing.") } do { let status = Status(rawValue: errSecStagedOperationInProgress) XCTAssertEqual(status, .stagedOperationInProgress) XCTAssertEqual(status?.description, "A staged operation is in progress.") } do { let status = Status(rawValue: errSecStagedOperationNotStarted) XCTAssertEqual(status, .stagedOperationNotStarted) XCTAssertEqual(status?.description, "A staged operation was not started.") } do { let status = Status(rawValue: errSecVerifyFailed) XCTAssertEqual(status, .verifyFailed) XCTAssertEqual(status?.description, "A cryptographic verification failure has occurred.") } do { let status = Status(rawValue: errSecQuerySizeUnknown) XCTAssertEqual(status, .querySizeUnknown) XCTAssertEqual(status?.description, "The query size is unknown.") } do { let status = Status(rawValue: errSecBlockSizeMismatch) XCTAssertEqual(status, .blockSizeMismatch) XCTAssertEqual(status?.description, "A block size mismatch occurred.") } do { let status = Status(rawValue: errSecPublicKeyInconsistent) XCTAssertEqual(status, .publicKeyInconsistent) XCTAssertEqual(status?.description, "The public key was inconsistent.") } do { let status = Status(rawValue: errSecDeviceVerifyFailed) XCTAssertEqual(status, .deviceVerifyFailed) XCTAssertEqual(status?.description, "A device verification failure has occurred.") } do { let status = Status(rawValue: errSecInvalidLoginName) XCTAssertEqual(status, .invalidLoginName) XCTAssertEqual(status?.description, "An invalid login name was detected.") } do { let status = Status(rawValue: errSecAlreadyLoggedIn) XCTAssertEqual(status, .alreadyLoggedIn) XCTAssertEqual(status?.description, "The user is already logged in.") } do { let status = Status(rawValue: errSecInvalidDigestAlgorithm) XCTAssertEqual(status, .invalidDigestAlgorithm) XCTAssertEqual(status?.description, "An invalid digest algorithm was detected.") } do { let status = Status(rawValue: errSecInvalidCRLGroup) XCTAssertEqual(status, .invalidCRLGroup) XCTAssertEqual(status?.description, "An invalid CRL group was detected.") } do { let status = Status(rawValue: errSecCertificateCannotOperate) XCTAssertEqual(status, .certificateCannotOperate) XCTAssertEqual(status?.description, "The certificate cannot operate.") } do { let status = Status(rawValue: errSecCertificateExpired) XCTAssertEqual(status, .certificateExpired) XCTAssertEqual(status?.description, "An expired certificate was detected.") } do { let status = Status(rawValue: errSecCertificateNotValidYet) XCTAssertEqual(status, .certificateNotValidYet) XCTAssertEqual(status?.description, "The certificate is not yet valid.") } do { let status = Status(rawValue: errSecCertificateRevoked) XCTAssertEqual(status, .certificateRevoked) XCTAssertEqual(status?.description, "The certificate was revoked.") } do { let status = Status(rawValue: errSecCertificateSuspended) XCTAssertEqual(status, .certificateSuspended) XCTAssertEqual(status?.description, "The certificate was suspended.") } do { let status = Status(rawValue: errSecInsufficientCredentials) XCTAssertEqual(status, .insufficientCredentials) XCTAssertEqual(status?.description, "Insufficient credentials were detected.") } do { let status = Status(rawValue: errSecInvalidAction) XCTAssertEqual(status, .invalidAction) XCTAssertEqual(status?.description, "The action was not valid.") } do { let status = Status(rawValue: errSecInvalidAuthority) XCTAssertEqual(status, .invalidAuthority) XCTAssertEqual(status?.description, "The authority was not valid.") } do { let status = Status(rawValue: errSecVerifyActionFailed) XCTAssertEqual(status, .verifyActionFailed) XCTAssertEqual(status?.description, "A verify action has failed.") } do { let status = Status(rawValue: errSecInvalidCertAuthority) XCTAssertEqual(status, .invalidCertAuthority) XCTAssertEqual(status?.description, "The certificate authority was not valid.") } do { let status = Status(rawValue: errSecInvaldCRLAuthority) XCTAssertEqual(status, .invaldCRLAuthority) XCTAssertEqual(status?.description, "The CRL authority was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLEncoding) XCTAssertEqual(status, .invalidCRLEncoding) XCTAssertEqual(status?.description, "The CRL encoding was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLType) XCTAssertEqual(status, .invalidCRLType) XCTAssertEqual(status?.description, "The CRL type was not valid.") } do { let status = Status(rawValue: errSecInvalidCRL) XCTAssertEqual(status, .invalidCRL) XCTAssertEqual(status?.description, "The CRL was not valid.") } do { let status = Status(rawValue: errSecInvalidFormType) XCTAssertEqual(status, .invalidFormType) XCTAssertEqual(status?.description, "The form type was not valid.") } do { let status = Status(rawValue: errSecInvalidID) XCTAssertEqual(status, .invalidID) XCTAssertEqual(status?.description, "The ID was not valid.") } do { let status = Status(rawValue: errSecInvalidIdentifier) XCTAssertEqual(status, .invalidIdentifier) XCTAssertEqual(status?.description, "The identifier was not valid.") } do { let status = Status(rawValue: errSecInvalidIndex) XCTAssertEqual(status, .invalidIndex) XCTAssertEqual(status?.description, "The index was not valid.") } do { let status = Status(rawValue: errSecInvalidPolicyIdentifiers) XCTAssertEqual(status, .invalidPolicyIdentifiers) XCTAssertEqual(status?.description, "The policy identifiers are not valid.") } do { let status = Status(rawValue: errSecInvalidTimeString) XCTAssertEqual(status, .invalidTimeString) XCTAssertEqual(status?.description, "The time specified was not valid.") } do { let status = Status(rawValue: errSecInvalidReason) XCTAssertEqual(status, .invalidReason) XCTAssertEqual(status?.description, "The trust policy reason was not valid.") } do { let status = Status(rawValue: errSecInvalidRequestInputs) XCTAssertEqual(status, .invalidRequestInputs) XCTAssertEqual(status?.description, "The request inputs are not valid.") } do { let status = Status(rawValue: errSecInvalidResponseVector) XCTAssertEqual(status, .invalidResponseVector) XCTAssertEqual(status?.description, "The response vector was not valid.") } do { let status = Status(rawValue: errSecInvalidStopOnPolicy) XCTAssertEqual(status, .invalidStopOnPolicy) XCTAssertEqual(status?.description, "The stop-on policy was not valid.") } do { let status = Status(rawValue: errSecInvalidTuple) XCTAssertEqual(status, .invalidTuple) XCTAssertEqual(status?.description, "The tuple was not valid.") } do { let status = Status(rawValue: errSecMultipleValuesUnsupported) XCTAssertEqual(status, .multipleValuesUnsupported) XCTAssertEqual(status?.description, "Multiple values are not supported.") } do { let status = Status(rawValue: errSecNotTrusted) XCTAssertEqual(status, .notTrusted) XCTAssertEqual(status?.description, "The trust policy was not trusted.") } do { let status = Status(rawValue: errSecNoDefaultAuthority) XCTAssertEqual(status, .noDefaultAuthority) XCTAssertEqual(status?.description, "No default authority was detected.") } do { let status = Status(rawValue: errSecRejectedForm) XCTAssertEqual(status, .rejectedForm) XCTAssertEqual(status?.description, "The trust policy had a rejected form.") } do { let status = Status(rawValue: errSecRequestLost) XCTAssertEqual(status, .requestLost) XCTAssertEqual(status?.description, "The request was lost.") } do { let status = Status(rawValue: errSecRequestRejected) XCTAssertEqual(status, .requestRejected) XCTAssertEqual(status?.description, "The request was rejected.") } do { let status = Status(rawValue: errSecUnsupportedAddressType) XCTAssertEqual(status, .unsupportedAddressType) XCTAssertEqual(status?.description, "The address type is not supported.") } do { let status = Status(rawValue: errSecUnsupportedService) XCTAssertEqual(status, .unsupportedService) XCTAssertEqual(status?.description, "The service is not supported.") } do { let status = Status(rawValue: errSecInvalidTupleGroup) XCTAssertEqual(status, .invalidTupleGroup) XCTAssertEqual(status?.description, "The tuple group was not valid.") } do { let status = Status(rawValue: errSecInvalidBaseACLs) XCTAssertEqual(status, .invalidBaseACLs) XCTAssertEqual(status?.description, "The base ACLs are not valid.") } do { let status = Status(rawValue: errSecInvalidTupleCredendtials) XCTAssertEqual(status, .invalidTupleCredendtials) XCTAssertEqual(status?.description, "The tuple credentials are not valid.") } do { let status = Status(rawValue: errSecInvalidEncoding) XCTAssertEqual(status, .invalidEncoding) XCTAssertEqual(status?.description, "The encoding was not valid.") } do { let status = Status(rawValue: errSecInvalidValidityPeriod) XCTAssertEqual(status, .invalidValidityPeriod) XCTAssertEqual(status?.description, "The validity period was not valid.") } do { let status = Status(rawValue: errSecInvalidRequestor) XCTAssertEqual(status, .invalidRequestor) XCTAssertEqual(status?.description, "The requestor was not valid.") } do { let status = Status(rawValue: errSecRequestDescriptor) XCTAssertEqual(status, .requestDescriptor) XCTAssertEqual(status?.description, "The request descriptor was not valid.") } do { let status = Status(rawValue: errSecInvalidBundleInfo) XCTAssertEqual(status, .invalidBundleInfo) XCTAssertEqual(status?.description, "The bundle information was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLIndex) XCTAssertEqual(status, .invalidCRLIndex) XCTAssertEqual(status?.description, "The CRL index was not valid.") } do { let status = Status(rawValue: errSecNoFieldValues) XCTAssertEqual(status, .noFieldValues) XCTAssertEqual(status?.description, "No field values were detected.") } do { let status = Status(rawValue: errSecUnsupportedFieldFormat) XCTAssertEqual(status, .unsupportedFieldFormat) XCTAssertEqual(status?.description, "The field format is not supported.") } do { let status = Status(rawValue: errSecUnsupportedIndexInfo) XCTAssertEqual(status, .unsupportedIndexInfo) XCTAssertEqual(status?.description, "The index information is not supported.") } do { let status = Status(rawValue: errSecUnsupportedLocality) XCTAssertEqual(status, .unsupportedLocality) XCTAssertEqual(status?.description, "The locality is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumAttributes) XCTAssertEqual(status, .unsupportedNumAttributes) XCTAssertEqual(status?.description, "The number of attributes is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumIndexes) XCTAssertEqual(status, .unsupportedNumIndexes) XCTAssertEqual(status?.description, "The number of indexes is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumRecordTypes) XCTAssertEqual(status, .unsupportedNumRecordTypes) XCTAssertEqual(status?.description, "The number of record types is not supported.") } do { let status = Status(rawValue: errSecFieldSpecifiedMultiple) XCTAssertEqual(status, .fieldSpecifiedMultiple) XCTAssertEqual(status?.description, "Too many fields were specified.") } do { let status = Status(rawValue: errSecIncompatibleFieldFormat) XCTAssertEqual(status, .incompatibleFieldFormat) XCTAssertEqual(status?.description, "The field format was incompatible.") } do { let status = Status(rawValue: errSecInvalidParsingModule) XCTAssertEqual(status, .invalidParsingModule) XCTAssertEqual(status?.description, "The parsing module was not valid.") } do { let status = Status(rawValue: errSecDatabaseLocked) XCTAssertEqual(status, .databaseLocked) XCTAssertEqual(status?.description, "The database is locked.") } do { let status = Status(rawValue: errSecDatastoreIsOpen) XCTAssertEqual(status, .datastoreIsOpen) XCTAssertEqual(status?.description, "The data store is open.") } do { let status = Status(rawValue: errSecMissingValue) XCTAssertEqual(status, .missingValue) XCTAssertEqual(status?.description, "A missing value was detected.") } do { let status = Status(rawValue: errSecUnsupportedQueryLimits) XCTAssertEqual(status, .unsupportedQueryLimits) XCTAssertEqual(status?.description, "The query limits are not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumSelectionPreds) XCTAssertEqual(status, .unsupportedNumSelectionPreds) XCTAssertEqual(status?.description, "The number of selection predicates is not supported.") } do { let status = Status(rawValue: errSecUnsupportedOperator) XCTAssertEqual(status, .unsupportedOperator) XCTAssertEqual(status?.description, "The operator is not supported.") } do { let status = Status(rawValue: errSecInvalidDBLocation) XCTAssertEqual(status, .invalidDBLocation) XCTAssertEqual(status?.description, "The database location is not valid.") } do { let status = Status(rawValue: errSecInvalidAccessRequest) XCTAssertEqual(status, .invalidAccessRequest) XCTAssertEqual(status?.description, "The access request is not valid.") } do { let status = Status(rawValue: errSecInvalidIndexInfo) XCTAssertEqual(status, .invalidIndexInfo) XCTAssertEqual(status?.description, "The index information is not valid.") } do { let status = Status(rawValue: errSecInvalidNewOwner) XCTAssertEqual(status, .invalidNewOwner) XCTAssertEqual(status?.description, "The new owner is not valid.") } do { let status = Status(rawValue: errSecInvalidModifyMode) XCTAssertEqual(status, .invalidModifyMode) XCTAssertEqual(status?.description, "The modify mode is not valid.") } do { let status = Status(rawValue: errSecMissingRequiredExtension) XCTAssertEqual(status, .missingRequiredExtension) XCTAssertEqual(status?.description, "A required certificate extension is missing.") } do { let status = Status(rawValue: errSecExtendedKeyUsageNotCritical) XCTAssertEqual(status, .extendedKeyUsageNotCritical) XCTAssertEqual(status?.description, "The extended key usage extension was not marked critical.") } do { let status = Status(rawValue: errSecTimestampMissing) XCTAssertEqual(status, .timestampMissing) XCTAssertEqual(status?.description, "A timestamp was expected but was not found.") } do { let status = Status(rawValue: errSecTimestampInvalid) XCTAssertEqual(status, .timestampInvalid) XCTAssertEqual(status?.description, "The timestamp was not valid.") } do { let status = Status(rawValue: errSecTimestampNotTrusted) XCTAssertEqual(status, .timestampNotTrusted) XCTAssertEqual(status?.description, "The timestamp was not trusted.") } do { let status = Status(rawValue: errSecTimestampServiceNotAvailable) XCTAssertEqual(status, .timestampServiceNotAvailable) XCTAssertEqual(status?.description, "The timestamp service is not available.") } do { let status = Status(rawValue: errSecTimestampBadAlg) XCTAssertEqual(status, .timestampBadAlg) XCTAssertEqual(status?.description, "An unrecognized or unsupported Algorithm Identifier in timestamp.") } do { let status = Status(rawValue: errSecTimestampBadRequest) XCTAssertEqual(status, .timestampBadRequest) XCTAssertEqual(status?.description, "The timestamp transaction is not permitted or supported.") } do { let status = Status(rawValue: errSecTimestampBadDataFormat) XCTAssertEqual(status, .timestampBadDataFormat) XCTAssertEqual(status?.description, "The timestamp data submitted has the wrong format.") } do { let status = Status(rawValue: errSecTimestampTimeNotAvailable) XCTAssertEqual(status, .timestampTimeNotAvailable) XCTAssertEqual(status?.description, "The time source for the Timestamp Authority is not available.") } do { let status = Status(rawValue: errSecTimestampUnacceptedPolicy) XCTAssertEqual(status, .timestampUnacceptedPolicy) XCTAssertEqual(status?.description, "The requested policy is not supported by the Timestamp Authority.") } do { let status = Status(rawValue: errSecTimestampUnacceptedExtension) XCTAssertEqual(status, .timestampUnacceptedExtension) XCTAssertEqual(status?.description, "The requested extension is not supported by the Timestamp Authority.") } do { let status = Status(rawValue: errSecTimestampAddInfoNotAvailable) XCTAssertEqual(status, .timestampAddInfoNotAvailable) XCTAssertEqual(status?.description, "The additional information requested is not available.") } do { let status = Status(rawValue: errSecTimestampSystemFailure) XCTAssertEqual(status, .timestampSystemFailure) XCTAssertEqual(status?.description, "The timestamp request cannot be handled due to system failure.") } do { let status = Status(rawValue: errSecSigningTimeMissing) XCTAssertEqual(status, .signingTimeMissing) XCTAssertEqual(status?.description, "A signing time was expected but was not found.") } do { let status = Status(rawValue: errSecTimestampRejection) XCTAssertEqual(status, .timestampRejection) XCTAssertEqual(status?.description, "A timestamp transaction was rejected.") } do { let status = Status(rawValue: errSecTimestampWaiting) XCTAssertEqual(status, .timestampWaiting) XCTAssertEqual(status?.description, "A timestamp transaction is waiting.") } do { let status = Status(rawValue: errSecTimestampRevocationWarning) XCTAssertEqual(status, .timestampRevocationWarning) XCTAssertEqual(status?.description, "A timestamp authority revocation warning was issued.") } do { let status = Status(rawValue: errSecTimestampRevocationNotification) XCTAssertEqual(status, .timestampRevocationNotification) XCTAssertEqual(status?.description, "A timestamp authority revocation notification was issued.") } #endif } } ================================================ FILE: Lib/KeychainAccessTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: Lib/KeychainAccessTests/KeychainAccessTests.swift ================================================ // // KeychainAccessTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation import XCTest import KeychainAccess class KeychainAccessTests: XCTestCase { override func setUp() { super.setUp() do { try Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").removeAll() } catch {} do { try Keychain(service: "Twitter").removeAll() } catch {} do { try Keychain(server: URL(string: "https://example.com")!, protocolType: .https).removeAll() } catch {} do { try Keychain(server: URL(string: "https://example.com:443")!, protocolType: .https).removeAll() } catch {} do { try Keychain().removeAll() } catch {} } override func tearDown() { super.tearDown() } // MARK: func testGenericPassword() { do { // Add Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") let password = try! keychain.get("password") XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") let password = try! keychain.get("password") XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testGenericPasswordSubscripting() { do { // Add Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = "kishikawa_katsumi" keychain["password"] = "password_1234" let username = keychain["username"] XCTAssertEqual(username, "kishikawa_katsumi") let password = keychain["password"] XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = "katsumi_kishikawa" keychain["password"] = "1234_password" let username = keychain["username"] XCTAssertEqual(username, "katsumi_kishikawa") let password = keychain["password"] XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = nil keychain["password"] = nil XCTAssertNil(keychain["username"]) XCTAssertNil(keychain["password"]) } } func testGenericPasswordWithAccessGroup1() { do { // Add Keychain items // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. // https://developer.apple.com/documentation/security/ksecattraccessgroup let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychainWithAccessGroup.remove("username") } catch {} do { try keychainWithAccessGroup.remove("password") } catch {} XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) XCTAssertNotNil(try! keychain.get("username")) XCTAssertNotNil(try! keychain.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testGenericPasswordWithAccessGroup2() { do { // Add Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} // If the access group is empty, the query will match all access group. So delete all values in other access groups. XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) } } // MARK: func testInternetPassword() { do { // Add Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") let password = try! keychain.get("password") XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") let password = try! keychain.get("password") XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testInternetPasswordSubscripting() { do { // Add Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = "kishikawa_katsumi" keychain["password"] = "password_1234" let username = keychain["username"] XCTAssertEqual(username, "kishikawa_katsumi") let password = keychain["password"] XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = "katsumi_kishikawa" keychain["password"] = "1234_password" let username = keychain["username"] XCTAssertEqual(username, "katsumi_kishikawa") let password = keychain["password"] XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = nil keychain["password"] = nil XCTAssertNil(keychain["username"]) XCTAssertNil(keychain["password"]) } } func testInternetPasswordWithAccessGroup1() { do { // Add Keychain items // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. // https://developer.apple.com/documentation/security/ksecattraccessgroup let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychainWithAccessGroup.remove("username") } catch {} do { try keychainWithAccessGroup.remove("password") } catch {} XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) XCTAssertNotNil(try! keychain.get("username")) XCTAssertNotNil(try! keychain.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testInternetPasswordWithAccessGroup2() { do { // Add Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} // If the access group is empty, the query will match all access group. So delete all values in other access groups. XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) } } // MARK: func testDefaultInitializer() { let keychain = Keychain() XCTAssertEqual(keychain.service, Bundle.main.bundleIdentifier) let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(keychain.service, service) XCTAssertNil(keychain.accessGroup) } func testInitializerWithService() { let keychain = Keychain(service: "com.example.github-token") XCTAssertEqual(keychain.service, "com.example.github-token") XCTAssertNil(keychain.accessGroup) } func testInitializerWithAccessGroup() { let keychain = Keychain(accessGroup: "27AEDK3C9F.shared") let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(keychain.service, service) XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared") } func testInitializerWithServiceAndAccessGroup() { let keychain = Keychain(service: "com.example.github-token", accessGroup: "27AEDK3C9F.shared") XCTAssertEqual(keychain.service, "com.example.github-token") XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared") } func testInitializerWithServer() { let server = "https://kishikawakatsumi.com" let url = URL(string: server)! do { let keychain = Keychain(server: server, protocolType: .https) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.default) } do { let keychain = Keychain(server: url, protocolType: .https) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.default) } } func testInitializerWithServerAndAuthenticationType() { let server = "https://kishikawakatsumi.com" let url = URL(string: server)! do { let keychain = Keychain(server: server, protocolType: .https, authenticationType: .htmlForm) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.htmlForm) } do { let keychain = Keychain(server: url, protocolType: .https, authenticationType: .htmlForm) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.htmlForm) } } // MARK: func testContains() { let keychain = Keychain(service: "Twitter") XCTAssertFalse(try! keychain.contains("username"), "not stored username") XCTAssertFalse(try! keychain.contains("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertTrue(try! keychain.contains("username"), "stored username") XCTAssertFalse(try! keychain.contains("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertTrue(try! keychain.contains("username"), "stored username") XCTAssertTrue(try! keychain.contains("password"), "stored password") } // MARK: func testSetString() { let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") } func testSetStringWithLabel() { let keychain = Keychain(service: "Twitter") .label("Twitter Account") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertNil(label) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertEqual(label, "Twitter Account") } catch { XCTFail("error occurred") } } func testSetStringWithComment() { let keychain = Keychain(service: "Twitter") .comment("Kishikawa Katsumi") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertNil(comment) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertEqual(comment, "Kishikawa Katsumi") } catch { XCTFail("error occurred") } } func testSetStringWithLabelAndComment() { let keychain = Keychain(service: "Twitter") .label("Twitter Account") .comment("Kishikawa Katsumi") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertNil(label) let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertNil(comment) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertEqual(label, "Twitter Account") let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertEqual(comment, "Kishikawa Katsumi") } catch { XCTFail("error occurred") } } func testSetData() { let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.getData("JSONData"), "not stored JSON data") do { try keychain.set(JSONData, key: "JSONData") } catch {} XCTAssertEqual(try! keychain.getData("JSONData"), JSONData, "stored JSON data") } func testStringConversionError() { let keychain = Keychain(service: "Twitter") let length = 256 let data = NSMutableData(length: length)! let bytes = data.mutableBytes.bindMemory(to: UInt8.self, capacity: length) _ = SecRandomCopyBytes(kSecRandomDefault, length, bytes) do { try keychain.set(data as Data, key: "RandomData") let _ = try keychain.getString("RandomData") XCTFail("no error occurred") } catch let error as NSError { XCTAssertEqual(error.domain, KeychainAccessErrorDomain) XCTAssertEqual(error.code, Int(Status.conversionError.rawValue)) XCTAssertEqual(error.userInfo[NSLocalizedDescriptionKey] as! String, Status.conversionError.localizedDescription) } catch { XCTFail("unexpected error occurred") } do { try keychain.set(data as Data, key: "RandomData") let _ = try keychain.getString("RandomData") XCTFail("no error occurred") } catch Status.conversionError { XCTAssertTrue(true) } catch { XCTFail("unexpected error occurred") } } func testGetPersistentRef() { let keychain = Keychain(service: "Twitter") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let persistentRef = try keychain.get("kishikawakatsumi") { $0?.persistentRef } XCTAssertNil(persistentRef) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let persistentRef = try keychain.get("kishikawakatsumi") { $0?.persistentRef } XCTAssertNotNil(persistentRef) } catch { XCTFail("error occurred") } } #if os(iOS) || os(tvOS) func testSetAttributes() { do { var attributes = [String: Any]() attributes[String(kSecAttrDescription)] = "Description Test" attributes[String(kSecAttrComment)] = "Comment Test" attributes[String(kSecAttrCreator)] = "Creator Test" attributes[String(kSecAttrType)] = "Type Test" attributes[String(kSecAttrLabel)] = "Label Test" attributes[String(kSecAttrIsInvisible)] = true attributes[String(kSecAttrIsNegative)] = true let keychain = Keychain(service: "Twitter") .attributes(attributes) XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertNil(attributes) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.genericPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if targetEnvironment(macCatalyst) XCTAssertNotNil(attributes?.accessControl) #else if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 11, minorVersion: 3, patchVersion: 0)) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertEqual(attributes?.service, "Twitter") XCTAssertNil(attributes?.generic) XCTAssertNil(attributes?.securityDomain) XCTAssertNil(attributes?.server) XCTAssertNil(attributes?.`protocol`) XCTAssertNil(attributes?.authenticationType) XCTAssertNil(attributes?.port) XCTAssertNil(attributes?.path) XCTAssertEqual(attributes?[String(kSecClass)] as? String, ItemClass.genericPassword.rawValue) XCTAssertEqual(attributes?[String(kSecValueData)] as? Data, "password1234".data(using: .utf8)) } catch { XCTFail("error occurred") } } do { var attributes = [String: Any]() attributes[String(kSecAttrDescription)] = "Description Test" attributes[String(kSecAttrComment)] = "Comment Test" attributes[String(kSecAttrCreator)] = "Creator Test" attributes[String(kSecAttrType)] = "Type Test" attributes[String(kSecAttrLabel)] = "Label Test" attributes[String(kSecAttrIsInvisible)] = true attributes[String(kSecAttrIsNegative)] = true attributes[String(kSecAttrSecurityDomain)] = "securitydomain" let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) .attributes(attributes) XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertNil(attributes) } catch { XCTFail("error occurred") } do { keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") keychain["kishikawakatsumi"] = "1234password" XCTAssertEqual(keychain["kishikawakatsumi"], "1234password", "updated password") let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "1234password".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) .attributes([String(kSecAttrDescription): "Updated Description"]) XCTAssertEqual(keychain["kishikawakatsumi"], "1234password", "stored password") keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "updated password") let attributes = keychain[attributes: "kishikawakatsumi"] XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Updated Description") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } } } #endif func testRemoveString() { let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") do { try keychain.remove("username") } catch {} XCTAssertNil(try! keychain.get("username"), "removed username") XCTAssertEqual(try! keychain.get("password"), "password1234", "left password") do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username"), "removed username") XCTAssertNil(try! keychain.get("password"), "removed password") } func testRemoveData() { let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.getData("JSONData"), "not stored JSON data") do { try keychain.set(JSONData, key: "JSONData") } catch {} XCTAssertEqual(try! keychain.getData("JSONData"), JSONData, "stored JSON data") do { try keychain.remove("JSONData") } catch {} XCTAssertNil(try! keychain.getData("JSONData"), "removed JSON data") } // MARK: func testSubscripting() { let keychain = Keychain(service: "Twitter") XCTAssertNil(keychain["username"], "not stored username") XCTAssertNil(keychain["password"], "not stored password") XCTAssertNil(keychain[string: "username"], "not stored username") XCTAssertNil(keychain[string: "password"], "not stored password") keychain["username"] = "kishikawakatsumi" XCTAssertEqual(keychain["username"], "kishikawakatsumi", "stored username") XCTAssertEqual(keychain[string: "username"], "kishikawakatsumi", "stored username") keychain["password"] = "password1234" XCTAssertEqual(keychain["password"], "password1234", "stored password") XCTAssertEqual(keychain[string: "password"], "password1234", "stored password") keychain[string: "username"] = nil XCTAssertNil(keychain["username"], "removed username") XCTAssertEqual(keychain["password"], "password1234", "left password") XCTAssertNil(keychain[string: "username"], "removed username") XCTAssertEqual(keychain[string: "password"], "password1234", "left password") keychain[string: "password"] = nil XCTAssertNil(keychain["username"], "removed username") XCTAssertNil(keychain["password"], "removed password") XCTAssertNil(keychain[string: "username"], "removed username") XCTAssertNil(keychain[string: "password"], "removed password") let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) XCTAssertNil(keychain[data:"JSONData"], "not stored JSON data") keychain[data: "JSONData"] = JSONData XCTAssertEqual(keychain[data: "JSONData"], JSONData, "stored JSON data") keychain[data: "JSONData"] = nil XCTAssertNil(keychain[data:"JSONData"], "removed JSON data") } // MARK: func testErrorHandling() { do { let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain(service: "Twitter") try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain() try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { // Add Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("kishikawa_katsumi", key: "username") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { try keychain.set("password_1234", key: "password") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let username = try keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") } catch { XCTFail("error occurred") } do { let password = try keychain.get("password") XCTAssertEqual(password, "password_1234") } catch { XCTFail("error occurred") } } do { // Update Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("katsumi_kishikawa", key: "username") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { try keychain.set("1234_password", key: "password") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let username = try keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") } catch { XCTFail("error occurred") } do { let password = try keychain.get("password") XCTAssertEqual(password, "1234_password") } catch { XCTFail("error occurred") } } do { // Remove Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.remove("username") XCTAssertNil(try! keychain.get("username")) } catch { XCTFail("error occurred") } do { try keychain.remove("password") XCTAssertNil(try! keychain.get("username")) } catch { XCTFail("error occurred") } } } // MARK: func testSetStringWithCustomService() { let username_1 = "kishikawakatsumi" let password_1 = "password1234" let username_2 = "kishikawa_katsumi" let password_2 = "password_1234" let username_3 = "k_katsumi" let password_3 = "12341234" let service_1 = "" let service_2 = "com.kishikawakatsumi.KeychainAccess" let service_3 = "example.com" do { try Keychain().removeAll() } catch {} do { try Keychain(service: service_1).removeAll() } catch {} do { try Keychain(service: service_2).removeAll() } catch {} do { try Keychain(service: service_3).removeAll() } catch {} XCTAssertNil(try! Keychain().get("username"), "not stored username") XCTAssertNil(try! Keychain().get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_1).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain().set(username_1, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_1).set(username_1, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_2).set(username_2, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_3).set(username_3, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "stored username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "stored username") do { try Keychain().set(password_1, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_1).set(password_1, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_2).set(password_2, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_3).set(password_3, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "stored password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "stored password") do { try Keychain().remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "left username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_1).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_2).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_3).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "removed username") do { try Keychain().remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "left password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_1).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_2).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_3).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") } // MARK: func testProperties() { guard #available(macOS 10.10, *) else { return } let keychain = Keychain() XCTAssertEqual(keychain.synchronizable, false) XCTAssertEqual(keychain.synchronizable(true).synchronizable, true) XCTAssertEqual(keychain.synchronizable(false).synchronizable, false) XCTAssertEqual(keychain.accessibility(.afterFirstUnlock).accessibility, Accessibility.afterFirstUnlock) XCTAssertEqual(keychain.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence).accessibility, Accessibility.whenPasscodeSetThisDeviceOnly) XCTAssertEqual(keychain.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence).authenticationPolicy, AuthenticationPolicy.userPresence) XCTAssertNil(keychain.label) XCTAssertEqual(keychain.label("Label").label, "Label") XCTAssertNil(keychain.comment) XCTAssertEqual(keychain.comment("Comment").comment, "Comment") XCTAssertEqual(keychain.authenticationPrompt("Prompt").authenticationPrompt, "Prompt") } // MARK: func testAllKeys() { do { let keychain = Keychain() keychain["key1"] = "value1" keychain["key2"] = "value2" keychain["key3"] = "value3" let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 3) XCTAssertEqual(allKeys.sorted(), ["key1", "key2", "key3"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 3) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(macOS) let service: String let accessGroup: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[0]["service"] as? String, service) XCTAssertEqual(sortedItems[0]["value"] as? String, "value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlock") XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[1]["service"] as? String, service) XCTAssertEqual(sortedItems[1]["value"] as? String, "value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock") XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[2]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[2]["service"] as? String, service) XCTAssertEqual(sortedItems[2]["value"] as? String, "value3") XCTAssertEqual(sortedItems[2]["key"] as? String, "key3") XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[2]["accessibility"] as? String, "AfterFirstUnlock") #else XCTAssertEqual(sortedItems[0]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[0]["key"] as? String, "key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[1]["key"] as? String, "key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[2]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[2]["key"] as? String, "key3") XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword") #endif } do { let keychain = Keychain(service: "service1") try! keychain .synchronizable(true) .accessibility(.whenUnlockedThisDeviceOnly) .set("service1_value1", key: "service1_key1") try! keychain .synchronizable(false) .accessibility(.afterFirstUnlockThisDeviceOnly) .set("service1_value2", key: "service1_key2") let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 2) XCTAssertEqual(allKeys.sorted(), ["service1_key1", "service1_key2"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 2) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(macOS) let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "true") XCTAssertEqual(sortedItems[0]["service"] as? String, "service1") XCTAssertEqual(sortedItems[0]["value"] as? String, "service1_value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "WhenUnlockedThisDeviceOnly") XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[1]["service"] as? String, "service1") XCTAssertEqual(sortedItems[1]["value"] as? String, "service1_value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly") #else XCTAssertEqual(sortedItems[0]["service"] as? String, "service1") XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["service"] as? String, "service1") XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") #endif } do { let keychain = Keychain(server: "https://google.com", protocolType: .https) #if !targetEnvironment(macCatalyst) try! keychain .synchronizable(false) .accessibility(.alwaysThisDeviceOnly) .set("google.com_value1", key: "google.com_key1") #else try! keychain .synchronizable(false) .accessibility(.afterFirstUnlockThisDeviceOnly) .set("google.com_value1", key: "google.com_key1") #endif #if !targetEnvironment(macCatalyst) try! keychain .synchronizable(true) .accessibility(.always) .set("google.com_value2", key: "google.com_key2") #else try! keychain .synchronizable(true) .accessibility(.afterFirstUnlock) .set("google.com_value2", key: "google.com_key2") #endif let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 2) XCTAssertEqual(allKeys.sorted(), ["google.com_key1", "google.com_key2"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 2) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(macOS) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[0]["value"] as? String, "google.com_value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1") XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS") #if targetEnvironment(macCatalyst) XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly") #else XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AlwaysThisDeviceOnly") #endif XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "true") XCTAssertEqual(sortedItems[1]["value"] as? String, "google.com_value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2") XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS") #if targetEnvironment(macCatalyst) XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock") #else XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "Always") #endif #else XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1") XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS") XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2") XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS") #endif } #if !os(macOS) do { let allKeys = Keychain.allKeys(.genericPassword) XCTAssertEqual(allKeys.count, 5) let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame } let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedKeys[0].0, service) XCTAssertEqual(sortedKeys[0].1, "key1") XCTAssertEqual(sortedKeys[1].0, service) XCTAssertEqual(sortedKeys[1].1, "key2") XCTAssertEqual(sortedKeys[2].0, service) XCTAssertEqual(sortedKeys[2].1, "key3") XCTAssertEqual(sortedKeys[3].0, "service1") XCTAssertEqual(sortedKeys[3].1, "service1_key1") XCTAssertEqual(sortedKeys[4].0, "service1") XCTAssertEqual(sortedKeys[4].1, "service1_key2") } do { let allKeys = Keychain.allKeys(.internetPassword) XCTAssertEqual(allKeys.count, 2) let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame } XCTAssertEqual(sortedKeys[0].0, "google.com") XCTAssertEqual(sortedKeys[0].1, "google.com_key1") XCTAssertEqual(sortedKeys[1].0, "google.com") XCTAssertEqual(sortedKeys[1].1, "google.com_key2") } #endif } func testDescription() { do { let keychain = Keychain() XCTAssertEqual(keychain.description, "[]") XCTAssertEqual(keychain.debugDescription, "[]") } } // MARK: func testAuthenticationPolicy() { guard #available(iOS 9.0, macOS 10.11, *) else { return } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } #if os(iOS) do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .or, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .and, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } #endif #if os(macOS) do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } #endif } func testIgnoringAttributeSynchronizable() { let keychain = Keychain(service: "Twitter").synchronizable(false) let keychainSynchronizable = Keychain(service: "Twitter").synchronizable(true) XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username", ignoringAttributeSynchronizable: false) } catch {} do { try keychainSynchronizable.set("kishikawakatsumi_synchronizable", key: "username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertEqual(try! keychain.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") do { try keychain.set("password1234", key: "password", ignoringAttributeSynchronizable: false) } catch {} do { try keychainSynchronizable.set("password1234_synchronizable", key: "password", ignoringAttributeSynchronizable: false) } catch {} XCTAssertEqual(try! keychain.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychain.get("password", ignoringAttributeSynchronizable: false), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "password1234_synchronizable", "stored password") do { try keychain.remove("username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") do { try keychainSynchronizable.remove("username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertEqual(try! keychain.get("password", ignoringAttributeSynchronizable: false), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "password1234_synchronizable", "stored password") do { try keychain.removeAll() } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") } func testIgnoringAttributeSynchronizableBackwardCompatibility() { let keychain = Keychain(service: "Twitter").synchronizable(false) let keychainSynchronizable = Keychain(service: "Twitter").synchronizable(true) XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi", "stored username") do { try keychainSynchronizable.set("kishikawakatsumi_synchronizable", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234", "stored password") do { try keychainSynchronizable.set("password1234_synchronizable", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychain.get("password"), "password1234_synchronizable", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234_synchronizable", "stored password") do { try keychain.remove("username") } catch {} XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertEqual(try! keychain.get("password"), "password1234_synchronizable", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234_synchronizable", "stored password") do { try keychain.removeAll() } catch {} XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") } } ================================================ FILE: Lib/KeychainAccessTests/SharedCredentialTests.swift ================================================ // // SharedCredentialTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class SharedCredentialTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testGetSharedPassword() { do { let expectation = self.expectation(description: "getSharedPassword") let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https) keychain.getSharedPassword("kishikawakatsumi") { (password, error) -> () in XCTAssertNil(password) XCTAssertNotNil(error) expectation.fulfill() } waitForExpectations(timeout: 10.0, handler: nil) } do { let expectation = self.expectation(description: "getSharedPassword") let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https) keychain.getSharedPassword { (account, password, error) -> () in XCTAssertNil(account) XCTAssertNil(password) XCTAssertNotNil(error) expectation.fulfill() } waitForExpectations(timeout: 10.0, handler: nil) } } func testGeneratePassword() { do { var passwords = Set() for _ in 0...100_000 { let password = Keychain.generatePassword() #if swift(>=4.2) XCTAssertEqual(password.count, "xxx-xxx-xxx-xxx".count) #else XCTAssertEqual(password.characters.count, "xxx-xxx-xxx-xxx".characters.count) #endif XCTAssertFalse(passwords.contains(password)) passwords.insert(password) } } } } ================================================ FILE: Lib/TestHost/AppDelegate.swift ================================================ // // AppDelegate.swift // TestHost // // Created by kishikawa katsumi on 7/10/16. // Copyright © 2016 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #if os(macOS) import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var window: NSWindow! func applicationDidFinishLaunching(aNotification: NSNotification) {} } #else import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? #if swift(>=4.2) func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { return true } #else func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { return true } #endif } #endif ================================================ FILE: Lib/TestHost/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Lib/TestHost/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIRequiresFullScreen NSPrincipalClass $(PRINCIPAL_CLASS) ================================================ FILE: Lib/TestHost/TestHost.entitlements ================================================ keychain-access-groups $(AppIdentifierPrefix)com.kishikawakatsumi.KeychainAccess.TestHost $(AppIdentifierPrefix)shared ================================================ FILE: Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/AppDelegate.swift ================================================ // // AppDelegate.swift // TestHost-MacCatalyst // // Created by Kishikawa Katsumi on 2019/10/21. // Copyright © 2019 Kishikawa Katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } } ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UISceneConfigurations UIWindowSceneSessionRoleApplication UISceneConfigurationName Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate UISceneStoryboardFile Main UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/SceneDelegate.swift ================================================ // // SceneDelegate.swift // TestHost-MacCatalyst // // Created by Kishikawa Katsumi on 2019/10/21. // Copyright © 2019 Kishikawa Katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? } ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements ================================================ com.apple.security.app-sandbox com.apple.security.network.client keychain-access-groups $(AppIdentifierPrefix)com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst $(AppIdentifierPrefix)shared ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/ViewController.swift ================================================ // // ViewController.swift // TestHost-MacCatalyst // // Created by Kishikawa Katsumi on 2019/10/21. // Copyright © 2019 Kishikawa Katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } } ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 14A98C4F235D284F00BBB893 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C4E235D284F00BBB893 /* AppDelegate.swift */; }; 14A98C51235D284F00BBB893 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C50235D284F00BBB893 /* SceneDelegate.swift */; }; 14A98C53235D284F00BBB893 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C52235D284F00BBB893 /* ViewController.swift */; }; 14A98C56235D284F00BBB893 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C54235D284F00BBB893 /* Main.storyboard */; }; 14A98C58235D285000BBB893 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C57235D285000BBB893 /* Assets.xcassets */; }; 14A98C5B235D285000BBB893 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */; }; 14A98C65235D286B00BBB893 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14A98C64235D286B00BBB893 /* KeychainAccess.framework */; }; 14A98C66235D286B00BBB893 /* KeychainAccess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 14A98C64235D286B00BBB893 /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14A98C88235D2AFA00BBB893 /* ErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */; }; 14A98C8A235D2AFA00BBB893 /* KeychainAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */; }; 14A98C8B235D2AFA00BBB893 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C87235D2AFA00BBB893 /* EnumTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 14A98C82235D293300BBB893 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14A98C43235D284F00BBB893 /* Project object */; proxyType = 1; remoteGlobalIDString = 14A98C4A235D284F00BBB893; remoteInfo = "TestHost-MacCatalyst"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 14A98C67235D286B00BBB893 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 14A98C66235D286B00BBB893 /* KeychainAccess.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TestHost-MacCatalyst.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 14A98C4E235D284F00BBB893 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 14A98C50235D284F00BBB893 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 14A98C52235D284F00BBB893 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 14A98C55235D284F00BBB893 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 14A98C57235D285000BBB893 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 14A98C5A235D285000BBB893 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 14A98C5C235D285000BBB893 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14A98C62235D286500BBB893 /* TestHost-MacCatalyst.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "TestHost-MacCatalyst.entitlements"; sourceTree = ""; }; 14A98C64235D286B00BBB893 /* KeychainAccess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeychainAccess.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KeychainAccessTests-MacCatalyst.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 14A98C7E235D292000BBB893 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTypeTests.swift; sourceTree = ""; }; 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainAccessTests.swift; sourceTree = ""; }; 14A98C87235D2AFA00BBB893 /* EnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 14A98C48235D284F00BBB893 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14A98C65235D286B00BBB893 /* KeychainAccess.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A98C77235D292000BBB893 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 14A98C42235D284F00BBB893 = { isa = PBXGroup; children = ( 14A98C4D235D284F00BBB893 /* TestHost-MacCatalyst */, 14A98C7B235D292000BBB893 /* KeychainAccessTests-MacCatalyst */, 14A98C4C235D284F00BBB893 /* Products */, 14A98C63235D286B00BBB893 /* Frameworks */, ); sourceTree = ""; }; 14A98C4C235D284F00BBB893 /* Products */ = { isa = PBXGroup; children = ( 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */, 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */, ); name = Products; sourceTree = ""; }; 14A98C4D235D284F00BBB893 /* TestHost-MacCatalyst */ = { isa = PBXGroup; children = ( 14A98C4E235D284F00BBB893 /* AppDelegate.swift */, 14A98C50235D284F00BBB893 /* SceneDelegate.swift */, 14A98C52235D284F00BBB893 /* ViewController.swift */, 14A98C54235D284F00BBB893 /* Main.storyboard */, 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */, 14A98C57235D285000BBB893 /* Assets.xcassets */, 14A98C5C235D285000BBB893 /* Info.plist */, 14A98C62235D286500BBB893 /* TestHost-MacCatalyst.entitlements */, ); path = "TestHost-MacCatalyst"; sourceTree = ""; }; 14A98C63235D286B00BBB893 /* Frameworks */ = { isa = PBXGroup; children = ( 14A98C64235D286B00BBB893 /* KeychainAccess.framework */, ); name = Frameworks; sourceTree = ""; }; 14A98C7B235D292000BBB893 /* KeychainAccessTests-MacCatalyst */ = { isa = PBXGroup; children = ( 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */, 14A98C87235D2AFA00BBB893 /* EnumTests.swift */, 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */, 14A98C7E235D292000BBB893 /* Info.plist */, ); path = "KeychainAccessTests-MacCatalyst"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */ = { isa = PBXNativeTarget; buildConfigurationList = 14A98C5F235D285000BBB893 /* Build configuration list for PBXNativeTarget "TestHost-MacCatalyst" */; buildPhases = ( 14A98C47235D284F00BBB893 /* Sources */, 14A98C48235D284F00BBB893 /* Frameworks */, 14A98C49235D284F00BBB893 /* Resources */, 14A98C67235D286B00BBB893 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "TestHost-MacCatalyst"; productName = "TestHost-MacCatalyst"; productReference = 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */; productType = "com.apple.product-type.application"; }; 14A98C79235D292000BBB893 /* KeychainAccessTests-MacCatalyst */ = { isa = PBXNativeTarget; buildConfigurationList = 14A98C7F235D292000BBB893 /* Build configuration list for PBXNativeTarget "KeychainAccessTests-MacCatalyst" */; buildPhases = ( 14A98C76235D292000BBB893 /* Sources */, 14A98C77235D292000BBB893 /* Frameworks */, 14A98C78235D292000BBB893 /* Resources */, ); buildRules = ( ); dependencies = ( 14A98C83235D293300BBB893 /* PBXTargetDependency */, ); name = "KeychainAccessTests-MacCatalyst"; productName = "KeychainAccessTests-MacCatalyst"; productReference = 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 14A98C43235D284F00BBB893 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1100; LastUpgradeCheck = 1200; ORGANIZATIONNAME = "Kishikawa Katsumi"; TargetAttributes = { 14A98C4A235D284F00BBB893 = { CreatedOnToolsVersion = 11.0; }; 14A98C79235D292000BBB893 = { CreatedOnToolsVersion = 11.0; LastSwiftMigration = 1100; TestTargetID = 14A98C4A235D284F00BBB893; }; }; }; buildConfigurationList = 14A98C46235D284F00BBB893 /* Build configuration list for PBXProject "TestHost-MacCatalyst" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 14A98C42235D284F00BBB893; productRefGroup = 14A98C4C235D284F00BBB893 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */, 14A98C79235D292000BBB893 /* KeychainAccessTests-MacCatalyst */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 14A98C49235D284F00BBB893 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A98C5B235D285000BBB893 /* LaunchScreen.storyboard in Resources */, 14A98C58235D285000BBB893 /* Assets.xcassets in Resources */, 14A98C56235D284F00BBB893 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A98C78235D292000BBB893 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 14A98C47235D284F00BBB893 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A98C53235D284F00BBB893 /* ViewController.swift in Sources */, 14A98C4F235D284F00BBB893 /* AppDelegate.swift in Sources */, 14A98C51235D284F00BBB893 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A98C76235D292000BBB893 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A98C88235D2AFA00BBB893 /* ErrorTypeTests.swift in Sources */, 14A98C8B235D2AFA00BBB893 /* EnumTests.swift in Sources */, 14A98C8A235D2AFA00BBB893 /* KeychainAccessTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 14A98C83235D293300BBB893 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */; targetProxy = 14A98C82235D293300BBB893 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 14A98C54235D284F00BBB893 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 14A98C55235D284F00BBB893 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 14A98C5A235D285000BBB893 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 14A98C5D235D285000BBB893 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 14A98C5E235D285000BBB893 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 14A98C60235D285000BBB893 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements"; CODE_SIGN_STYLE = Automatic; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "TestHost-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 14A98C61235D285000BBB893 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements"; CODE_SIGN_STYLE = Automatic; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "TestHost-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 14A98C80235D292000BBB893 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "KeychainAccessTests-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccessTests-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHost-MacCatalyst.app/TestHost-MacCatalyst"; }; name = Debug; }; 14A98C81235D292000BBB893 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "KeychainAccessTests-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccessTests-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHost-MacCatalyst.app/TestHost-MacCatalyst"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 14A98C46235D284F00BBB893 /* Build configuration list for PBXProject "TestHost-MacCatalyst" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A98C5D235D285000BBB893 /* Debug */, 14A98C5E235D285000BBB893 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14A98C5F235D285000BBB893 /* Build configuration list for PBXNativeTarget "TestHost-MacCatalyst" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A98C60235D285000BBB893 /* Debug */, 14A98C61235D285000BBB893 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14A98C7F235D292000BBB893 /* Build configuration list for PBXNativeTarget "KeychainAccessTests-MacCatalyst" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A98C80235D292000BBB893 /* Debug */, 14A98C81235D292000BBB893 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 14A98C43235D284F00BBB893 /* Project object */; } ================================================ FILE: Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/xcshareddata/xcschemes/TestHost-MacCatalyst.xcscheme ================================================ ================================================ FILE: Package.swift ================================================ // swift-tools-version:5.0 // Package.swift // KeychainAccess // // Created by kishikawa katsumi on 2015/12/4. // Copyright (c) 2015 kishikawa katsumi. All rights reserved. // import PackageDescription let package = Package( name: "KeychainAccess", platforms: [ .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2) ], products: [ .library(name: "KeychainAccess", targets: ["KeychainAccess"]) ], targets: [ .target( name: "KeychainAccess", path: "Lib/KeychainAccess", linkerSettings: [.unsafeFlags(["-Xlinker", "-no_application_extension"])]) ] ) ================================================ FILE: Package@swift-5.3.swift ================================================ // swift-tools-version:5.3 // Package.swift // KeychainAccess // // Created by kishikawa katsumi on 2015/12/4. // Copyright (c) 2015 kishikawa katsumi. All rights reserved. // import PackageDescription let package = Package( name: "KeychainAccess", platforms: [ .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) ], products: [ .library(name: "KeychainAccess", targets: ["KeychainAccess"]) ], targets: [ .target( name: "KeychainAccess", path: "Lib/KeychainAccess", exclude: ["Info.plist"], linkerSettings: [.unsafeFlags(["-Xlinker", "-no_application_extension"])] ) ] ) ================================================ FILE: README.md ================================================ # KeychainAccess [![Build Status](https://travis-ci.com/kishikawakatsumi/KeychainAccess.svg?branch=master)](https://travis-ci.com/kishikawakatsumi/KeychainAccess) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![SPM supported](https://img.shields.io/badge/SPM-supported-DE5C43.svg?style=flat)](https://swift.org/package-manager) [![Version](https://img.shields.io/cocoapods/v/KeychainAccess.svg)](http://cocoadocs.org/docsets/KeychainAccess) [![Platform](https://img.shields.io/cocoapods/p/KeychainAccess.svg)](http://cocoadocs.org/docsets/KeychainAccess) KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and macOS. Makes using Keychain APIs extremely easy and much more palatable to use in Swift. ## :bulb: Features - Simple interface - Support access group - [Support accessibility](#accessibility) - [Support iCloud sharing](#icloud_sharing) - **[Support TouchID and Keychain integration (iOS 8+)](#touch_id_integration)** - **[Support Shared Web Credentials (iOS 8+)](#shared_web_credentials)** - [Works on both iOS & macOS](#requirements) - [watchOS and tvOS are supported](#requirements) - **[Mac Catalyst is supported](#requirements)** - **[Swift 3, 4 and 5 compatible](#requirements)** ## :book: Usage ##### :eyes: See also: - [:link: iOS Example Project](https://github.com/kishikawakatsumi/KeychainAccess/tree/master/Examples/Example-iOS) ### :key: Basics #### Saving Application Password ```swift let keychain = Keychain(service: "com.example.github-token") keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` #### Saving Internet Password ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ### :key: Instantiation #### Create Keychain for Application Password ```swift let keychain = Keychain(service: "com.example.github-token") ``` ```swift let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared") ``` #### Create Keychain for Internet Password ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) ``` ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https, authenticationType: .htmlForm) ``` ### :key: Adding an item #### subscripting ##### for String ```swift keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ```swift keychain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ##### for NSData ```swift keychain[data: "secret"] = NSData(contentsOfFile: "secret.bin") ``` #### set method ```swift keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") ``` #### error handling ```swift do { try keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print(error) } ``` ### :key: Obtaining an item #### subscripting ##### for String (If the value is NSData, attempt to convert to String) ```swift let token = keychain["kishikawakatsumi"] ``` ```swift let token = keychain[string: "kishikawakatsumi"] ``` ##### for NSData ```swift let secretData = keychain[data: "secret"] ``` #### get methods ##### as String ```swift let token = try? keychain.get("kishikawakatsumi") ``` ```swift let token = try? keychain.getString("kishikawakatsumi") ``` ##### as NSData ```swift let data = try? keychain.getData("kishikawakatsumi") ``` ### :key: Removing an item #### subscripting ```swift keychain["kishikawakatsumi"] = nil ``` #### remove method ```swift do { try keychain.remove("kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ### :key: Set Label and Comment ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) do { try keychain .label("github.com (kishikawakatsumi)") .comment("github access token") .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ### :key: Obtaining Other Attributes #### PersistentRef ```swift let keychain = Keychain() let persistentRef = keychain[attributes: "kishikawakatsumi"]?.persistentRef ... ``` #### Creation Date ```swift let keychain = Keychain() let creationDate = keychain[attributes: "kishikawakatsumi"]?.creationDate ... ``` #### All Attributes ```swift let keychain = Keychain() do { let attributes = try keychain.get("kishikawakatsumi") { $0 } print(attributes?.comment) print(attributes?.label) print(attributes?.creator) ... } catch let error { print("error: \(error)") } ``` ##### subscripting ```swift let keychain = Keychain() if let attributes = keychain[attributes: "kishikawakatsumi"] { print(attributes.comment) print(attributes.label) print(attributes.creator) } ``` ### :key: Configuration (Accessibility, Sharing, iCloud Sync) **Provides fluent interfaces** ```swift let keychain = Keychain(service: "com.example.github-token") .label("github.com (kishikawakatsumi)") .synchronizable(true) .accessibility(.afterFirstUnlock) ``` #### Accessibility ##### Default accessibility matches background application (=kSecAttrAccessibleAfterFirstUnlock) ```swift let keychain = Keychain(service: "com.example.github-token") ``` ##### For background application ###### Creating instance ```swift let keychain = Keychain(service: "com.example.github-token") .accessibility(.afterFirstUnlock) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ###### One-shot ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain .accessibility(.afterFirstUnlock) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ##### For foreground application ###### Creating instance ```swift let keychain = Keychain(service: "com.example.github-token") .accessibility(.whenUnlocked) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ###### One-shot ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain .accessibility(.whenUnlocked) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` #### :couple: Sharing Keychain items ```swift let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared") ``` #### :arrows_counterclockwise: Synchronizing Keychain items with iCloud ###### Creating instance ```swift let keychain = Keychain(service: "com.example.github-token") .synchronizable(true) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ###### One-shot ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain .synchronizable(true) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ### :cyclone: Touch ID (Face ID) integration **Any Operation that require authentication must be run in the background thread.** **If you run in the main thread, UI thread will lock for the system to try to display the authentication dialog.** **To use Face ID, add `NSFaceIDUsageDescription` key to your `Info.plist`** #### :closed_lock_with_key: Adding a Touch ID (Face ID) protected item If you want to store the Touch ID protected Keychain item, specify `accessibility` and `authenticationPolicy` attributes. ```swift let keychain = Keychain(service: "com.example.github-token") DispatchQueue.global().async { do { // Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked` try keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny]) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { // Error handling if needed... } } ``` #### :closed_lock_with_key: Updating a Touch ID (Face ID) protected item The same way as when adding. **Do not run in the main thread if there is a possibility that the item you are trying to add already exists, and protected.** **Because updating protected items requires authentication.** Additionally, you want to show custom authentication prompt message when updating, specify an `authenticationPrompt` attribute. If the item not protected, the `authenticationPrompt` parameter just be ignored. ```swift let keychain = Keychain(service: "com.example.github-token") DispatchQueue.global().async { do { // Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked` try keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny]) .authenticationPrompt("Authenticate to update your access token") .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { // Error handling if needed... } } ``` #### :closed_lock_with_key: Obtaining a Touch ID (Face ID) protected item The same way as when you get a normal item. It will be displayed automatically Touch ID or passcode authentication If the item you try to get is protected. If you want to show custom authentication prompt message, specify an `authenticationPrompt` attribute. If the item not protected, the `authenticationPrompt` parameter just be ignored. ```swift let keychain = Keychain(service: "com.example.github-token") DispatchQueue.global().async { do { let password = try keychain .authenticationPrompt("Authenticate to login to server") .get("kishikawakatsumi") print("password: \(password)") } catch let error { // Error handling if needed... } } ``` #### :closed_lock_with_key: Removing a Touch ID (Face ID) protected item The same way as when you remove a normal item. There is no way to show Touch ID or passcode authentication when removing Keychain items. ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain.remove("kishikawakatsumi") } catch let error { // Error handling if needed... } ``` ### :key: Shared Web Credentials > Shared web credentials is a programming interface that enables native iOS apps to share credentials with their website counterparts. For example, a user may log in to a website in Safari, entering a user name and password, and save those credentials using the iCloud Keychain. Later, the user may run a native app from the same developer, and instead of the app requiring the user to reenter a user name and password, shared web credentials gives it access to the credentials that were entered earlier in Safari. The user can also create new accounts, update passwords, or delete her account from within the app. These changes are then saved and used by Safari. > ```swift let keychain = Keychain(server: "https://www.kishikawakatsumi.com", protocolType: .HTTPS) let username = "kishikawakatsumi@mac.com" // First, check the credential in the app's Keychain if let password = try? keychain.get(username) { // If found password in the Keychain, // then log into the server } else { // If not found password in the Keychain, // try to read from Shared Web Credentials keychain.getSharedPassword(username) { (password, error) -> () in if password != nil { // If found password in the Shared Web Credentials, // then log into the server // and save the password to the Keychain keychain[username] = password } else { // If not found password either in the Keychain also Shared Web Credentials, // prompt for username and password // Log into server // If the login is successful, // save the credentials to both the Keychain and the Shared Web Credentials. keychain[username] = inputPassword keychain.setSharedPassword(inputPassword, account: username) } } } ``` #### Request all associated domain's credentials ```swift Keychain.requestSharedWebCredential { (credentials, error) -> () in } ``` #### Generate strong random password Generate strong random password that is in the same format used by Safari autofill (xxx-xxx-xxx-xxx). ```swift let password = Keychain.generatePassword() // => Nhu-GKm-s3n-pMx ``` #### How to set up Shared Web Credentials > 1. Add a com.apple.developer.associated-domains entitlement to your app. This entitlement must include all the domains with which you want to share credentials. > > 2. Add an apple-app-site-association file to your website. This file must include application identifiers for all the apps with which the site wants to share credentials, and it must be properly signed. > > 3. When the app is installed, the system downloads and verifies the site association file for each of its associated domains. If the verification is successful, the app is associated with the domain. **More details:** ### :mag: Debugging #### Display all stored items if print keychain object ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) print("\(keychain)") ``` ``` => [ [authenticationType: default, key: kishikawakatsumi, server: github.com, class: internetPassword, protocol: https] [authenticationType: default, key: hirohamada, server: github.com, class: internetPassword, protocol: https] [authenticationType: default, key: honeylemon, server: github.com, class: internetPassword, protocol: https] ] ``` #### Obtaining all stored keys ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) let keys = keychain.allKeys() for key in keys { print("key: \(key)") } ``` ``` => key: kishikawakatsumi key: hirohamada key: honeylemon ``` #### Obtaining all stored items ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) let items = keychain.allItems() for item in items { print("item: \(item)") } ``` ``` => item: [authenticationType: Default, key: kishikawakatsumi, server: github.com, class: InternetPassword, protocol: https] item: [authenticationType: Default, key: hirohamada, server: github.com, class: InternetPassword, protocol: https] item: [authenticationType: Default, key: honeylemon, server: github.com, class: InternetPassword, protocol: https] ``` ## Keychain sharing capability If you encounter the error below, you need to add an `Keychain.entitlements`. ``` OSStatus error:[-34018] Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements. ``` Screen Shot 2019-10-27 at 8 08 50 ## Requirements | | OS | Swift | | ---------- | ---------------------------------------------------------- | ------------------ | | **v1.1.x** | iOS 7+, macOS 10.9+ | 1.1 | | **v1.2.x** | iOS 7+, macOS 10.9+ | 1.2 | | **v2.0.x** | iOS 7+, macOS 10.9+, watchOS 2+ | 2.0 | | **v2.1.x** | iOS 7+, macOS 10.9+, watchOS 2+ | 2.0 | | **v2.2.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1 | | **v2.3.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1, 2.2 | | **v2.4.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.2, 2.3 | | **v3.0.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 3.x | | **v3.1.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2 | | **v3.2.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2, 5.0 | | **v4.0.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2, 5.1 | | **v4.1.x** | iOS 8+, macOS 10.9+, watchOS 3+, tvOS 9+, Mac Catalyst 13+ | 4.0, 4.1, 4.2, 5.1 | ## Installation ### CocoaPods KeychainAccess is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following lines to your Podfile: ```ruby use_frameworks! pod 'KeychainAccess' ``` ### Carthage KeychainAccess is available through [Carthage](https://github.com/Carthage/Carthage). To install it, simply add the following line to your Cartfile: `github "kishikawakatsumi/KeychainAccess"` ### Swift Package Manager KeychainAccess is also available through [Swift Package Manager](https://github.com/apple/swift-package-manager/). #### Xcode Select `File > Add Packages... > Add Package Dependency...`, #### CLI First, create `Package.swift` that its package declaration includes: ```swift // swift-tools-version:5.0 import PackageDescription let package = Package( name: "MyLibrary", products: [ .library(name: "MyLibrary", targets: ["MyLibrary"]), ], dependencies: [ .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "3.0.0"), ], targets: [ .target(name: "MyLibrary", dependencies: ["KeychainAccess"]), ] ) ``` Then, type ```shell $ swift build ``` ### To manually add to your project 1. Add `Lib/KeychainAccess.xcodeproj` to your project 2. Link `KeychainAccess.framework` with your target 3. Add `Copy Files Build Phase` to include the framework to your application bundle _See [iOS Example Project](https://github.com/kishikawakatsumi/KeychainAccess/tree/master/Examples/Example-iOS) as reference._ ## Author kishikawa katsumi, kishikawakatsumi@mac.com ## License KeychainAccess is available under the MIT license. See the LICENSE file for more info.