public class SecureStorageDeviceProtection

  1. Object
  2. DeviceProtection
  3. SecureStorageDeviceProtection

The DeviceProtection every port gets when it does not supply one of its own: a random wrapping key in SecureStorage, and AES-GCM performed in shared code.

This is the right implementation wherever SecureStorage is the operating system’s secret store – the iOS keychain, the Android keystore, the desktop credential store. The wrapping key is bytes, and it is bytes in the one place on the device that is built to hold bytes nobody else should reach.

It is the wrong implementation in a browser, where SecureStorage is ordinary origin-private storage: the wrapping key would sit beside the ciphertext it protects, which is no protection at all. The JavaScript port therefore overrides CodenameOneImplementation.getDeviceProtection with one built on a non-extractable CryptoKey, and this class never runs there.

Racing callers

Key creation goes through SecureStorage.setIfAbsent(String, String), which returns what the store ended up holding rather than what this call wrote. Two processes that both find nothing therefore agree on one key instead of each overwriting the other – which matters more here than almost anywhere, because the loser’s key is what a device’s remembered vault was wrapped under.

Constructors

public SecureStorageDeviceProtection()

Methods

protected SecureStorage storage()The store backing this.
public ProtectionReport protection()What this device protection actually provides, as observed rather than as advertised.
public int keyState(String keyId)Whether a device key exists for this id.
public AsyncResource<Boolean> ensureKey(String keyId)Creates the device key if there is none, and converges when two callers race.
public AsyncResource<byte[]> wrap(String keyId, byte[] plaintext, byte[] aad)Encrypts under the device key.
public AsyncResource<byte[]> unwrap(String keyId, byte[] wrapped, byte[] aad)Decrypts what wrap produced.
public AsyncResource<Boolean> deleteKey(String keyId)Removes the device key, which is what “forget this device” does.

Inherited fields

Inherited methods

Constructor details

SecureStorageDeviceProtection

public SecureStorageDeviceProtection()

Method details

storage

protected SecureStorage storage()
The store backing this. Overridable so a port can supply a store other than the one SecureStorage.getInstance() returns, which the simulator’s test harness uses.

protection

public ProtectionReport protection()

What this device protection actually provides, as observed rather than as advertised.

A port that has not yet tried an operation may only report what it can verify; several answers are legitimately ProtectionReport.UNKNOWN – no browser can say whether a key is hardware backed, and reporting NO there would understate an authenticator that uses a secure element.

keyState

public int keyState(String keyId)
Whether a device key exists for this id.

Parameters

keyId String
the vault’s device key id

ensureKey

public AsyncResource<Boolean> ensureKey(String keyId)
Creates the device key if there is none, and converges when two callers race.

Parameters

keyId String
the vault’s device key id

Returns

a resource completing with true when a key is in place – whether this call created it or found another one already there – and erroring with a VaultException when no key could be established

wrap

public AsyncResource<byte[]> wrap(String keyId, byte[] plaintext, byte[] aad)
Encrypts under the device key.

Parameters

keyId String
the vault’s device key id
plaintext byte[]
what to protect, normally a vault’s 32 byte data key
aad byte[]
associated data the result is bound to; the same bytes must be supplied to unwrap

Returns

a resource completing with the wrapped bytes. The format is the port’s own and is never interpreted by shared code, but it must be authenticated

unwrap

public AsyncResource<byte[]> unwrap(String keyId, byte[] wrapped, byte[] aad)

Decrypts what wrap produced.

A failure to authenticate must arrive as VaultError.AUTHENTICATION_FAILED, a missing key as VaultError.KEY_MISSING, and a store that could not be read as VaultError.TEMPORARILY_UNREADABLE. Collapsing the last two is how a vault regenerates a key and orphans its data.

deleteKey

public AsyncResource<Boolean> deleteKey(String keyId)

Removes the device key, which is what “forget this device” does.

Everything wrapped under it becomes unopenable on this device. That is the intent; it is not revocation, because a copy taken while the key existed is beyond reach.