ewf-image
A pure-Rust library for reading and writing Expert Witness Format (EWF) forensic images.

ewf-image reads and writes the EWF/E01 image formats used in digital
forensics — the physical, logical, SMART, and EWF2 families — with no unsafe
code and no external tool dependencies. It exposes focused APIs for raw stream
access, metadata inspection, logical single-file catalogs, and EWF output
creation.
Highlights
- Broad format coverage. Reads and writes EWF1 (
.E01,.L01,.S01) and EWF2 (.Ex01,.Lx01), including raw, zlib, BZip2, and pattern-fill chunks. The EWF1 reader also supports X-Ways Forensics 20.9+ Zstandard images and password-protected X-Ways AES-128/AES-256 images. - Streaming reads. Immutable
Imagehandles offer positioned reads,Read + Seekcursors, and bounded decoded-chunk caching. - Rich metadata. Inspect acquisition headers, stored MD5/SHA1 hashes, acquisition errors, sessions, and tracks.
- Integrity verification. Recompute and compare stored hashes with a single
Image::verify()call, or compute SHA256 and compare external references with progress, cancellation, and optional parallel decompression. - Analysis and recovery. Typed integrity findings, bounded reports, section inspection, and a separate physical raw/zlib EWF1 recovery API with provenance.
- Positioned backings. Open EWF1/EWF2 segments from files, memory, bounded subranges, or caller-provided thread-safe positioned sources.
- Flexible writing. Compression, segment splitting, secondary/shadow mirroring, authored metadata, and resume-by-rewrite.
- Safe by construction.
#![forbid(unsafe_code)], linted under clippy pedantic/nursery, and tested against external EWF tool oracles.
This release ships the Rust library. Command-line, mount, and service runtime layers are planned but not yet implemented.
Installation
[]
= "0.4"
The default verify feature enables streamed MD5/SHA1 verification through
Image::verify(). Drop it if you don't need that API:
[]
= { = "0.4", = false }
Quick Start
Open an image, inspect its format, and read from the logical media stream:
use Read;
let image = open?;
let info = image.info;
println!;
// Read the first sector from the cursor...
let mut first_sector = vec!;
image.cursor.read_exact?;
// ...or read directly at any offset.
let mut sector_at_offset = vec!;
image.read_at?;
Open a password-protected X-Ways EWF1 image by keeping password bytes in the
zeroizing EwfPassword wrapper:
let password = utf8;
let image = open_with_password?;
if let Some = image.encryption_info
EwfPassword::from_bytes is available when the password must be supplied in
an encoding other than UTF-8. X-Ways AES-128 accepts at most 16 password bytes;
AES-256 accepts at most 32.
Each open attempt accepts one password; callers own candidate iteration.
Password and derived-key storage is zeroized where owned, and public diagnostics
do not include cryptographic material. AES-CTR is not authenticated encryption:
a stored verifier confirms the password, while verifier-less images rely on
strict validation of the first decrypted media chunk during open. Existing EWF
checksums and Image::verify() remain the integrity mechanisms for later data.
Read forensic metadata and verify stored hashes:
let image = open?;
if let Some = image.header_value
let result = image.verify?;
println!;
Reader tuning and diagnostics
Reader caches are shared by every clone and cursor created from an Image.
The decoded-chunk cache defaults to 64 chunks, and table entries use a bounded
4 MiB page cache. Configure byte limits and opt into cumulative diagnostics
with OpenOptions:
let options = default
.with_chunk_cache_size_bytes
.with_table_entry_cache_size_bytes
.with_maximum_open_handles
.with_reader_statistics;
let image = open_with_options?;
let before = image.reader_statistics.expect;
// Perform the reads being measured.
let mut sector = ;
image.read_at?;
let delta = image
.reader_statistics
.expect
.saturating_delta;
println!;
let cache = image.reader_cache_info;
println!;
Statistics collection is disabled by default. reader_statistics() returns
None unless it was enabled when the image was opened. See
Migrating to 0.2 for the OpenOptions builder
migration.
Verification and analysis
Image::verify_with_options computes MD5, SHA1, and SHA256 and accepts external
reference digests through VerifyOptions. verify_with_progress supports
per-operation cancellation. Verification always validates backing chunks,
including when ordinary reads permit zero-filling damaged data.
Image::analyze collects typed findings and reports incomplete media coverage
without producing partial-stream hashes. EwfRecovery separately exports
recoverable physical raw/zlib EWF1 data to a new raw file, recording primary,
redundant-table, suspect, and zero-filled outcomes.
The optional parallel feature enables bounded parallel scans; serde enables
report serialization. Defaults remain single-threaded. See
Verification, analysis, and recovery for APIs,
cancellation, source backings, memory budgets, and recovery limits.
Write a new compressed EWF2 image from raw bytes:
use File;
let mut input = open?;
let mut options = default;
options.format = Ewf2Physical;
options.compression = Zlib;
options.metadata.set_header_value;
let mut writer = create?;
copy?;
let result = writer.finish?;
println!;
See examples/ for complete, runnable programs, including reading,
raw export, logical inspection, and mirrored secondary output.
Supported Formats
| Family | Read | Write | Notes |
|---|---|---|---|
EWF1 physical .E01 / EVF |
✓ | ✓ | Segment discovery, raw/zlib chunks, metadata, hashes, acquisition errors, sessions, tracks, and split output. Reading additionally supports the X-Ways 20.9+ Zstandard profile and X-Ways AES-128/AES-256 encryption. |
EWF1 logical .L01 / LVF |
✓ | ✓ | Logical single-file catalogs and path lookup. |
EWF1 SMART .S01 |
✓ | ✓ | SMART media profile handling. |
EWF2 physical .Ex01 |
✓ | ✓ | Raw, zlib, BZip2, and pattern-fill chunks; EWF2 metadata, memory extents, and split output. |
EWF2 logical .Lx01 |
✓ | ✓ | Logical single-file catalogs and auxiliary single-file tables. |
The writer additionally supports SMART output, compression, segment splitting, secondary/shadow mirroring, authored metadata, stored hashes, incomplete EWF1 output, and resume-by-rewrite.
Limitations
- Encrypted EWF2 images are detected and rejected; decryption is not yet implemented.
- Encrypted writing is not yet implemented.
- Base-plus-overlay delta/shadow images are not yet implemented.
- X-Ways Zstandard EWF1 images are read-only; the writer does not generate or preserve X-Ways Zstandard chunks.
- EWF2 BZip2 chunks are supported locally, but some external EWF tools cannot generate or export BZip2 fixtures, so external oracle coverage for them is tracked separately.
See docs/limitations.md for details.
Testing
Compatibility is covered by local synthetic fixtures, writer round trips, and optional external EWF tool oracle tests. The routine checks are:
RUSTDOCFLAGS="-D warnings"
External corpus and tool oracle checks are ignored by default, since they require local fixtures and installed EWF tools. See docs/testing.md and docs/compatibility.md.
Documentation
- Architecture
- Compatibility
- Limitations
- Testing
- Verification, analysis, and recovery
- Migrating to 0.3
- Migrating to 0.2
- API reference (docs.rs)
Contributing
Contributions are welcome. See CONTRIBUTING.md to get started, and SECURITY.md for reporting security issues.
License
Licensed under the Apache License, Version 2.0. See LICENSE.