AppStateBackupEncryptor

AES-256-GCM encryption for the AppStateBackup blob.

Schema (v2 — post-PRF):

Blob:      [version=0x02 : 1 byte] [IV : 12 bytes] [ciphertext+GCMtag : variable]
Plaintext: [metadataLen : 2 bytes BE] [metadata : 0..N bytes]

Variable length — appMetadata is what differs between dApps (Kicks's witness state ≠ BBoard's drafts ≠ future agent state). The pre-PRF v1 schema padded plaintext to a fixed 496 bytes to hide whether a seed was present. Post-PRF the blob doesn't carry a seed (PRF derives it deterministically from the passkey), so the only thing the size could leak is "this user has app state of approximately N bytes," which the package name already discloses anyway.

All intermediate plaintext is wiped in a finally.

Properties

Link copied to clipboard
const val MAX_METADATA_SIZE: Int = 2048

Upper bound on appMetadata — large enough to hold Kicks's full match-state witness bundle (~1 KB worst case) with room to grow, small enough that Block Store's 4 KB entry limit isn't a concern.

Functions

Link copied to clipboard
fun decrypt(blob: ByteArray, aesKey: ByteArray): ByteArray

Decrypts a v2 blob and returns the embedded appMetadata bytes (zero-length array when the blob stored no metadata).

Link copied to clipboard
fun encrypt(aesKey: ByteArray, appMetadata: ByteArray): ByteArray

Encrypts appMetadata into the v2 blob.