The storage is kept in a private file which is contained in the application sandbox. Both the permanent and volatile storages are encrypted. The data integrity of the permanent storage is verified during the reading process.

To ensure that the data stored in the non-volatile storage is readily available, the keys in the key-value pairs of the data are not encrypted. The value is decrypted once the data is accessed using the getString() and getBytes() method.

Data-based protection

The storage protection is ensured with the following input parameters:

  • Device fingerprint: Used during the storage encryption. This data must be device-dependent to bind the storage with the mobile device. To generate a device fingerprint you can develop your own binding mechanism, or use OneSpan Device Binding SDK.

    For more information, seethe Device Binding SDK Integration Guide in Integration Guides .

Hardware-based protection

By default, the storage is protected with secure hardware to set up very strong binding between the storage and the mobile device. Supported secure hardware processors or processor areas are for example Trusted Execution Environment (TEE) or Secure Element (SE) on Android, and Secure Enclave on iOS.

On Android, the hardware protection is dependent on the manufacturer’s integration of the Android OS.

On iOS, the hardware protection is available on all supported devices.

The Secure Storage SDK provides utility methods to check whether the storage is protected by secure hardware:

  • isProtectedBySecureHardware on iOS (deprecated since all supported devices are protected by secure hardware).
  • isSecureHardwareProtected on Android.

On Android, some devices may experience issues during key recovery from the secure hardware. This is due to manufacturer-specific implementations and occurs when the devices have been used for a long time without restart. In this case, the Secure Storage SDK will return an UNRECOVERABLE_KEY exception.

iOS user authentication protection

The Protection parameter can protect the storage by requiring a device passcode or biometry as authentication options before unlocking the storage. Once the storage is initialized and unlocked, the protection level can be changed in a subsequent write call by passing the desired protection level. For more information, refer to the technical documentation in product package.

Setting the protection level has an impact on the UX of the hosting app. Each call to initialize a protected storage will result in a prompt for the user to either scan biometry or enter the device PIN. The impact could be bigger if you use the Device Binding SDK to provide a fingerprint to the Secure Storage SDK (recommended) because the same protection can be applied in the Device Binding SDK. If you use these SDKs together you might opt for using the protection in only one of the two SDKs to avoid multiple prompts in the hosting app.

Before using biometry as an option for the Protection parameter, you must add a Privacy - Face ID Usage Description key to the Info.plist file.

Any storage saved with Protection.biometryCurrentSet will be locked if there are any changes to the currently-enrolled biometry set after the storage is saved with that protection. These changes include adding, removing, or resetting a fingerprint or Face ID. There is no guarantee from Apple that a major iOS update will not impact the Protection.currentBiometrySet.

Android biometric protection

The biometric protection is available for devices with Android version 11 (API level 30) and above. It can be enabled when the data in memory is written to permanent storage using the public method writeBiometryProtected, which requires some additional parameters. These parameters define the feature behaviors such as timeout for re-authentication, allowing fallback to device credentials (such as PIN), etc.

The Secure Storage API offers two ways to persist data: one with biometric protection named writeBiometryProtected and the other without biometric protection named write. Therefore, the order of the calls in the code is important as it will either set the protection level of the entire storage, or disable the protection. If you require the ability to store values with and without biometry protection, we strongly recommend having separate storages.

Extreme caution should be taken when making changes to the device and storage protection levels. Consider a device that has a currently-enrolled biometry set and security methods such as PIN, fingerprints, etc, with values stored in the Secure Storage SDK. That storage could be locked, and subsequently lost, if a user reduces the device protection to a level lower than the protection of the secure storage. Protection changes include removing or resetting fingerprints, face recognition, PIN, pattern, etc.