public abstract class KeyHandle

  1. Object
  2. KeyHandle

A key you can use and cannot read.

Why this is not a Key

Key is built around encoded bytes: the constructor requires them, getEncoded() is final, and SecretKey is final on top of that. Every one of those is correct for what that class is – a value object wrapping key material – and every one of them makes it impossible to represent a key whose material does not exist as bytes the application can reach. A browser CryptoKey created with extractable: false is exactly that key, and so is an Android keystore key and an iOS Secure Enclave key. Subclassing would have produced a getEncoded() that either lies or throws, and code that takes a Key would keep compiling while silently getting neither.

So this is a separate type with no byte accessor at all. There is no getEncoded, no export, and no flag that turns one on. isExportable() reports whether the underlying material could be exported by some other means – it never provides the means.

What it does not protect against

A handle stops the key material being copied. It does not stop the key being used: while the vault is unlocked, any code running in the application – including script an XSS injected into the page – can call seal and open on a handle it can reach, exactly as the application does. Non-extractability limits what an attacker can carry away, not what they can do while they are there. destroy() and Vault.lock() cut that off for future calls and cannot reach a plaintext already handed out.

Lifecycle

A handle obtained from a vault is invalidated when that vault locks. Calls afterwards fail with VaultError.LOCKED rather than returning stale results, and an operation already in flight when the lock happens does not deliver its result – see Vault.lock().

Constructors

protected KeyHandle()Subclasses are created by the vault or by a port.

Methods

public abstract String getAlgorithm()The algorithm this key is for, e.g. "AES-GCM".
public abstract String getKeyId()A stable identifier for this key, carried in the envelopes it seals so a reader holding several keys can pick the right one without trying each.
public abstract int getVersion()The rotation counter.
public abstract boolean isExportable()Whether the key material could be exported through some other path.
public abstract KeyUsage[] getUsages()The operations this handle permits, fixed at creation.
public boolean permits(KeyUsage usage)Whether this handle permits an operation.
public abstract ProtectionReport getProtection()What actually protects this key, as observed.
public abstract AsyncResource<byte[]> seal(byte[] plaintext, AssociatedData aad)Authenticated encryption.
public abstract AsyncResource<byte[]> open(byte[] sealed, AssociatedData aad)Authenticated decryption of what seal produced.
public AsyncResource<byte[]> mac(byte[] data)A message authentication tag over data, for a handle that permits KeyUsage.MAC.
public AsyncResource<Boolean> verifyMac(byte[] data, byte[] tag)Verifies a tag from [#mac(byte[])], in constant time.
public abstract void destroy()Releases the handle and, where the material is held in this process, overwrites it.
public abstract boolean isDestroyed()Whether destroy() has been called, or the owning vault has locked.

Inherited methods

Constructor details

KeyHandle

protected KeyHandle()
Subclasses are created by the vault or by a port.

Method details

getAlgorithm

public abstract String getAlgorithm()
The algorithm this key is for, e.g. "AES-GCM".

getKeyId

public abstract String getKeyId()
A stable identifier for this key, carried in the envelopes it seals so a reader holding several keys can pick the right one without trying each.

getVersion

public abstract int getVersion()
The rotation counter. Incremented by Vault.rotateDataKey(); envelopes record the version that sealed them, so an old envelope can still be opened after a rotation and can be identified as needing a rewrite.

isExportable

public abstract boolean isExportable()

Whether the key material could be exported through some other path.

false is the interesting answer and means the platform holds the key in a form it will not hand back – a non-extractable CryptoKey, a keystore alias, a Secure Enclave reference. true means the material exists as bytes somewhere in the process; the handle still will not give them to you, but it is not claiming the platform could not.

This method never enables an export. There is no method on this class that does.

getUsages

public abstract KeyUsage[] getUsages()
The operations this handle permits, fixed at creation.

permits

public boolean permits(KeyUsage usage)
Whether this handle permits an operation.

getProtection

public abstract ProtectionReport getProtection()
What actually protects this key, as observed. See ProtectionReport.

seal

public abstract AsyncResource<byte[]> seal(byte[] plaintext, AssociatedData aad)
Authenticated encryption. The result is a SecureEnvelope: the nonce, the key id and the version are managed here and are not the caller’s to choose, because a reused nonce destroys AES-GCM and an API that let one be passed in would eventually see one.

Parameters

plaintext byte[]
the bytes to protect
aad AssociatedData
the binding the result may only be opened against, may be null

Returns

a resource completing with the sealed bytes, or erroring with a VaultException

open

public abstract AsyncResource<byte[]> open(byte[] sealed, AssociatedData aad)

Authenticated decryption of what seal produced.

On a tag mismatch the resource errors with VaultError.AUTHENTICATION_FAILED and no plaintext is delivered – not a truncated one, not an unverified one. A caller that wants the bytes anyway cannot have them from here.

mac

public AsyncResource<byte[]> mac(byte[] data)

A message authentication tag over data, for a handle that permits KeyUsage.MAC.

The default reports VaultError.NOT_SUPPORTED; a handle whose platform provides HMAC overrides it.

verifyMac

public AsyncResource<Boolean> verifyMac(byte[] data, byte[] tag)
Verifies a tag from [#mac(byte[])], in constant time.

destroy

public abstract void destroy()

Releases the handle and, where the material is held in this process, overwrites it.

Best effort, and said so plainly: a managed runtime may have copied the buffer during a collection, the browser’s heap is not ours to scrub, and a JIT may hold a register copy. What this does guarantee is that the handle stops working – subsequent calls fail with VaultError.LOCKED – and that the application’s own reference is cleared.

isDestroyed

public abstract boolean isDestroyed()
Whether destroy() has been called, or the owning vault has locked.