Valet

repository·main·Indexed 26 days ago

https://github.com/square/valet

A library providing a simplified interface for securely storing String and Data objects in the iOS, tvOS, watchOS, or macOS Keychain. It supports features such as iCloud syncing, Keychain Sharing, App Groups, and hardware-backed protection via SecureEnclaveValet and SinglePromptSecureEnclaveValet for Face ID, Touch ID, or passcode authentication.

Tokens
2.2K
Snippets
9
Records
17
Agent score
38%

What's inside Valet

  1. Integrate Valet into a macOS application

    main

    macOS applications must have the Keychain Sharing entitlement enabled to use Valet.

    If your app supports macOS 10.14 or earlier, you must call myValet.migrateObjectsFromPreCatalina() before reading values to upgrade items to include the kSecUseDataProtectionKeychain:true attribute required by macOS Catalina.

  2. Install Valet via Swift Package Manager

    main

    Add Valet as a dependency in your Package.swift file to use it in your Swift projects.

    dependencies: [
        .package(url: "https://github.com/Square/Valet", from: "5.0.0"),
    ],
  3. Migrate data between Valet instances with different accessibility

    main

    If you change a Valet's Accessibility value after persisting data, you must migrate the existing key:value pairs to a new Valet instance with the new accessibility to avoid data loss.

    let myOldValet = Valet.valet(withExplicitlySet: Identifier(nonEmpty: "Druidia")!, accessibility: .whenUnlocked)
    let myNewValet = Valet.valet(withExplicitlySet: Identifier(nonEmpty: "Druidia")!, accessibility: .afterFirstUnlock)
    try? myNewValet.migrateObjects(from: myOldValet, removeOnCompletion: true)
  4. Migrate from Valet 2 to Valet 3/4/5

    main

    Upgrading from Valet 2 involves several breaking changes:

    • Initializers: Both Swift and Objective-C now use class methods instead of instance initializers.
    • iCloud Syncing: VALSynchronizableValet is replaced by Valet.iCloudValet(with:accessibility:) (Swift) or +[VALValet iCloudValetWithIdentifier:accessibility:] (Objective-C).
    • Access Control: VALAccessControl is renamed to SecureEnclaveAccessControl (VALSecureEnclaveAccessControl in Objective-C). The TouchID case is replaced by biometric to support Face ID.
    • Inheritance: Valet, SecureEnclaveValet, and SinglePromptSecureEnclaveValet no longer share a common inheritance tree; they all inherit directly from NSObject and use composition.
  5. Migrate from Valet 4 to Valet 5

    main

    Upgrading from Valet 4 involves the following changes:

    • Typed Throws: Most throwing methods now utilize Swift's typed throws, which may require updating catch statements.
    • tvOS/watchOS Prompting: SecureEnclaveValet's withPrompt API has been removed on tvOS and watchOS. Use the new APIs provided for these platforms that perform the same actions without a custom prompt.
    • watchOS Single Prompt Migration: SinglePromptSecureEnclaveValet was removed from watchOS. To migrate existing key/value pairs, use migrateObjectsFromSinglePromptSecureEnclaveValet(removeOnCompletion:) on a SecureEnclaveValet instance with the same identifiers and access control.
  6. Migrate from Valet 3 to Valet 4/5

    main

    Upgrading from Valet 3 involves several breaking changes:

    • Deprecated Accessibility: The always and alwaysThisDeviceOnly accessibility values have been removed. To migrate data, use the following methods on a new Valet instance with your preferred accessibility:
      • For always: migrateObjectsFromAlwaysAccessibleValet(removeOnCompletion:)
      • For alwaysThisDeviceOnly: migrateObjectsFromAlwaysAccessibleThisDeviceOnlyValet(removeOnCompletion:)
    • Error Handling: Most APIs that previously returned optionals or Bool now throw errors.
      • Swift: Use try? to maintain previous behavior or do-catch to handle specific errors.
      • Objective-C: Pass an NSError pointer to the method calls.
    • Shared Access Groups: When creating a Valet for shared access groups, you must now explicitly pass the App ID prefix using SharedGroupIdentifier(appIDPrefix:nonEmptyGroup:).
  7. Troubleshoot Valet keychain access issues

    main

    If canAccessKeychain() returns false or operations fail, check the following:

    • Accessibility Mismatch: Using .whenUnlocked while the app is in the background, or .whenPasscodeSetThisDeviceOnly when no passcode is set.
    • Missing Entitlements: Shared group Valets require the group to be in your Entitlements file. macOS apps require Keychain Sharing entitlements.
    • Hardware Limitations: SecureEnclaveValet requires a device with a Secure Enclave (A7 chip or later).
    • Xcode/Debugger Issues: Running in DEBUG mode or with a debugger attached can cause entitlement errors. Try running the app without the debugger attached.
    • Data Size: Attempting to write data larger than 4KB will fail as the Keychain is intended for small secrets.
    • Unit Tests: Unit tests must be run inside a host application to satisfy the application-identifier entitlement requirement.
  8. Share secrets across devices with iCloud

    main

    Use iCloudValet(with:accessibility:) to store data that syncs via iCloud Keychain. If iCloud Keychain is disabled on a device, data can still be read/written locally but will not sync.

    let myCloudValet = Valet.iCloudValet(with: Identifier(nonEmpty: "Druidia")!, accessibility: .whenUnlocked)
  9. Initialize a basic Valet instance

    main

    To store data securely on a single device, create a Valet instance using an Identifier and an Accessibility value. The identifier creates a sandbox; Valets with different identifiers cannot access each other's data.

    Note for macOS developers: If your app is signed with a developer ID, the identifier might be visible to users. To provide a user-friendly identifier, use valet(withExplicitlySet:accessibility:). However, doing so bypasses Valet's sandbox guarantee, so ensure your identifiers are globally unique.