Skip to content

Latest commit

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SplitDisk — Tool Specification

Version: 0.2-draft
Language: Rust (stable toolchain, x86_64-unknown-linux-musl for embedded binary)
Status: Specification only — not yet implemented
License: GPL-3.0 (matching Galdralag-firmware)
CESS conformance: CESS-FULL (targets CESS v0.2-draft; see §4 and §15)
Glossary:Plain-language definitions of technical terms
AI contributors:AGENTS.md (see also CLAUDE.md); Cursor rules live in .cursor/rules/*.mdc


1. Purpose

SplitDisk is a command-line tool and initramfs-resident assembly agent for splitting a disk image or filesystem into k-of-n physical USB shares, each bootable, such that any k drives inserted in sequence can reconstruct and write the original content to a target disk. Members holding drives are not informed of their share index, the total number of shares, or the threshold required.

Optional integration with the Galdralag hardware security token (Baochip-1x running Galdralag firmware) provides on-device PIN verification, Shamir key-share storage, and Brainpool/post-quantum encryption without requiring any custom host-side cryptographic driver beyond a standard CCID stack.


2. Threat Model and Design Goals

GoalDescription
Holder anonymityA drive holder does not know their share index, the value of k or n, or the content.
Coercion resistanceA holder genuinely cannot answer "how many others are there?" or "are you required?"
Physical confidentialityDrive contents look like opaque binary blobs. No filenames, labels, or metadata reveal the scheme.
Multi-factor unlockEach drive requires PIN (≥ 5 alphanumeric characters) and optionally biometric before its share is accepted.
Long-term securityEncryption must remain secure for decades. Hybrid classical + post-quantum where available.
Fault tolerancen > k so that absent or unavailable members do not prevent reconstruction.
IntegrityEach share chunk is hashed with BLAKE3; corruption is detected before reconstruction begins.
Resumable assemblyIf power is lost mid-write, assembly can resume from a checkpoint.
Verified writeReconstruction is dry-run tested against the source hash before the operator is permitted to destroy the original.
Source destructionAfter verified write, the source drive or image file can be overwritten with random data so the plaintext cannot be recovered from the enrollment machine.
CESS conformanceAll cryptographic primitives conform to CESS v0.2 algorithm registry; NSA-designed and NIST-only primitives are excluded.

Non-goals: network transmission of shares, online key management, integration with cloud storage.


3. Architecture Overview

SplitDisk consists of two programs:

  • splitdisk-create — run once on an air-gapped enrollment machine to produce n share carriers (USB drives, Galdralag tokens, or a mix) from a source disk image.
  • splitdisk-assemble — a statically linked binary embedded in the initramfs of every share USB drive, or invoked on the assembly machine when all shares are Galdralag tokens. Orchestrates reconstruction.

USB drive mode:

┌─────────────────────────────────────────────────────┐
│ splitdisk-create │
│ │
│ source image ──► [encrypt] ──► [Reed-Solomon │
│ split k-of-n] │
│ │ │
│ [SSS split of session key] │
│ │ │
│ ┌────────────┼────────────┐ │
│ USB 1 USB 2 ... USB n │
│ (bootable, (bootable, (bootable, │
│ chunk 1, chunk 2, chunk n, │
│ key share 1) key share 2) key share n) │
└─────────────────────────────────────────────────────┘
At boot time on any share USB:
┌─────────────────────────────────────────────────────┐
│ splitdisk-assemble (initramfs) │
│ │
│ Boot USB ──► read own chunk + key share silently │
│ Loop until k shares collected: │
│ insert drive ──► PIN check ──► biometric check │
│ ──► read chunk + key share ──► progress bar +1 │
│ Reconstruct session key via SSS │
│ Decrypt + Reed-Solomon decode │
│ Write to target disk with checkpoint journal │
└─────────────────────────────────────────────────────┘

Galdralag token mode:

┌─────────────────────────────────────────────────────┐
│ splitdisk-create │
│ │
│ source image ──► [encrypt] ──► [Reed-Solomon │
│ split k-of-n] │
│ │ │
│ [SSS split of session key] │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ Token 1 Token 2 ... Token n │
│ RRAM: key share RRAM: key share RRAM: key share│
│ SD: chunk 1 SD: chunk 2 SD: chunk n │
└─────────────────────────────────────────────────────┘
At assembly time (any machine, no bootable share USB needed):
┌─────────────────────────────────────────────────────┐
│ splitdisk-assemble │
│ │
│ Loop until k tokens inserted: │
│ insert token ──► CCID session ──► PIN on token │
│ ──► read chunk from SD ──► read key share │
│ from RRAM ──► progress bar +1 │
│ Reconstruct session key via SSS │
│ Decrypt + Reed-Solomon decode │
│ Write to target disk with checkpoint journal │
└─────────────────────────────────────────────────────┘

Mixed mode (some USB drives, some tokens) is also supported within the same scheme. The assembly agent handles both share types transparently.


4. Cryptographic Design

SplitDisk targets CESS-FULL conformance (CESS v0.2-draft). All primitives are drawn from the CESS Algorithm Registry. NSA-designed primitives (AES, SHA-2, SHA-3) and NIST-only curves (P-256, P-384, P-521) are excluded. ML-KEM is excluded per CESS policy; FrodoKEM-1344 is the approved post-quantum KEM where PQ is enabled.

4.1 Encryption Layer

All encryption uses a hybrid scheme for long-term security:

Session key: 32 random bytes from hardware TRNG or OS CSPRNG
Classical KEM: Ephemeral ECDH over BrainpoolP384r1 (default, outer session)
or BrainpoolP512r1 (inner profile, --cipher brainpool512)
RFC 5639, BSI TR-03111
Shared secret: x-coordinate, fixed-length big-endian encoding
Point validation: mandatory (invalid curve points rejected)
Post-quantum KEM: FrodoKEM-1344 (feature-gated, --features pq)
Hybrid only; never standalone
KDF: HKDF-BLAKE3 (RFC 5869 structure, HMAC-BLAKE3 as PRF)
info strings: "cess-kem-v1" (KEM), "cess-pin-v1" (PIN wrap),
"cess-outer-envelope-v1" (outer K_outer)
Combined (hybrid):
session_key = HKDF-BLAKE3(
ikm = classical_shared_secret || pq_shared_secret,
info = "cess-kem-v1"
)
Bulk cipher (select one):
ChaCha20-Poly1305 (RFC 8439, 32-byte key, 12-byte nonce) [default]
Serpent-256-CTR + Poly1305 (32-byte key, RFC 8439 MAC layout)
Twofish-256-CTR + Poly1305 (256-bit key, 128-bit block, same MAC layout)
Cascade: ChaCha20-Poly1305 inner + Serpent-256-CTR+Poly1305 outer
Cascade: ChaCha20-Poly1305 inner + Twofish-256-CTR+Poly1305 outer
Cascade: Twofish inner + Serpent outer
Triple cascade: ChaCha inner + Serpent middle + Twofish outer
Suite selection is encoded as a CESS suite_id (16-bit, see §15).

Outer wrapper (Mode A): When --mode a is used (default for USB drive mode), an outer ChaCha20-Poly1305 layer keyed from ephemeral BrainpoolP384r1 ECDH conceals the suite_id and inner ciphertext from observers. The suite_id is never transmitted in cleartext. See CESS v0.2 §6.6 and §8.

Simple mode (Mode B): When --mode b is used, the cipher profile is pre-agreed and no suite_id appears on the wire. Suitable for simpler deployments where metadata concealment is not required.

BrainpoolP256r1 + ChaCha20-Poly1305: A lightweight profile using BrainpoolP256r1 is available for less sensitive deployments via --cipher brainpool256. This operates in Mode B (no outer wrapper, suite profile visible); it is outside the CESS-FULL normative profile but supported as a documented extension. Shared secret is the x-coordinate, 32-byte fixed-length big-endian encoding.

4.2 Data Splitting

Reed-Solomon erasure coding splits the encrypted image into n chunks, any k of which suffice to reconstruct.

chunk_size ≈ image_size / k (plus RS parity overhead ~10%)

Each chunk is prefixed with a BLAKE3 hash for integrity verification before reconstruction begins.

4.3 Key Splitting

The 32-byte session key is split using Shamir's Secret Sharing over GF(2⁸) (reduction polynomial x^8 + x^4 + x^3 + x + 1, identical to CESS §5.1) into n shares, threshold k.

Each key share is encrypted at rest on the USB using the PIN-derived key (see §5.2), so raw access to the drive bytes does not expose the share.

4.4 Crate Policy

All cryptographic primitives come from audited crates. Nothing is implemented in-tree. Crates are selected for no_std + musl compatibility where required by the embedded initramfs binary.

CratePurposeAudit status
chacha20poly1305Bulk AEAD (primary)RustCrypto audited
serpentSerpent-256 bulk cipherReview pending
twofishTwofish-256 bulk cipherReview pending
poly1305MAC for Serpent/Twofish CTR modesRustCrypto audited
p384BrainpoolP384r1 ECDH baseRustCrypto audited
hkdfHKDF structure (RFC 5869)RustCrypto audited
blake3BLAKE3 integrity and HKDF PRFAudited (NCC Group, Kudelski)
argon2PIN hashing (Argon2id)RustCrypto audited
vsss-rsShamir secret sharing GF(2⁸)Independent review
reed-solomon-erasureData splittingReview pending
zeroizeSecret memory clearingRustCrypto audited
subtleConstant-time comparisonsRustCrypto audited
sequoia-openpgpOptional GPG envelope (Brainpool keys)Active maintenance
pqcrypto-frodoFrodoKEM-1344 (feature-gated)Pending independent audit

One can also check integrity of crates with this when the pr is closed: rust-lang/cargo#16850

Post-quantum features are gated behind --features pq and carry a warning that the underlying crates have not been independently audited, matching the policy in Galdralag firmware and CESS §7.3.


5. Authentication Per Drive

5.1 Factor Overview

Each drive requires the holder to present:

FactorMechanismRequired
Something they haveThe USB driveAlways
Something they knowPIN ≥ 5 alphanumeric charactersAlways
Something they areBiometric (iris/retina scan)Optional per drive

All factors are checked before the drive's key share is released into memory. Failure in any factor does not indicate which factor failed — the UI reports only "Authentication failed. Please remove drive."

5.2 PIN

  • Minimum length: 5 alphanumeric characters, enforced at input boundary before any hash comparison.
  • Hashing: Argon2id (RFC 9106) with a per-drive random 16-byte salt, memory = 64 MiB, iterations = 3, parallelism = 4 (CESS §5.4 profile).
  • The Argon2id hash is stored in the drive's hidden data partition.
  • PIN-derived key: K_pin = HKDF-BLAKE3(argon2id_output, info="cess-pin-v1") → 32 bytes, wrapping the key share with ChaCha20-Poly1305 (default) or the profile's registered AEAD.
  • Attempt limit: 5 per drive insertion (configurable 3–10 at creation time).
  • On limit exhaustion: drive is ejected and must be re-inserted. The PIN hash is not modified (no lockout wipe, unlike a hardware token).
  • Cool-down: 30-second delay after each failed attempt.

5.3 Biometric

Biometric support is optional and configured per drive at enrollment time.

Supported hardware: USB iris scanners with Linux drivers (e.g. IriShield MK series, or compatible devices exposing a V4L2 or proprietary SDK interface).

Enrollment flow:

  1. Capture iris image on the enrollment machine using the scanner.
  2. Extract ISO/IEC 19794-6 feature template.
  3. Encrypt template with K_pin (PIN must be correct first).
  4. Store encrypted template in hidden partition alongside PIN hash.

Verification flow:

  1. PIN verified first.
  2. K_pin derived → template decrypted in memory.
  3. Iris capture performed.
  4. Feature extraction and comparison against stored template, within a configurable tolerance threshold (Hamming distance on iris codes, threshold ≤ 0.32 by default, following ISO recommendations).
  5. On match: key share released. Template and derived keys zeroized from memory.
  6. On failure: 3 biometric attempts per successful PIN entry, then drive ejected.

The biometric template never leaves the drive unencrypted. No central biometric database exists.

5.4 Galdralag Hardware Token Integration (Optional)

When --galdralag mode is used, the Galdralag Baochip-1x token is the complete share — no separate USB drive is needed or issued to the member. The token holds both the encrypted data chunk and the key share in its on-device storage (RRAM vault or optional SD card), and presents itself to the assembly machine over USB. The member carries only the token.

Storage tiers on the token:

StorageCapacityUsed for
RRAM vaultSmall (key material only)SSS key share, PIN policy, metadata
Optional SD cardLarge (bulk data)Encrypted data chunk

If the token has an SD card fitted, the data chunk is stored there. If no SD card is present, the token cannot hold a data chunk and --galdralag mode requires SD card presence — splitdisk-create will check and abort if the card is absent or insufficient at enrollment time.

What the token provides during assembly:

  • Data chunk — streamed from SD card over USB to the assembly machine.
  • Key share — held in RRAM vault, released only after PIN and optional biometric verification on the token itself.
  • BrainpoolP384r1 or BrainpoolP512r1 ECDH — wraps the key share in transit using the token's on-device keys.
  • Authenticated ephemeral ECDH session — forward-secret channel between token and assembly agent; the key share never appears on the USB bus in plaintext.
  • Hardware TRNG — used for all randomness during enrollment and assembly.
  • Hardware PIN counter with zeroization — 3–10 configurable attempts; on exhaustion the token zeroizes its RRAM, permanently destroying the share.

Assembly agent behaviour:

The agent detects a Galdralag token by its CCID descriptor on any USB port. When detected, it opens an ephemeral ECDH session, requests PIN entry (handled on the token's own PIN policy, not the software attempt counter), reads the data chunk from the SD card, and retrieves the decrypted key share over the authenticated channel. No separate USB drive insertion is prompted for that share.

Requirements:

  • CCID-capable host (pcscd + ccid driver included in initramfs)
  • Token fitted with SD card of sufficient capacity (ceil(image_size / k) + 10%)
  • Token provisioned at enrollment via galdra device shamir-store and galdra device chunk-write

Mixed modes are supported: some members can hold USB drives (standard mode) and others can hold Galdralag tokens, within the same k-of-n scheme. The assembly agent handles both transparently in the same insertion loop.


6. USB Drive Layout

Each drive presents a standard bootable layout. To an observer mounting the drive on any operating system, it looks like a minimal bootable Linux USB with some unremarkable binary files.

/dev/sdX1 FAT32 ~256 MiB EFI System Partition
/EFI/BOOT/BOOTX64.EFI Standard EFI bootloader (GRUB or systemd-boot)
/boot/vmlinuz Linux kernel (minimal config)
/boot/initramfs.img initramfs containing splitdisk-assemble
/dev/sdX2 ext4 remainder System partition
/usr/bin/splitdisk-assemble (statically linked, stripped)
/share/chunk.bin Encrypted RS data chunk (opaque bytes)
/share/auth/pin.hash Argon2id PIN hash
/share/auth/retina.enc Encrypted biometric template (if enrolled)
/share/meta.bin Encrypted metadata (share index, k, n, chunk hash)

meta.bin is encrypted with K_pin, so share index and scheme parameters are not visible without the correct PIN. The partition label is blank. The volume UUID is randomly generated and not reused across drives.

GRUB is configured with GRUB_TIMEOUT=0 and no menu. The screen is blank during boot until the assembly agent TUI appears.


7. Assembly Agent TUI

The TUI is built with ratatui and runs fullscreen. It intentionally reveals no information about share indices or member count.

7.1 Startup Screen

┌─────────────────────────────────────────────────────┐
│ │
│ SECURE ASSEMBLY SYSTEM │
│ │
│ Insert drives one at a time. │
│ Remove each drive when prompted. │
│ │
│ Progress: ░░░░░░░░░░ │
│ │
│ Waiting for first drive... │
│ │
└─────────────────────────────────────────────────────┘

The booting drive's own share is read silently and automatically at startup. Progress advances by one without user interaction.

7.2 PIN Entry

┌─────────────────────────────────────────────────────┐
│ │
│ Drive detected. │
│ │
│ Enter PIN: ▓▓▓▓▓▓▓▓░ │
│ (minimum 5 characters) │
│ │
│ Press Enter to confirm. │
│ │
└─────────────────────────────────────────────────────┘

Input is masked with characters. Backspace supported. No character count displayed (avoid leaking PIN length).

7.3 Biometric Prompt (if enrolled)

┌─────────────────────────────────────────────────────┐
│ │
│ Look into the scanner. │
│ │
│ [ Scanning... ████████░░ ] │
│ │
└─────────────────────────────────────────────────────┘

7.4 Progress

┌─────────────────────────────────────────────────────┐
│ │
│ SECURE ASSEMBLY SYSTEM │
│ │
│ Progress: ████████░░░░░░░░░░░░ 3 drives read │
│ │
│ Please insert another drive. │
│ │
└─────────────────────────────────────────────────────┘

No member names, no share IDs, no indication of how many remain.

7.5 Duplicate Detection

If the same drive is inserted twice (detected by comparing a hidden drive fingerprint stored in meta.bin), the progress bar does not advance:

 This drive has already been read. Please insert a different one.

No indication that it is "drive 4" or "already seen drive 4."

7.6 Reconstruction

When k shares are collected:

┌─────────────────────────────────────────────────────┐
│ │
│ All required drives collected. │
│ │
│ Verifying integrity... ████████████ 100% │
│ Reconstructing... ████████████ 100% │
│ Decrypting... ██████░░░░░░ 63% │
│ Writing to disk... ████░░░░░░░░ 41% │
│ │
│ Target: /dev/sdb (238.5 GiB) │
│ │
└─────────────────────────────────────────────────────┘

7.7 Target Disk Selection

Before writing, the agent lists available block devices (excluding share USBs) and prompts for confirmation. It checks that the target is large enough and warns if it is not.

7.8 Failure Screens

All failure messages are generic:

 Authentication failed. Please remove drive.
Integrity check failed. Drive may be corrupted.
Target disk too small. Minimum required: 128.4 GiB.

8. splitdisk-create CLI

8.1 Basic Usage

splitdisk-create \
--input /dev/sda \
--drives /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf /dev/sdg \
--threshold 3 \
--cipher brainpool384 \
--bulk-cipher chacha20 \
--pin-attempts 5 \
--shred

This creates a 3-of-6 scheme using BrainpoolP384r1 + ChaCha20-Poly1305 in Mode A (outer wrapper concealing suite identity). The tool announces required drive sizes, writes all 6 drives, verifies reconstruction with 3 drives, then offers to shred the source.

8.2 Options

FlagDescriptionDefault
--input <path>Source block device or image fileRequired
--drives <paths>Target USB devices (exactly n)Required
--threshold <k>Minimum shares for reconstructionRequired
--cipher <n>brainpool256, brainpool384, brainpool512brainpool384
--bulk-cipher <n>chacha20, serpent, twofish, cascade-cs, cascade-ct, cascade-ts, cascade-cstchacha20
--mode <n>a (outer wrapper, suite concealed) or b (pre-agreed, no wrapper)a
--gpg-recipients <fps>Wrap session key for GPG recipients (Brainpool keys)None
--pin-attempts <n>Failed PIN attempts before cooldown per insertion5
--biometricEnroll biometric at creation time (requires scanner)Off
--galdralagStore key shares on Galdralag tokens instead of drivesOff
--features pqEnable FrodoKEM-1344 post-quantum hybrid (feature-gated)Off
--label <text>Boot-time display label (shown before PIN prompt)None
--checkpoint-dir <path>Directory on target disk for resume journal/var/splitdisk
--shredOffer to overwrite source with random data after verified writeOff
--shred-passes <n>Reserved; always 1 (multiple passes not meaningful on modern media)1

Bulk cipher shorthand for --bulk-cipher:

ValueMeaning
chacha20ChaCha20-Poly1305 single layer
serpentSerpent-256-CTR + Poly1305 single layer
twofishTwofish-256-CTR + Poly1305 single layer
cascade-csChaCha inner + Serpent outer (CESS default cascade)
cascade-ctChaCha inner + Twofish outer
cascade-tsTwofish inner + Serpent outer
cascade-cstTriple: ChaCha inner + Serpent middle + Twofish outer

8.3 Pre-flight: Carrier Requirement Announcement

Before any writes, splitdisk-create inspects the source image and the chosen k and n parameters, then prints a clear human-readable summary and requires explicit confirmation before proceeding.

USB drive mode example:

Source: /dev/sda (476.9 GiB)
Scheme: 3-of-6 (any 3 drives reconstruct the content)
Cipher: BrainpoolP384r1 + ChaCha20-Poly1305 [Mode A]
CESS suite_id: 0x0001
You will need: 6 × USB drives, each at least 128 GiB
(chunk size: ~159 GiB per drive including boot environment
and Reed-Solomon parity overhead)
Drives provided: /dev/sdb 256 GiB ✓
/dev/sdc 256 GiB ✓
/dev/sdd 256 GiB ✓
/dev/sde 256 GiB ✓
/dev/sdf 256 GiB ✓
/dev/sdg 128 GiB ✗ INSUFFICIENT — need 159 GiB
WARNING: /dev/sdg is too small. Aborting.

Galdralag token mode example:

Source: /dev/sda (476.9 GiB)
Scheme: 3-of-6 (any 3 tokens reconstruct the content)
Cipher: BrainpoolP384r1 + ChaCha20-Poly1305 [Mode A]
CESS suite_id: 0x0001
You will need: 6 × Galdralag tokens, each with SD card ≥ 159 GiB
Key shares fit in RRAM — no minimum RRAM size beyond firmware.
Tokens detected:
Token A SD: 256 GiB RRAM: OK ✓
Token B SD: 256 GiB RRAM: OK ✓
Token C SD: 256 GiB RRAM: OK ✓
Token D SD: 256 GiB RRAM: OK ✓
Token E SD: 256 GiB RRAM: OK ✓
Token F SD: 64 GiB RRAM: OK ✗ INSUFFICIENT — SD needs ≥ 159 GiB
WARNING: Token F SD card is too small. Aborting.

If all carriers are sufficient:

All 6 carriers are sufficient.
This operation will DESTROY all data on the 6 carriers listed above.
Type YES to continue, or press Ctrl-C to abort: _

Required size per carrier:

required_per_carrier = ceil(image_size / k)
+ rs_parity_overhead (~10% of chunk size)
+ boot_environment_size (512 MiB, USB drives only)

8.4 Enrollment Session

splitdisk-create performs enrollment interactively after pre-flight passes:

  1. Reads and BLAKE3-hashes the source image (streaming, reports progress).
  2. Generates 32-byte session key from hardware TRNG or OS CSPRNG.
  3. Encrypts image with chosen cipher suite (streaming, reports progress).
  4. Splits ciphertext into n chunks via Reed-Solomon.
  5. Splits session key into n shares via SSS (vsss-rs).
  6. For each drive in sequence: a. Writes bootloader, kernel, initramfs (produced by splitdisk-image; see §10). b. Writes data chunk. c. Prompts operator to set PIN for this drive (entered twice for confirmation, minimum 5 alphanumeric characters enforced before hashing). d. If --biometric: prompts to scan iris for this drive's holder. e. Encrypts key share and metadata with PIN-derived key. f. Writes encrypted share data. g. Reads back and verifies written chunk against source BLAKE3 hash.
  7. Post-write verification — see §8.5.
  8. Source shredding — see §8.6.

The session key is zeroized from memory immediately after step 6 completes.

8.5 Post-write Verification

After all n drives are written, splitdisk-create performs a mandatory reconstruction dry-run using exactly k drives to confirm the scheme is viable before the operator is permitted to shred the source.

The operator is prompted to insert any k of the n drives one at a time. For each inserted drive:

Verification — insert any 3 of your 6 drives for reconstruction test.
Drive 1 of 3: insert a drive now...
✓ Drive accepted. Chunk hash verified.
Drive 2 of 3: insert a drive now...
✓ Drive accepted. Chunk hash verified.
Drive 3 of 3: insert a drive now...
✓ Drive accepted. Chunk hash verified.
Reconstructing session key... ✓
Decrypting and decoding... ✓
Output hash matches source: ✓ (BLAKE3: a3f9…c214)
Verification successful. All 3 tested drives can reconstruct the content.

The reconstructed output is written to a temporary memory-mapped buffer and its BLAKE3 hash is compared against the hash of the source image taken in step 1 of §8.4. The temporary output is zeroized and discarded after verification; it is never written to a persistent disk.

Verification is not optional and cannot be skipped.

8.6 Source Shredding

Source shredding is offered only after §8.5 verification succeeds. It is explicitly opt-in — the operator must pass --shred at invocation time, and confirm again interactively before shredding begins.

Verification complete. The source drive or image can now be securely erased.
Target for shredding: /dev/sda (476.9 GiB)
WARNING: This will PERMANENTLY AND IRRECOVERABLY destroy all data on /dev/sda.
The content now exists only on the 6 USB drives.
Ensure all drives are physically secured before proceeding.
Type SHRED to overwrite with random data, or press Ctrl-C to skip: _

Shred procedure:

  1. Open the source block device or file for writing.
  2. Stream random bytes from the OS CSPRNG (/dev/urandom via getrandom(2)) over the entire device in 4 MiB blocks, reporting progress.
  3. Call fsync(2) after each block to ensure writes reach the storage medium.
  4. On completion, call ioctl(BLKFLSBUF) to flush the kernel block cache.
  5. Report final status with bytes written and time elapsed.
Shredding /dev/sda...
████████████████████████████ 100% 476.9 GiB / 476.9 GiB
Time elapsed: 14m 32s
Bytes written: 512,110,190,592
Shred complete. The source has been overwritten with random data.

Notes on shred effectiveness:

  • On HDDs: a single overwrite pass with random data is sufficient to prevent software-based recovery.
  • On SSDs and flash storage: wear levelling may preserve some data in overprovisioned cells. The tool prints a warning for detected SSDs and recommends ATA Secure Erase or NVMe Format after the overwrite.
  • On image files: the file is overwritten in place then unlinked. The tool warns that filesystem journalling or snapshots on the host may retain copies.

9. Checkpoint and Resume

If assembly is interrupted mid-write, a journal file is written to the target disk tracking which chunks have been fully written, the byte offset of the last confirmed write, and a BLAKE3 hash of each written segment.

On next boot, if a partial journal is detected, the agent offers to resume rather than restart. Resume requires the same k drives to be re-inserted (key material is not cached between boots).


10. Boot Image Build System (splitdisk-image)

A dedicated Rust binary, splitdisk-image, is responsible for producing the bootable per-drive USB image. It is invoked internally by splitdisk-create during enrollment (step 6a of §8.4) and can also be run standalone for testing. No external shell scripts, dracut, mkinitcpio, or manually maintained cpio archives are required; the entire boot image is assembled from Rust.

10.1 Design Goals

  • Pure Rust toolchain: the image builder is a normal Rust binary that runs on the enrollment machine (Linux x86-64). It does not shell out to system tools beyond the minimum required for block device I/O.
  • Reproducible output: given the same kernel blob and splitdisk-assemble binary, the output image is byte-for-byte identical across runs (except for the per-drive random UUID and share data).
  • Self-contained: the splitdisk-assemble binary embedded in the initramfs is statically linked against musl libc (x86_64-unknown-linux-musl) and requires no shared libraries in the initramfs environment.

10.2 Components Produced

splitdisk-image produces a raw disk image for a single share USB drive, containing:

ComponentHow produced
GPT partition tableWritten directly using the gpt crate
EFI System Partition (FAT32, ~256 MiB)Constructed using the fatfs crate; contains GRUB EFI binary and GRUB config
GRUB EFI binaryBundled as a compiled-in byte slice (fetched at build time via build.rs from a pinned upstream release, verified by SHA-256 → BLAKE3 of the blob)
Linux kernel (bzImage)Bundled as a compiled-in byte slice (pinned version, verified)
initramfs (cpio.gz)Assembled entirely in Rust; see §10.3
System partition (ext4)Written using the ext4-rs or equivalent crate
Share dataWritten to system partition by splitdisk-create after image base is laid down

10.3 initramfs Assembly

The initramfs is a gzip-compressed cpio archive assembled entirely in Rust using the cpio crate (or equivalent). It contains the minimum set of entries required for splitdisk-assemble to run:

/init → shell script or compiled init stub invoking
splitdisk-assemble
/usr/bin/splitdisk-assemble → statically linked, stripped Rust binary
(x86_64-unknown-linux-musl)
/usr/lib/pcsc/drivers/ → CCID driver shared objects (bundled)
/etc/reader.conf.d/ → pcscd reader config
/dev/ → empty; populated by the kernel at boot
/proc/ → empty; mounted by init
/sys/ → empty; mounted by init
/tmp/ → empty tmpfs mount point

pcscd is invoked as a subprocess by splitdisk-assemble at runtime (or linked statically if a suitable no_std/musl build is available). The CCID driver .so files are bundled as byte slices extracted at build time from a pinned upstream release and verified by BLAKE3 hash before embedding.

The init entry is a minimal compiled Rust binary (part of the splitdisk-image crate's output) that mounts /proc, /sys, and a tmpfs on /tmp, then exec's splitdisk-assemble. It does not use a shell.

10.4 GRUB Configuration

GRUB is configured with:

set timeout=0
set default=0
menuentry "SplitDisk" {
linux /boot/vmlinuz quiet loglevel=0 rd.udev.log_level=0
initrd /boot/initramfs.img
}

No menu is shown. The kernel command line suppresses all console output until splitdisk-assemble takes control of the terminal for its TUI.

10.5 Kernel Configuration

The bundled kernel is built from a minimal defconfig plus the following additions, compiled and pinned as a byte slice in build.rs:

Config optionReason
CONFIG_USB_SUPPORTUSB host stack
CONFIG_USB_XHCI_HCDUSB 3.x host controller
CONFIG_USB_EHCI_HCDUSB 2.0 host controller
CONFIG_USB_STORAGEUSB mass storage (share drives)
CONFIG_USB_SERIALUSB serial (CCID fallback)
CONFIG_USB_CHIPIDEACCID smartcard transport
CONFIG_PCMCIASmartcard reader support
CONFIG_SCSIRequired for USB storage
CONFIG_EXT4_FSRead system partition
CONFIG_VFAT_FSRead EFI partition
CONFIG_TMPFS/tmp for runtime state
CONFIG_PROC_FS/proc
CONFIG_SYSFS/sys
CONFIG_TTYTerminal for TUI
CONFIG_VTVirtual terminal
CONFIG_FBFramebuffer (TUI rendering)
CONFIG_DRMGPU-neutral display
CONFIG_CRYPTO_CHACHA20Kernel-side not required; userspace only

Network, sound, Bluetooth, Wi-Fi, and all non-essential drivers are disabled. The resulting bzImage is expected to be well under 10 MiB.

10.6 Build Reproducibility and Pinning

All bundled blobs (GRUB EFI binary, Linux kernel bzImage, CCID driver .so files) are:

  1. Fetched at crate build time by build.rs from pinned upstream URLs.
  2. Verified against a BLAKE3 hash embedded in the source tree.
  3. Embedded as include_bytes!() byte slices in the Rust binary.

This means splitdisk-image has no runtime dependency on GRUB, a kernel build system, or any external initramfs tooling on the enrollment machine. A fresh cargo build on any Linux x86-64 host with network access and the Rust stable toolchain produces a functional splitdisk-image binary.

10.7 Standalone Usage

# Produce a base USB image (no share data yet) for testing:
splitdisk-image \
--output /tmp/base.img \
--size 512MiB
# Inspect the produced image:
fdisk -l /tmp/base.img

splitdisk-create calls splitdisk-image internally and then writes share data into the system partition of the produced image before flashing to the target USB device.


11. Workspace Layout

splitdisk/
├── AGENTS.md (AI contributor guidelines; normative with README)
├── CLAUDE.md (pointer to AGENTS.md for Claude Code)
├── Cargo.toml (workspace)
├── .cursor/rules/ (Cursor IDE rules, split *.mdc files)
├── crates/
│ ├── splitdisk-create/ (enrollment tool)
│ ├── splitdisk-assemble/ (initramfs agent)
│ ├── splitdisk-image/ (boot image builder; see §10)
│ ├── splitdisk-core/ (shared: crypto, RS, SSS wrappers, metadata format)
│ ├── splitdisk-auth/ (PIN, biometric, Galdralag token integration)
│ └── splitdisk-tui/ (ratatui UI components)
└── docs/
├── SPEC.md (this document)
├── GLOSSARY.md (plain-language term definitions)
├── CRYPTO.md (detailed cryptographic rationale)
└── GALDRALAG.md (Galdralag integration guide)

12. Galdralag Integration Summary

When --galdralag is passed to splitdisk-create, each member receives only a Galdralag Baochip-1x token. No separate USB drive is issued. The token is the complete share — it holds both the encrypted data chunk (on SD card) and the key share (in RRAM vault).

AspectUSB drive modeGaldralag token mode
What the member carriesUSB driveGaldralag token only
Data chunk storageUSB drive partitionToken SD card
Key share storageEncrypted in USB hidden partitionOn-device RRAM vault
PIN enforcementSoftware attempt counterHardware PIN counter with zeroization
BiometricUSB iris scanner + softwareOptional; token PIN suffices
Encryption of key share in transitHKDF-BLAKE3 + ChaCha20-Poly1305BrainpoolP384r1 ECDH on-device
Forward secrecyNot applicableAuthenticated ephemeral ECDH session
TRNGOS CSPRNGHardware TRNG on Baochip-1x
SD card requiredNoYes (for data chunk storage)
Mixed mode with USB sharesN/AYes — both types can coexist in one scheme

13. Security Considerations

  • Enrollment machine must be air-gapped and have its memory securely erased after creating drives. splitdisk-create calls zeroize on all key material, but physical RAM remanence is a risk.
  • Drive firmware attacks: an adversary with physical access to a drive before it reaches its holder could modify the bootloader or initramfs. Mitigations: tamper-evident packaging, and optionally signing the initramfs with Ed25519 (matching the pattern used by Galdralag firmware's boot0 verification).
  • Rubber hose or 5$ hammer: a holder can be coerced into providing their PIN. The k-of-n threshold ensures one holder's coercion is insufficient for reconstruction. The holder genuinely does not know how many others exist.
  • Share count leakage: the progress bar reveals how many shares have been collected, which after k successes reveals k to an observer watching the screen. This is an accepted trade-off; the total n remains hidden.
  • Post-quantum: FrodoKEM-1344 is feature-gated and marked experimental. Enable only when an independently audited no_std Rust crate is available, following the same policy as Galdralag firmware and CESS §7.3.
  • Biometric template privacy: templates are stored only on the individual's own drive, encrypted at rest. No operator or other member can access another member's biometric data.
  • Boot image integrity: all bundled blobs in splitdisk-image are BLAKE3- verified at build time (§10.6). Implementers SHOULD verify the BLAKE3 hashes of pinned blobs out-of-band before shipping drives.

14. Out of Scope

  • Network-based share distribution or retrieval
  • Cloud key management
  • Windows or macOS host support for splitdisk-create (Linux only; assembly runs on the embedded Linux in initramfs on any x86-64 machine)
  • WebAuthn / FIDO2
  • VeraCrypt or LUKS integration (SplitDisk manages its own encryption layer)

15. CESS Conformance and Suite Identifiers

SplitDisk targets CESS-FULL conformance (CESS v0.2 ). The following table maps --cipher + --bulk-cipher combinations to their CESS suite_id values. Suite IDs are used internally and, in Mode A, are transmitted inside the authenticated outer ChaCha20-Poly1305 envelope only — never in cleartext.

--cipher--bulk-cipherCESS suite_idNotes
brainpool384chacha200x0001CESS-CORE default
brainpool384serpent0x0002
brainpool384cascade-cs0x0003CESS default cascade
brainpool384twofish0x0004
brainpool384cascade-ct0x0005
brainpool384cascade-ts0x0006
brainpool384cascade-cst0x0007Triple cascade
brainpool512chacha200x0011
brainpool512serpent0x0012
brainpool512cascade-cs0x0013
brainpool512twofish0x0014
brainpool512cascade-ct0x0015
brainpool512cascade-ts0x0016
brainpool512cascade-cst0x0017Triple cascade
brainpool256chacha20N/A (Mode B only)Extension; outside CESS-FULL normative profile

Note: suite_id values above are provisional pending final assignment in ALGORITHM-REGISTRY.md. The lookup table in that file is authoritative.


16. Dependencies Summary

# Core cryptochacha20poly1305 = "0.10"serpent = "0.1"# Serpent-256 CTRtwofish = "0.1"# Twofish-256 CTRpoly1305 = "0.8"# MAC for CTR-mode ciphershkdf = "0.12"argon2 = "0.5"blake3 = "1"zeroize = { version = "1", features = ["derive"] }
subtle = "2"# Curvesp384 = "0.13"# BrainpoolP384r1 / BrainpoolP512r1# Key splittingvsss-rs = "3"# Shamir, GF(2⁸)# Data splittingreed-solomon-erasure = "6"# Boot image builder (splitdisk-image crate only)gpt = "3"# GPT partition table constructionfatfs = "0.3"# FAT32 EFI partition constructioncpio = "0.2"# initramfs cpio archive assemblyflate2 = "1"# gzip compression for initramfs# TUIratatui = "0.26"# USB / block device I/Onix = "0.29"rusb = "0.9"# USB device enumeration# OpenPGP (optional GPG envelope)sequoia-openpgp = "1"# Post-quantum (feature-gated, experimental)pqcrypto-frodo = { version = "0.1", optional = true } # FrodoKEM-1344

This specification is a design document. No warranty of correctness is made. Cryptographic choices and security decisions should be reviewed by a qualified cryptographer before implementation or deployment.

About

No description or website provided.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors