{"id":51595153,"url":"https://github.com/securityronin/luks-forensic","last_synced_at":"2026-07-16T23:01:14.408Z","repository":{"id":370768695,"uuid":"1296619589","full_name":"SecurityRonin/luks-forensic","owner":"SecurityRonin","description":"LUKS forensic library — parse LUKS1/LUKS2 headers, derive the master key, and decrypt the payload. Panic-free, no unsafe, cryptsetup-validated.","archived":false,"fork":false,"pushed_at":"2026-07-10T18:12:27.000Z","size":78,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-10T22:05:49.296Z","etag":null,"topics":["cryptography","cryptsetup","dfir","digital-forensics","dm-crypt","encryption","forensics","incident-response","luks","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SecurityRonin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-10T15:01:01.000Z","updated_at":"2026-07-10T18:14:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/SecurityRonin/luks-forensic","commit_stats":null,"previous_names":["securityronin/luks-forensic"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/SecurityRonin/luks-forensic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fluks-forensic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fluks-forensic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fluks-forensic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fluks-forensic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SecurityRonin","download_url":"https://codeload.github.com/SecurityRonin/luks-forensic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SecurityRonin%2Fluks-forensic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35370428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptography","cryptsetup","dfir","digital-forensics","dm-crypt","encryption","forensics","incident-response","luks","rust"],"created_at":"2026-07-11T18:01:34.917Z","updated_at":"2026-07-11T18:01:36.625Z","avatar_url":"https://github.com/SecurityRonin.png","language":"Rust","funding_links":["https://github.com/sponsors/h4x0r"],"categories":[],"sub_categories":[],"readme":"# luks-forensic\n\n[![Crates.io: luks-core](https://img.shields.io/crates/v/luks-core.svg?label=luks-core)](https://crates.io/crates/luks-core)\n[![Crates.io: luks-forensic](https://img.shields.io/crates/v/luks-forensic.svg?label=luks-forensic)](https://crates.io/crates/luks-forensic)\n[![Docs.rs](https://img.shields.io/docsrs/luks-core?label=docs.rs)](https://docs.rs/luks-core)\n[![Rust 1.81+](https://img.shields.io/badge/rust-1.81%2B-blue.svg)](https://www.rust-lang.org)\n[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n[![Sponsor](https://img.shields.io/badge/sponsor-h4x0r-ea4aaa?logo=githubsponsors)](https://github.com/sponsors/h4x0r)\n\n[![CI](https://github.com/SecurityRonin/luks-forensic/actions/workflows/ci.yml/badge.svg)](https://github.com/SecurityRonin/luks-forensic/actions/workflows/ci.yml)\n[![Coverage](https://img.shields.io/badge/coverage-100%25%20lines-brightgreen.svg)](docs/validation.md)\n[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance)\n[![Security advisories](https://img.shields.io/badge/advisories-clean-success.svg)](https://rustsec.org)\n\n**Unlock a LUKS container from its passphrase and read the plaintext — a\nfrom-scratch, pure-Rust LUKS1/LUKS2 decryptor, validated sector-for-sector\nagainst `cryptsetup` on real containers.**\n\nNo `cryptsetup` C dependency, no `dm-crypt`, no mounting, no root: one library\nthat parses the on-disk header, derives the master key from a passphrase through\nPBKDF2 or Argon2, and decrypts sectors with AES-XTS.\n\n```rust,ignore\nuse std::fs::File;\nuse luks::LuksVolume;\n\n// Auto-detects LUKS1 vs LUKS2 from the header.\nlet mut vol = LuksVolume::unlock_with_passphrase(File::open(\"container.luks\")?, b\"luks-TEST\")?;\n\nlet mut first = [0u8; 512];\nvol.read_at(0, \u0026mut first)?;     // decrypted payload sector 0\n# Ok::\u003c(), Box\u003cdyn std::error::Error\u003e\u003e(())\n```\n\n## Scope\n\nThis build parses **LUKS1** partition headers and **LUKS2** binary-header + JSON\nmetadata, and unlocks both from a passphrase over the `aes-xts-plain64` cipher\n(AES-128/256-XTS) — the `cryptsetup` default. LUKS1 keyslots derive with\n**PBKDF2**; LUKS2 keyslots derive with **PBKDF2 or Argon2i/Argon2id**. Both format\nversions are validated against a `cryptsetup` 2.7.0 oracle:\n\n| Version | Cipher | KDF | Oracle (tier) |\n|---|---|---|---|\n| LUKS1 | `aes-xts-plain64` (AES-256-XTS) | PBKDF2-sha256 | self-minted `luks1.img` vs `cryptsetup` (Tier-2) |\n| LUKS2 | `aes-xts-plain64`, 4096-B sectors | Argon2id | self-minted `luks2.img` vs `cryptsetup` (Tier-2) |\n\nAn unsupported cipher/mode/hash is **recognized and refused with a named error**\n(the offending value verbatim) — never decrypted by construction. See\n[`docs/RESEARCH.md`](docs/RESEARCH.md).\n\n## The two-crate split\n\nFollowing the fleet reader/analyzer standard:\n\n| Crate | Role | Emits |\n|---|---|---|\n| **`luks-core`** | reader / decryptor (`pbkdf2` · `argon2` · `aes` · `xts-mode` · `hmac` · `sha2`) | plaintext `Read + Seek` view + typed header metadata |\n| **`luks-forensic`** | anomaly analyzer over the header | severity-graded findings |\n\n### Analyzer findings\n\n| Code | Severity | Meaning |\n|---|---|---|\n| `LUKS-WEAK-CIPHER-MODE` | Low | cipher mode is `cbc`/`ecb` (weaker than `xts-plain64`) |\n| `LUKS-WEAK-KDF-HASH` | Low | the KDF/AF hash is `sha1` |\n| `LUKS-LOW-KDF-ITERATIONS` | Medium | an active keyslot has \u003c 1000 PBKDF2 iterations (brute-force risk) |\n| `LUKS-KEYSLOT-INVENTORY` | Info | count of active keyslots (of 8) |\n\nFindings are **observations, never verdicts** — the examiner draws conclusions.\n\n## Trust but verify\n\n- **Every cryptographic primitive is an audited RustCrypto crate** (`pbkdf2`,\n  `argon2`, `aes`, `xts-mode`, `hmac`, `sha1`, `sha2`) — nothing hand-rolled. The\n  only bespoke routine is the LUKS **anti-forensic merge** (the TKS1 splitter),\n  validated **only** against the independent `cryptsetup` oracle on real\n  containers, never a self-authored round-trip.\n- **Panic-free, bounds-checked** parsing of untrusted containers;\n  `unwrap`/`expect` denied in production code (`#![forbid(unsafe_code)]`); the\n  LUKS1/LUKS2 header parsers, the AF-merge, and the full unlock pipeline are\n  fuzzed.\n- **Tier-2 validated**: decrypted sectors match `cryptsetup` byte-for-byte across\n  LUKS1 and LUKS2 — see [`docs/validation.md`](docs/validation.md).\n\n[Privacy Policy](https://securityronin.github.io/luks-forensic/privacy/) · [Terms of Service](https://securityronin.github.io/luks-forensic/terms/) · © 2026 Security Ronin Ltd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fluks-forensic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecurityronin%2Fluks-forensic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecurityronin%2Fluks-forensic/lists"}