The Problem
A naive encrypted-shard storage system leaks information: if the storage record says "shards 0, 1, 2 are real and shards 3, 4 are decoys", an attacker with storage access knows exactly which shards to target. Storing a ShardIndexMap (real/decoy type per shard) defeats the purpose of having decoys at all.
LockBox's shard model is designed so that the storage layer holds no information that distinguishes real shards from decoys.
How It Works
Uniform Key Derivation
All shard keys — real and decoy alike — are derived using the same HKDF context format:
LockBox:shard:{bundleID}:{position}
There is no "real" or "decoy" marker in the context string. An attacker who obtains the HKDF output for position 3 cannot tell whether position 3 holds real data or noise.
Decoy Construction
Decoy shards are generated by DecoyGenerator. Each decoy is:
- The same byte length as a real shard.
- Filled with cryptographically random data (indistinguishable from ciphertext).
- Assigned a random high-range position index to avoid colliding with real shard positions.
The mix ratio is tier-controlled:
| Tier | Decoy Ratio | Total Shards (1 real) |
|---|---|---|
| Basic | 0 | 1 |
| Standard | 0.5 | 1–2 |
| Premium | 1.0 | 2 |
| Elite | 2.0 | 3 |
No ShardIndexMap in Storage
The LockedAsset record stores TotalShards, RealCount, and the HKDF Salt. It does not store which positions are real. The legacy ShardIndexMap field exists for backward compatibility only and is marked deprecated — new locks never write it.
Recovery via Trial Decryption
To recover the real shards after a node restart (when the in-memory shard layout is lost), the service iterates over all shard positions and attempts decryption:
for position in 0..TotalShards:
key = HKDF(masterKey, salt, "LockBox:shard:{bundleID}:{position}")
plaintext, err = ChaCha20Decrypt(shard[position], key)
if err == nil:
realShards.append(plaintext)
if len(realShards) == RealCount:
break
A shard decrypts successfully only if the AEAD authentication tag matches — which is only true for real shards encrypted with the correct key. Decoy shards are random bytes and will fail authentication deterministically.
Metadata Decoys (Premium / Elite)
Beyond data shards, Premium and Elite tiers also apply decoys to the asset metadata (owner address, lock parameters, etc.):
- Premium — 1 real metadata shard + 1 decoy (ratio 1.0)
- Elite — 1 real metadata shard + 2 decoys (ratio 2.0)
The MetadataShardCount and MetadataIndexMap fields on LockedAsset track this. The MetadataIndexMap is still stored (unlike the data shard map) because metadata recovery cannot rely on content-based trial decryption as easily.
Security Boundary
The indistinguishability guarantee holds as long as the HKDF master key is not compromised. The master key is held in memory only, loaded from a secure keystore (SystemKeyStore or KMSKeyStore) at startup, and cleared via ClearBytes when no longer needed. An attacker with access only to the persisted LockedAsset records and the shard storage learns nothing about which shards carry real data.