# v0.5.0 — Additional Fragment Strategies + Composition (Layer 3 complete)

**Release date:** 2026-05-20
**Phase:** 0.5.0 — `RandomFragmenter` + `InterleavedFragmenter` + `LayeredFragmenter`
**Status:** development (pre-1.0; public API subject to change without notice)

---

## What is `key-vault`?

`key-vault` is an enterprise-grade in-memory key management vault for Rust.
See [README](../../README.md) for the 1.0 design and
[`docs/SECURITY.md`](../SECURITY.md) for the per-layer architecture.

After this release, **Layer 3 is complete**: the four built-in fragment
strategies described in the 1.0 design are all in place.

| Layer | Status after 0.5.0 |
|-------|--------------------|
| Layer 2 — page locking | ✅ |
| **Layer 3 — fragmentation (4/4 strategies)** | **✅ complete** |
| Layer 4 — decoy bytes (3 strategies) | ✅ |
| Layer 5 — codex (`IdentityCodex` + `FnCodex`) | partial |
| Layer 6 — constant-time handle equality | ✅ |
| Layer 7 — zero-on-drop | ✅ |

---

## What's in 0.5.0

### Three new Layer-3 strategies

#### `RandomFragmenter` — non-contiguous byte scatter

Each chunk's bytes come from independently-chosen random positions of the
original key. No chunk ever contains a contiguous run of key bytes longer
than 1 byte.

**Threat focus.** Defeats contiguous-format recognition attacks (DER
envelopes, PEM markers, ASCII-armored data, JWT headers). Even if an
attacker reads an entire chunk, they get up to `max_chunk` (default 4)
bytes from random positions — no recoverable substring.

#### `InterleavedFragmenter` — single pool with random byte placement

Allocates a single `LockedBytes` pool larger than the key (default 4×)
and writes key bytes at randomly-chosen positions within it. The other
positions are filled with CSPRNG random padding bytes.

**Threat focus.** Defeats byte-level statistical analysis of the pool —
key bytes are indistinguishable from padding without the position map.

#### `LayeredFragmenter` — composition by random routing

Holds a `Vec<Arc<dyn FragmentStrategy>>`. Each `fragment` call picks one
sub-strategy uniformly at random and delegates entirely; the picked
index is prepended to the layout as a 4-byte header so `defragment` can
dispatch correctly.

**Threat focus.** Against an attacker who has the chunks and the layout
buffer, `LayeredFragmenter` adds an additional `log2(N)` bits of
uncertainty about which sub-strategy was used.

```rust
use std::sync::Arc;
use key_vault::{
    FragmentStrategy, InterleavedFragmenter, LayeredFragmenter,
    RandomFragmenter, RawKey, StandardFragmenter,
};

let frag = LayeredFragmenter::new(vec![
    Arc::new(StandardFragmenter::new()) as Arc<dyn FragmentStrategy>,
    Arc::new(InterleavedFragmenter::new()) as Arc<dyn FragmentStrategy>,
    Arc::new(RandomFragmenter::new()) as Arc<dyn FragmentStrategy>,
])?;

let raw = RawKey::new(b"my application key".to_vec());
let fragments = frag.fragment(&raw)?;
let recovered = frag.defragment(&fragments)?;
assert_eq!(recovered.len(), 18);
# Ok::<(), key_vault::Error>(())
```

### Strategy comparison

| Strategy                | Storage shape                              | Threat focus              | Memory overhead |
|-------------------------|--------------------------------------------|---------------------------|-----------------|
| `StandardFragmenter`    | Many small `LockedBytes`, shuffled order   | Pattern recognition       | 1–2× (with decoy) |
| `RandomFragmenter`      | Many small `LockedBytes`, non-contiguous bytes | Contiguous-format recognition | 5× |
| `InterleavedFragmenter` | One large `LockedBytes` pool with random padding | Byte-level statistical analysis | 4× |
| `LayeredFragmenter`     | Whatever sub-strategy picked + 4B header   | Sophisticated attackers   | sub-strategy's |

`docs/SECURITY.md` carries the full per-strategy comparison with layout
encodings and decoy compatibility.

### New / changed public surface

| Item                                | Status                            |
|-------------------------------------|-----------------------------------|
| `RandomFragmenter`                  | **new**                           |
| `InterleavedFragmenter`             | **new**                           |
| `LayeredFragmenter`                 | **new**                           |
| `Fragments::chunk_count()`          | `pub(crate)` → `pub`              |
| Crate-root re-exports               | extended for the three new types  |

### Test surface (relative to 0.4.0)

|                   | 0.4.0 | 0.5.0 |
|-------------------|-------|-------|
| Unit tests        |   66  |   82  |
| Integration tests |    9  |   15  |
| Doc tests         |    9  |    9  |
| **Total**         | **84** | **106** |

---

## Breaking changes

Purely additive on top of 0.4.0:

- All 0.4.0 types and methods are preserved.
- `Fragments::chunk_count()` going from `pub(crate)` to `pub` is strictly
  a visibility relaxation.

---

## Verification

Verified locally on both stable and pinned MSRV 1.85:

```text
cargo {fmt, clippy, test, doc} --all-features      clean
106 tests pass; -D warnings on clippy + RUSTDOCFLAGS
```

No new `unsafe` blocks in this release.

---

## Installation

```toml
[dependencies]
key-vault = "0.5"
```

Default features unchanged from 0.4.0. Minimum supported Rust version:
**1.85** (edition 2024).

---

## Documentation

- [`README.md`](../../README.md) — overview + Current status table
- [`docs/SECURITY.md`](../SECURITY.md#strategies-shipped-in-050) — Layer-3 strategy comparison
- [`docs/TRANSFORMATION.md`](../TRANSFORMATION.md) — visual walkthrough
- [`CHANGELOG.md`](../../CHANGELOG.md) — full change log

---

## Diff

Full diff: [`v0.4.0...v0.5.0`](https://github.com/jamesgober/key-vault/compare/v0.4.0...v0.5.0)

---

<sub>key-vault v0.5.0 — Copyright (c) 2026 James Gober. Apache-2.0 OR MIT.</sub>
