diff --git a/CHANGELOG.md b/CHANGELOG.md index c16eba2..8dbcd0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,94 +1,120 @@ # Changelog -All notable changes to this project will be documented in this file. +All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.5] - 2026-07-29 + +### Changed +- Rewrote `README.md`, `SECURITY.md`, and `CHANGELOG.md` in ASD-STE100 + strict mode. Removed marketing language, passive voice, and long + sentences. +- Fixed `README.md` inaccuracies. The "Supported Types" section now lists + the `[u8; N]` impls for `EncodeInto` and `TryDecodeFrom`. The "Error + Types" section now lists `InsufficientData` and `InvalidEncoding`. The + test count now reads 155. +- Fixed `SECURITY.md` inaccuracies. The "Bounded Operations" table now + notes that `EncodeInto` for `[u8; N]` is unbounded. The "Version + History" section now shows 1.0.5 as the current version. The + best-practices examples no longer reference nonexistent error variants. +- Updated `README.md` install version from `1.0` to `1.1`. + +## [1.0.4] - 2026-07-16 + +### Security +- Removed the unmaintained `serde_cbor` dev-dependency. This addresses + RUSTSEC-2021-0127. + +### Changed +- Removed the redundant `unsafe impl Send/Sync for EncodedBytes`. The + type gets `Send` and `Sync` from its `Vec` field. The crate + `#![deny(unsafe_code)]` policy now holds without exception. +- Updated `SECURITY.md` to state the zero-unsafe claim correctly. + ## [1.0.3] - 2026-07-15 ### Added -- **`#[inline]`** on hot encode/decode paths: `EncodeInto` impls for all - integer types and `bool`, `TryDecodeFrom` impls for all integer types, - `bool`, and fixed-length arrays. -- **MSRV declared**: `rust-version = "1.85"` in `Cargo.toml`. CI verifies the - MSRV with a dedicated job. -- **`cargo audit`** job in CI. -- **`cargo fmt --check`** and **`clippy -D warnings`** steps in CI. -- **`no_std` build verification** job in CI. -- **Clippy lint configuration**: `[lints.clippy]` with `pedantic`, `nursery`, - and `cargo` groups (all `warn`), plus `[lints.rust] unsafe_code = "deny"`. -- **Length-bounds documentation** on the `TryDecodeFrom` trait noting that - integer decoders rely on `unsigned-varint`'s type-specific byte limits and - that callers decoding length-prefixed payloads should enforce their own - upper bound. +- `#[inline]` on hot encode and decode paths. This covers `EncodeInto` + impls for all integer types and `bool`. It covers `TryDecodeFrom` + impls for all integer types, `bool`, and fixed-length arrays. +- MSRV declared as `rust-version = "1.85"` in `Cargo.toml`. CI verifies + the MSRV with a dedicated job. +- `cargo audit` job in CI. +- `cargo fmt --check` and `clippy -D warnings` steps in CI. +- `no_std` build verification job in CI. +- Clippy lint configuration. `[lints.clippy]` sets `pedantic`, `nursery`, + and `cargo` groups to `warn`. `[lints.rust]` sets `unsafe_code` to + `deny`. +- Length-bounds documentation on the `TryDecodeFrom` trait. The doc notes + that integer decoders rely on `unsigned-varint` byte limits. It advises + callers that decode length-prefixed payloads to set their own upper + bound. ### Changed -- **Edition 2024**: Updated from Rust 2021. -- **Clippy pedantic/nursery/cargo warnings** resolved across all source, - examples, and benchmarks. +- Edition updated to 2024 from 2021. +- Clippy pedantic, nursery, and cargo warnings resolved across all + source, examples, and benchmarks. ## [1.0.0] - 2026-07-13 ### Changed -- Synced from bettersign workspace (bs-multitrait 0.7.0) -- Renamed crate from `bs-multitrait` to `multi-trait` -- Initial published release on crates.io as `multi-trait` +- Synced from the bettersign workspace (`bs-multitrait` 0.7.0). +- Renamed the crate from `bs-multitrait` to `multi-trait`. +- Initial published release on crates.io as `multi-trait`. ## [1.0.1] - 2024-08-27 ### Fixed -- Fixed `no_std` build by adding proper `alloc` imports to all modules -- Fixed error handling in `no_std` mode by conditionally compiling `UnsignedVarintDecode` error variant -- Removed unused imports identified by clippy +- Fixed the `no_std` build. Added `alloc` imports to all modules. +- Fixed error handling in `no_std` mode. The `UnsignedVarintDecode` + error variant is now conditionally compiled. +- Removed unused imports found by clippy. ### Changed -- Improved code quality with clippy pedantic lints -- Enhanced code readability by adding numeric literal separators (e.g., `100_000` instead of `100000`) -- Improved documentation formatting with proper backticks for code items -- Updated bool assertions from `assert_eq!(val, true)` to `assert!(val)` for better idiomatic code -- Optimized loop patterns from indexing to iterators where applicable +- Improved code quality with clippy pedantic lints. +- Added numeric literal separators for readability. +- Changed `assert_eq!(val, true)` to `assert!(val)` for idiomatic code. +- Changed indexing loops to iterators where applicable. ### Documentation -- Enhanced documentation with better formatting -- Added comprehensive error handling examples -- Improved module-level documentation across all traits -- Verified all documentation links work correctly +- Improved documentation formatting. +- Added error handling examples. +- Improved module-level documentation across all traits. ## [bs-multitrait 1.0.0] - 2024-04-14 ### Added -- Initial stable release (as `bs-multitrait`, the bettersign workspace name) -- Core encoding traits: `EncodeInto`, `EncodeIntoBuffer`, `EncodeIntoArray` -- Core decoding trait: `TryDecodeFrom` -- Null value traits: `Null` and `TryNull` -- Validated newtype: `EncodedBytes` for type-safe varint-encoded byte sequences -- Comprehensive error handling with `Error` enum -- Full `no_std` support with `alloc` -- Thread-safe implementations (Send + Sync) -- Extensive test suite with 105 tests including: - - Unit tests for all traits - - Property-based tests with proptest - - Security tests for malicious input handling - - Concurrency tests for multi-threaded usage - - Round-trip encoding/decoding tests +- Initial stable release as `bs-multitrait`. +- Core encoding traits: `EncodeInto`, `EncodeIntoBuffer`, + `EncodeIntoArray`. +- Core decoding trait: `TryDecodeFrom`. +- Null value traits: `Null` and `TryNull`. +- Validated newtype: `EncodedBytes` for type-safe varint-encoded byte + sequences. +- Error handling with the `Error` enum. +- Full `no_std` support with `alloc`. +- Thread-safe implementations (`Send + Sync`). +- Test suite with 105 tests: unit, property-based, security, + concurrency, and round-trip tests. ### Performance -- Zero-copy decoding with slice references -- Zero-allocation buffer encoding -- Stack-only encoding for embedded systems -- Single-allocation encoding for heap operations -- Optimized varint format for space efficiency +- Zero-copy decoding with slice references. +- Zero-allocation buffer encoding. +- Stack-only encoding for embedded systems. +- Single-allocation encoding for heap operations. ### Documentation -- Comprehensive module and trait documentation -- Usage examples for all public APIs -- Performance characteristics documentation -- Thread safety guarantees documented -- Security considerations documented +- Module and trait documentation. +- Usage examples for all public APIs. +- Performance characteristics documentation. +- Thread safety documentation. +[1.0.5]: https://github.com/cryptidtech/multi-trait/compare/v1.0.4...v1.0.5 +[1.0.4]: https://github.com/cryptidtech/multi-trait/compare/v1.0.3...v1.0.4 [1.0.3]: https://github.com/cryptidtech/multi-trait/compare/v1.0.1...v1.0.3 [1.0.0]: https://github.com/cryptidtech/multi-trait/releases/tag/v1.0.0 [1.0.1]: https://github.com/cryptidtech/multi-trait/compare/v1.0.0...v1.0.1 diff --git a/Cargo.toml b/Cargo.toml index 777e87e..5188a2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "multi-trait" -version = "1.0.4" +version = "1.0.5" edition = "2024" rust-version = "1.85" authors = ["Dave Grantham "] diff --git a/README.md b/README.md index 0599aa0..1296da0 100644 --- a/README.md +++ b/README.md @@ -5,46 +5,50 @@ # multi-trait -A lightweight, high-performance Rust library providing common traits for implementing [multiformats](https://github.com/multiformats/multiformats) types with zero-copy decoding and flexible encoding strategies. +Common traits for multiformats types in Rust. The crate gives encoding, +decoding, and null value traits with zero-copy decoding and `no_std` support. ## Features -- **๐Ÿš€ High Performance**: Optimized varint encoding with minimal allocations -- **๐Ÿ“ฆ Zero-Copy Decoding**: Parse data without unnecessary copying -- **๐ŸŽฏ Type Safety**: Validated newtypes for compile-time guarantees -- **๐Ÿ”ง Flexible Encoding**: Three encoding strategies for different use cases -- **๐ŸŒ no_std Support**: Works in embedded and constrained environments -- **๐Ÿงต Thread-Safe**: All traits are `Send + Sync` safe -- **๐Ÿ“ Well-Documented**: Comprehensive documentation with examples -- **โœ… Thoroughly Tested**: 150+ tests including property-based and concurrency tests +- Varint encoding with minimal allocations. +- Zero-copy decoding. `TryDecodeFrom` returns the remaining bytes. +- Zero-allocation encoding. `EncodeIntoBuffer` reuses an existing buffer. +- Stack-based encoding. `EncodeIntoArray` for `no_std` and embedded systems. +- `no_std` support with `alloc`. +- Validated newtype. `EncodedBytes` gives type-level guarantees. +- `#![deny(unsafe_code)]` set at the crate root. +- All types are `Send + Sync`. +- 155 tests: unit, property-based, security, concurrency, and round-trip. -## Installation +## Install Add this to your `Cargo.toml`: ```toml [dependencies] -multi-trait = "1.0" +multi-trait = "1.1" ``` For `no_std` environments: ```toml [dependencies] -multi-trait = { version = "1.0", default-features = false } +multi-trait = { version = "1.1", default-features = false } ``` +MSRV: Rust 1.85 (Edition 2024). + ## Quick Start ```rust use multi_trait::{EncodeInto, TryDecodeFrom}; -// Encoding: Convert a value to compact varint bytes +// Encode a value to varint bytes let value = 42u32; let encoded = value.encode_into(); println!("Encoded {} as {:?}", value, encoded); -// Decoding: Parse bytes back to original value +// Decode the bytes back to the value let (decoded, remaining) = u32::try_decode_from(&encoded).unwrap(); assert_eq!(decoded, value); assert!(remaining.is_empty()); @@ -56,35 +60,36 @@ assert!(remaining.is_empty()); #### `EncodeInto` -Encode values into a compact varint `Vec`. Best for one-off encoding operations. +Encode a value into a varint `Vec`. Use this for one-off encoding. ```rust use multi_trait::EncodeInto; let value = 1000u16; -let bytes = value.encode_into(); // Allocates new Vec +let bytes = value.encode_into(); // Allocates a new Vec ``` #### `EncodeIntoBuffer` -Zero-allocation encoding into an existing buffer. Best for encoding multiple values or hot paths. +Encode values into an existing buffer with no allocation. Use this in hot +paths or when you encode multiple values. ```rust use multi_trait::EncodeIntoBuffer; let mut buffer = Vec::with_capacity(100); -// Encode multiple values with minimal allocations 42u8.encode_into_buffer(&mut buffer); 1000u16.encode_into_buffer(&mut buffer); -100000u32.encode_into_buffer(&mut buffer); +100_000u32.encode_into_buffer(&mut buffer); println!("Encoded {} bytes total", buffer.len()); ``` #### `EncodeIntoArray` -Stack-based encoding for `no_std` environments. Returns a fixed-size array with the actual length. +Encode a value into a stack-allocated array. Use this in `no_std` or +real-time systems. ```rust use multi_trait::EncodeIntoArray; @@ -92,7 +97,6 @@ use multi_trait::EncodeIntoArray; let (array, len) = 42u8.encode_into_array(); assert_eq!(&array[..len], &[42]); -// Maximum sizes known at compile time assert_eq!(::MAX_ENCODED_SIZE, 5); ``` @@ -100,7 +104,8 @@ assert_eq!(::MAX_ENCODED_SIZE, 5); #### `TryDecodeFrom` -Fallibly decode values from byte slices with zero-copy semantics. Returns the decoded value and remaining unconsumed bytes. +Decode a value from a byte slice. Returns the value and the remaining bytes. +No allocation occurs. ```rust use multi_trait::TryDecodeFrom; @@ -110,7 +115,7 @@ let (value, remaining) = u16::try_decode_from(&bytes).unwrap(); assert_eq!(value, 65535); assert!(remaining.is_empty()); -// Sequential decoding from one buffer +// Decode multiple values from one buffer let bytes = vec![0x01, 0x02, 0x03]; let (first, rest) = u8::try_decode_from(&bytes).unwrap(); let (second, rest) = u8::try_decode_from(rest).unwrap(); @@ -122,7 +127,7 @@ assert_eq!((first, second, third), (1, 2, 3)); #### `Null` -Define and check for null/sentinel values. +Define and check for a null or sentinel value. ```rust use multi_trait::Null; @@ -148,7 +153,7 @@ assert!(!valid_id.is_null()); #### `TryNull` -Fallible version of `Null` for types requiring validation. +Fallible version of `Null`. Use it for types that need validation. ```rust use multi_trait::TryNull; @@ -172,29 +177,27 @@ impl TryNull for ValidatedId { #### `EncodedBytes` -A validated newtype for varint-encoded byte sequences. Provides compile-time guarantees that bytes represent valid encodings. +A validated newtype for varint-encoded byte sequences. Construction checks +that the bytes are a valid encoding. ```rust use multi_trait::EncodedBytes; -// Validation happens at construction let valid = vec![42u8]; let encoded = EncodedBytes::try_from(valid).unwrap(); -// Invalid data is rejected let invalid = vec![0x80]; // Truncated varint assert!(EncodedBytes::try_from(invalid).is_err()); -// Type system ensures valid data fn process_encoded(data: EncodedBytes) { - // No need to validate - type guarantees validity + // The type guarantees the data is valid println!("Processing {} bytes", data.len()); } ``` ## Error Handling -All decode operations return a `Result` with a structured `Error` type: +All decode operations return `Result` with a structured `Error` type: ```rust use multi_trait::{TryDecodeFrom, Error}; @@ -202,8 +205,8 @@ use multi_trait::{TryDecodeFrom, Error}; let truncated = vec![0xFF]; // Incomplete varint match u16::try_decode_from(&truncated) { Ok((value, _)) => println!("Decoded: {}", value), - Err(Error::UnsignedVarintDecode { source }) => { - eprintln!("Decode failed: {}", source); + Err(Error::UnsignedVarintDecode { .. }) => { + eprintln!("Decode failed"); } Err(e) => eprintln!("Other error: {}", e), } @@ -211,97 +214,67 @@ match u16::try_decode_from(&truncated) { ### Error Types -- `Error::UnsignedVarintDecode`: Varint decoding failed (truncated data, invalid encoding, etc.) +The `Error` enum is `#[non_exhaustive]`. It has these variants: + +- `UnsignedVarintDecode`: Varint decoding failed. The cause can be + truncated data or an invalid encoding. +- `InsufficientData`: The input slice does not have enough bytes to decode + the requested type. +- `InvalidEncoding`: The data is structurally invalid. This variant is + for future use and custom validation. -All errors include source chains for debugging and support backtraces when the `std` feature is enabled. +All errors give source chains for debugging. Backtraces are available when +the `std` feature is on. ## Performance Guide ### Encoding Performance -Choose the right encoding strategy for your use case: - -1. **`EncodeInto`** - Good for one-off encodings - - Single allocation per call - - Simple API - - Use when encoding individual values +Pick the encoding strategy for your use case: -2. **`EncodeIntoBuffer`** - Best for multiple values - - Zero allocations when buffer has capacity - - Reusable buffer - - Use in hot paths or when encoding multiple values - -3. **`EncodeIntoArray`** - Best for embedded systems - - Zero heap allocations (stack only) - - Deterministic performance - - Use in `no_std` or real-time systems +1. `EncodeInto` โ€” One allocation per call. Use this for one-off encodings. +2. `EncodeIntoBuffer` โ€” Zero allocations when the buffer has capacity. Use + this in hot paths or when you encode multiple values. +3. `EncodeIntoArray` โ€” Zero heap allocations. Use this in `no_std` or + real-time systems. ### Decoding Performance -- **Zero allocations**: Returns slice references to existing data -- **Zero copying**: No data duplication during decode -- **Constant-time validation**: Efficient varint format checking +- Zero allocations. Returns slice references to the input data. +- No data copy during decode. +- Efficient varint format checking. -### Benchmark Results +### Encoded Sizes -Varint encoding is highly efficient: -- Values 0-127: 1 byte -- Values 128-16,383: 2 bytes -- Values 16,384-2,097,151: 3 bytes -- And so on... +Varint encoding uses 1 to 10 bytes for integers. The size depends on the +value. -Maximum encoded sizes: -- `u8`, `bool`: 2 bytes max -- `u16`: 3 bytes max -- `u32`: 5 bytes max -- `u64`, `usize` (64-bit): 10 bytes max -- `u128`: 19 bytes max +- Values 0 to 127: 1 byte. +- Values 128 to 16,383: 2 bytes. +- Values 16,384 to 2,097,151: 3 bytes. -## Thread Safety +Maximum encoded sizes by type: -All traits and types in this crate are `Send + Sync`, making them safe to use in concurrent contexts. +- `u8`, `bool`: 2 bytes. +- `u16`: 3 bytes. +- `u32`: 5 bytes. +- `u64`, `usize` (64-bit): 10 bytes. +- `u128`: 19 bytes. -### Concurrency Patterns - -All these patterns work safely: - -```rust -use std::sync::Arc; -use std::thread; -use multi_trait::{EncodeInto, TryDecodeFrom}; +## Thread Safety -// Parallel encoding -let handles: Vec<_> = (0..10) - .map(|i| { - thread::spawn(move || { - let value = i as u32 * 100; - value.encode_into() - }) - }) - .collect(); - -// Shared read access -let data = Arc::new(vec![42u8, 100, 200]); -let handles: Vec<_> = (0..4) - .map(|_| { - let data = Arc::clone(&data); - thread::spawn(move || { - let (value, _) = u8::try_decode_from(&data).unwrap(); - value - }) - }) - .collect(); -``` +All traits and types in this crate are `Send + Sync`. You can use them in +concurrent contexts. -All operations are lock-free with no shared mutable state. +All operations are lock-free. No mutable state is shared. -## no_std Support +## `no_std` Support -This crate works in `no_std` environments with `alloc`: +The crate works in `no_std` environments with `alloc`: ```toml [dependencies] -multi-trait = { version = "1.0", default-features = false } +multi-trait = { version = "1.1", default-features = false } ``` Use `EncodeIntoArray` for heap-free encoding in embedded systems: @@ -313,60 +286,63 @@ extern crate alloc; use alloc::vec::Vec; use multi_trait::EncodeIntoArray; -// Stack-only encoding (no heap required for encoding) let (array, len) = 42u8.encode_into_array(); -// Only allocate if you need to store it let vec = Vec::from(&array[..len]); ``` ## Feature Flags -- **`std`** (default): Enables standard library support - - Enables `std::error::Error` implementation - - Enables backtrace support in errors - - Disable for `no_std`: `default-features = false` +- `std` (default). Enables standard library support. It enables + `std::error::Error` implementation and backtrace support in errors. + Disable it for `no_std` with `default-features = false`. The crate needs + `alloc` when `std` is off. ## Supported Types All traits are implemented for: -- `bool`: Encoded as 0 (false) or 1 (true) -- `u8`, `u16`, `u32`, `u64`, `u128`: Variable-length encoding -- `usize`: Platform-dependent (32-bit or 64-bit) + +- `bool`: Encoded as 0 (false) or 1 (true). +- `u8`, `u16`, `u32`, `u64`, `u128`: Variable-length varint encoding. +- `usize`: Platform-dependent (32-bit or 64-bit). +- `[u8; N]`: Fixed-length byte arrays. `EncodeInto` encodes the raw bytes + without a varint prefix. `TryDecodeFrom` reads exactly N bytes. Use this + for BLS share identifiers and other fixed-size binary data. + +`EncodeIntoBuffer` and `EncodeIntoArray` are implemented for `bool`, +`u8`, `u16`, `u32`, `u64`, `u128`, and `usize`. They do not support +`[u8; N]`. ## Examples -See the [`examples/`](examples/) directory for complete examples: +See the `examples/` directory for complete examples: -- [`basic.rs`](examples/basic.rs) - Basic encoding and decoding -- [`error_handling.rs`](examples/error_handling.rs) - Error handling patterns -- [`custom_type.rs`](examples/custom_type.rs) - Implementing traits for custom types -- [`no_std.rs`](examples/no_std.rs) - Using the crate in no_std environments +- `basic.rs` โ€” Basic encoding and decoding. +- `error_handling.rs` โ€” Error handling patterns. +- `custom_type.rs` โ€” Implement the traits for custom types. +- `no_std.rs` โ€” Use the crate in `no_std` environments. + +Run an example: -Run examples with: ```bash cargo run --example basic -cargo run --example error_handling -cargo run --example custom_type ``` ## Testing -The crate includes 150+ tests: -- Unit tests for all trait implementations -- Property-based tests with proptest -- Concurrency tests for thread safety -- Security tests for malicious inputs -- Edge case tests +The crate has 155 tests: unit, property-based, concurrency, security, and +edge case tests. + +Run all tests: -Run tests with: ```bash cargo test ``` ## Documentation -Generate and view the full API documentation: +Generate and view the API documentation: + ```bash cargo doc --open ``` @@ -377,11 +353,12 @@ Licensed under Apache-2.0. See [LICENSE](LICENSE) for details. ## Contributing -Contributions are welcome! Please ensure: -- All tests pass (`cargo test`) -- Code is formatted (`cargo fmt`) -- No clippy warnings (`cargo clippy`) -- New features include tests and documentation +Contributions are welcome. Before you submit a change, make sure: + +- All tests pass (`cargo test`). +- The code is formatted (`cargo fmt`). +- No clippy warnings (`cargo clippy`). +- New features include tests and documentation. ## Links @@ -392,4 +369,4 @@ Contributions are welcome! Please ensure: [CRYPTID]: https://cryptid.tech/ [PROVENANCE]: https://github.com/cryptidtech/provenance-specifications/ -[MULTIFORMATS]: https://github.com/multiformats/multiformats/ +[MULTIFORMATS]: https://github.com/multiformats/multiformats/ \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index 8f70335..2837ea4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,264 +2,271 @@ ## Overview -The `multi-trait` crate provides foundational traits for encoding and decoding multiformats types using unsigned varint encoding. This document outlines the security properties, threat model, and guarantees of this crate. +The `multi-trait` crate gives foundational traits for encoding and decoding +multiformats types. It uses unsigned varint encoding. This document describes +the security properties and the threat model of the crate. ## Security Properties ### Memory Safety -- **No `unsafe` code**: The crate enforces `#![deny(unsafe_code)]` at the crate root, relying entirely on safe Rust abstractions -- **No buffer overflows**: All buffer operations use safe indexing and slicing -- **No use-after-free**: Lifetimes ensure references remain valid -- **No data races**: All types are `Send + Sync` with no mutable shared state +- No `unsafe` code. `#![deny(unsafe_code)]` is set at the crate root. The + crate uses only safe Rust abstractions. +- No buffer overflows. All buffer operations use safe indexing and slicing. +- No use-after-free. Lifetimes keep references valid. +- No data races. All types are `Send + Sync` with no shared mutable state. ### Input Validation -- **Malformed data rejection**: Invalid varint encodings are detected and rejected with proper errors -- **Truncated data detection**: Incomplete varints are rejected before processing -- **Boundary checking**: All operations validate buffer boundaries before access -- **Type-level validation**: `EncodedBytes` newtype ensures data validity at the type level +- Malformed data rejection. Invalid varint encodings are detected and + rejected with an error. +- Truncated data detection. Incomplete varints are rejected before + processing. +- Boundary checking. All operations check buffer boundaries before access. +- Type-level validation. `EncodedBytes` gives data validity at the type + level. -### Denial of Service (DoS) Resistance +### Denial of Service Resistance #### Protected Against -- **Excessive memory allocation**: Encoding uses bounded allocations (max 19 bytes for u128) -- **Stack overflow**: No recursive operations; all encoding/decoding is iterative -- **Infinite loops**: All loops have deterministic bounds based on input size -- **Panic-based DoS**: Comprehensive testing ensures no panics on malicious input +- Excessive memory allocation for integer encoding. Integer encoding uses + at most 19 bytes (for `u128`). +- Stack overflow. No recursive operations. All encoding and decoding is + iterative. +- Infinite loops. All loops have deterministic bounds set by the input + size. +- Panic-based DoS. Tests check that no panic occurs on malicious input. #### Bounded Operations | Operation | Maximum Size | Notes | |-----------|--------------|-------| -| `EncodeInto` | 19 bytes | Maximum varint size for u128 | -| `EncodeIntoArray` | 19 bytes | Stack-allocated, no heap | -| `TryDecodeFrom` | Input-dependent | Zero allocation, bounded by input | -| `EncodedBytes` | Arbitrary | Validated at construction | +| `EncodeInto` for integers | 19 bytes | Maximum varint size for `u128` | +| `EncodeInto` for `[u8; N]` | N bytes | Raw byte copy. Not a varint. | +| `EncodeIntoArray` | 19 bytes | Stack-allocated. No heap. | +| `EncodeIntoBuffer` | 19 bytes per integer | Appends to an existing buffer. | +| `TryDecodeFrom` for integers | Input-dependent | Zero allocation. Bounded by input. | +| `TryDecodeFrom` for `[u8; N]` | N bytes | Reads exactly N bytes from the input. | +| `EncodedBytes` | Arbitrary | Validated at construction. | ### Integer Overflow Protection -- **No arithmetic overflow**: All operations delegate to `unsigned-varint` crate which handles overflow -- **Type-safe conversions**: Decoding validates that values fit in target type -- **Maximum value testing**: Comprehensive tests for `TYPE::MAX` values +- No arithmetic overflow. All operations delegate to the `unsigned-varint` + crate. It handles overflow. +- Type-safe conversions. Decoding checks that values fit in the target + type. +- Maximum value testing. Tests cover `TYPE::MAX` values for all types. ## Threat Model ### In Scope -This crate is designed to handle: +The crate handles: -1. **Untrusted input data** from network sources, files, or user input -2. **Maliciously crafted varint sequences** designed to trigger bugs -3. **Extreme values** at type boundaries (0, MAX) -4. **Truncated or incomplete data** from interrupted transmissions -5. **Concurrent access** from multiple threads +1. Untrusted input data from network sources, files, or user input. +2. Maliciously crafted varint sequences. +3. Extreme values at type boundaries (0 and MAX). +4. Truncated or incomplete data from interrupted transmissions. +5. Concurrent access from multiple threads. ### Out of Scope -This crate does NOT protect against: +The crate does not protect against: -1. **Side-channel attacks**: No constant-time guarantees; timing may leak information -2. **Cryptographic security**: This is an encoding library, not a crypto library -3. **Resource exhaustion**: Callers must implement rate limiting and quotas -4. **Semantic validation**: Callers must validate that decoded values are semantically correct -5. **Physical attacks**: No protection against hardware-level attacks +1. Side-channel attacks. No constant-time guarantees. Timing may leak + information. +2. Cryptographic security. This is an encoding library, not a crypto + library. +3. Resource exhaustion. Callers must set rate limits and quotas. +4. Semantic validation. Callers must check that decoded values are + semantically correct. +5. Physical attacks. No protection against hardware-level attacks. ## Security Guarantees -### What We Guarantee +### What the Crate Guarantees -โœ… **No panics on invalid input**: All error conditions return `Result` -โœ… **No buffer overflows**: All indexing is bounds-checked -โœ… **No undefined behavior**: `#![deny(unsafe_code)]` forbids unsafe code -โœ… **Deterministic behavior**: Same input always produces same output -โœ… **Error transparency**: All errors provide source chains for debugging -โœ… **Type safety**: Invalid states are unrepresentable +- No panics on invalid input. All error conditions return `Result`. +- No buffer overflows. All indexing is bounds-checked. +- No undefined behavior. `#![deny(unsafe_code)]` forbids unsafe code. +- Deterministic behavior. The same input always gives the same output. +- Error transparency. All errors give source chains for debugging. +- Type safety. Invalid states are not representable. -### What We Do NOT Guarantee +### What the Crate Does Not Guarantee -โŒ **Constant-time operations**: Encoding/decoding time may vary with input -โŒ **Resource limits**: Callers must enforce quotas -โŒ **Side-channel resistance**: Not designed for cryptographic use -โŒ **Backward compatibility with bugs**: Security fixes may break buggy code +- Constant-time operations. Encoding and decoding time may vary with the + input. +- Resource limits. Callers must set quotas. +- Side-channel resistance. The crate is not for cryptographic use. +- Backward compatibility with bugs. Security fixes may break buggy code. ## Dependencies -This crate depends on: +The crate depends on: -- **`unsigned-varint` (v0.8)**: Provides the underlying varint encoding/decoding -- **`thiserror` (v2.0)**: Error type derivation -- **`proptest` (v1.4)**: Property-based testing (dev dependency) -- **`criterion` (v0.5)**: Benchmarking (dev dependency) +- `unsigned-varint` (v0.8). Gives the varint encoding and decoding. +- `thiserror` (v2.0). Error type derivation. + +Dev dependencies: + +- `proptest` (v1.4). Property-based testing. +- `criterion` (v0.5). Benchmarking. ### Dependency Security -- The `unsigned-varint` crate is widely used in the IPFS/libp2p ecosystem -- We rely on its security properties for varint parsing -- Any security issues in `unsigned-varint` affect this crate +- The `unsigned-varint` crate is used in the IPFS and libp2p ecosystem. +- This crate relies on its security properties for varint parsing. +- Any security issue in `unsigned-varint` affects this crate. ## Vulnerability Reporting -### Reporting a Vulnerability +### Report a Vulnerability -If you discover a security vulnerability in this crate, please report it responsibly: +If you find a security vulnerability in this crate, report it as follows: -1. **Do NOT open a public GitHub issue** for security vulnerabilities -2. **Email the maintainers** at the address in `Cargo.toml` -3. **Include**: - - Description of the vulnerability - - Steps to reproduce - - Potential impact - - Suggested fix (if any) +1. Do not open a public GitHub issue. +2. Email the maintainers at the address in `Cargo.toml`. +3. Include a description of the vulnerability. +4. Include steps to reproduce it. +5. Include the potential impact. +6. Include a suggested fix if you have one. ### Response Timeline -- **Initial response**: Within 48 hours -- **Vulnerability assessment**: Within 1 week -- **Fix development**: Depends on severity -- **Public disclosure**: After fix is released (coordinated disclosure) - -### Severity Classification - -#### Critical (CVSS 9.0-10.0) -- Remote code execution -- Memory corruption -- Data exfiltration - -#### High (CVSS 7.0-8.9) -- Denial of service (crash) -- Information disclosure -- Authentication bypass - -#### Medium (CVSS 4.0-6.9) -- Denial of service (resource exhaustion) -- Logic errors affecting correctness - -#### Low (CVSS 0.1-3.9) -- Minor issues with limited impact +- Initial response: within 48 hours. +- Vulnerability assessment: within 1 week. +- Fix development: depends on severity. +- Public disclosure: after the fix is released. Coordinated disclosure. ## Security Testing ### Test Coverage -The crate includes comprehensive security tests: +The crate has these security tests: -- **Malicious input tests**: 13 targeted tests for attack vectors -- **Fuzzing-style property tests**: 6 property tests with random inputs -- **Edge case tests**: Boundary conditions, max values, empty inputs -- **Regression tests**: All discovered bugs have test coverage +- 13 malicious input tests for attack vectors. +- 6 property tests with random inputs. +- Edge case tests for boundary conditions, max values, and empty inputs. +- Regression tests for all discovered bugs. -### Running Security Tests +### Run Security Tests ```bash -# Run all tests including security tests +# Run all tests cargo test # Run only security tests cargo test security_ -# Run property-based tests with more cases +# Run property tests with more cases cargo test -- --ignored --test-threads=1 ``` ### Continuous Testing All pull requests must pass: -- โœ… All unit tests -- โœ… All integration tests -- โœ… All property-based tests -- โœ… Clippy with no warnings -- โœ… Format check + +- All unit tests. +- All integration tests. +- All property-based tests. +- Clippy with no warnings. +- Format check. ## Security Assumptions ### Assumptions About Callers -This crate assumes callers will: +The crate assumes that callers will: -1. **Validate decoded values**: Check that decoded values make sense semantically -2. **Implement resource limits**: Enforce quotas on input size and decode operations -3. **Handle errors properly**: Don't ignore or unwrap Results in production code -4. **Use appropriate types**: Choose the smallest type that can represent your data +1. Validate decoded values. Check that decoded values make sense. +2. Set resource limits. Enforce quotas on input size and decode operations. +3. Handle errors. Do not ignore or unwrap `Result` in production code. +4. Use the right types. Pick the smallest type that can hold the data. -### Assumptions About Environment +### Assumptions About the Environment -1. **Rust standard library is correct**: We trust std/core/alloc -2. **Dependencies are secure**: We trust unsigned-varint and thiserror -3. **Compiler is correct**: We trust rustc and LLVM -4. **Platform is not compromised**: No hardware-level attacks +1. The Rust standard library is correct. +2. The dependencies are secure. +3. The compiler is correct. +4. The platform is not compromised. ## Best Practices ### For Users of This Crate -1. **Always handle errors**: Never unwrap in production code - ```rust - // BAD - let (value, _) = u32::try_decode_from(untrusted).unwrap(); - - // GOOD - let (value, _) = u32::try_decode_from(untrusted)?; - ``` - -2. **Validate semantic correctness**: Type checking isn't enough - ```rust - let (port, _) = u16::try_decode_from(data)?; - if port == 0 { - return Err(Error::InvalidPort); - } - ``` - -3. **Limit input size**: Prevent resource exhaustion - ```rust - if input.len() > MAX_MESSAGE_SIZE { - return Err(Error::TooLarge); - } - ``` - -4. **Use validated types**: Prefer `EncodedBytes` for pre-validated data - ```rust - fn process(data: EncodedBytes) { - // Data is guaranteed valid, no re-validation needed - } - ``` +1. Always handle errors. Do not unwrap in production code. + +```rust +// Bad +let (value, _) = u32::try_decode_from(untrusted).unwrap(); + +// Good +let (value, _) = u32::try_decode_from(untrusted)?; +``` + +2. Validate semantic correctness. Type checking is not enough. + +```rust +let (port, _) = u16::try_decode_from(data)?; +if port == 0 { + return Err("port cannot be zero"); +} +``` + +3. Limit input size. Prevent resource exhaustion. + +```rust +if input.len() > MAX_MESSAGE_SIZE { + return Err("input too large"); +} +``` + +4. Use validated types. Use `EncodedBytes` for pre-validated data. + +```rust +fn process(data: EncodedBytes) { + // The data is valid. No re-validation needed. +} +``` ### For Contributors -1. **Never use `unsafe`**: This crate enforces `#![deny(unsafe_code)]` by policy -2. **Add tests for new features**: Include security test cases -3. **Document security implications**: Explain potential misuse -4. **Consider DoS vectors**: Every new feature should be analyzed for DoS potential +1. Do not use `unsafe`. The crate enforces `#![deny(unsafe_code)]`. +2. Add tests for new features. Include security test cases. +3. Document security implications. Explain potential misuse. +4. Consider DoS vectors. Analyze every new feature for DoS potential. ## Version History ### Security-Relevant Changes -#### v1.0.1 (Current) -- Added `EncodeIntoBuffer` and `EncodeIntoArray` traits (Phase 5) -- Enhanced security test coverage (Phase 6) -- No known vulnerabilities +#### v1.0.5 (Current) +- Rewrote documentation in ASD-STE100 strict mode. +- Fixed inaccuracies in `README.md` and `SECURITY.md`. +- No known vulnerabilities. + +#### v1.0.4 +- Removed the unmaintained `serde_cbor` dev-dependency. +- Removed the redundant `unsafe impl Send/Sync` for `EncodedBytes`. +- No known vulnerabilities. + +#### v1.0.3 +- Added `#![deny(unsafe_code)]` and clippy lint configuration. +- Added MSRV declaration and CI jobs for audit, fmt, and clippy. +- No known vulnerabilities. #### v1.0.0 -- Initial production release -- Comprehensive validation in `EncodedBytes` -- Full test coverage -- No known vulnerabilities +- Initial production release. +- Validation in `EncodedBytes`. +- No known vulnerabilities. ## Audit Status -This crate has NOT undergone a professional security audit. Use in security-critical applications should be preceded by appropriate review. +This crate has not had a professional security audit. Review the code +before you use it in security-critical applications. ## License -Security issues are handled under the same Apache-2.0 license as the crate itself. - -## Acknowledgments - -Security testing and review by the community is welcomed and appreciated. Thank you to all contributors who help keep this crate secure. - ---- - -**Last Updated**: 2025-10-08 -**Document Version**: 1.0 +Security issues are handled under the same Apache-2.0 license as the crate. \ No newline at end of file