diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d26f6..16113c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,101 +1,133 @@ # 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.4] - 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 stale note that said the published + version is `1.0.1` is removed. The install version now reads `1.1`. + The test count now reads 142. +- Fixed `SECURITY.md` inaccuracies. The security changelog now lists + 1.0.2 and 1.0.3 changes. The stale "Last Updated" date is corrected. + The reference to "the multibase crate" in the reporting section now + says "the `multi-base` crate". + +## [1.0.3] - 2026-07-16 + +### Security +- Removed the unmaintained `serde_cbor` dev-dependency. This addresses + RUSTSEC-2021-0127. + +### Changed +- Updated `SECURITY.md` to document the inline `Base256Emoji` + implementation. The stale reference to the dropped `base256emoji` + dependency is corrected. +- Updated `SECURITY.md` code examples to use `multi_base::` instead of + the old `multibase::` module path. + ## [1.0.2] - 2026-07-15 ### Added -- **`#![deny(unsafe_code)]`** at the crate root. The library contains no - `unsafe` code; this lint enforces that invariant at compile time. -- **`#[inline]`** on hot encode/decode paths: `encode`, `decode`, - `encode_into`, `decode_into`, `encode_to_validated`, `parse_encoded`, - `Base::from_code`, `Base::code`, `Base::encode`, `Base::decode`, - `Base::decode_into`, `EncodedString::base`, `EncodedString::as_str`, - `EncodedString::decode`, `EncodedString::decode_with_strictness`, and the - macro-generated `BaseCodec` implementations. -- **`const fn`**: `Base::from_code`, `Base::code`, and `EncodedString::base` - are now `const fn`. -- **`#[must_use]`** on `EncodedString::base`, `as_str`, and `into_inner`. -- **`# Errors`** doc sections on `decode()` and `Base::from_code`. -- **`# Panics`** doc section on `encode_to_validated()`. -- **MSRV declared**: `rust-version = "1.85"` in `Cargo.toml`. CI verifies the - MSRV with a dedicated job. -- **`cargo audit`** job in CI. -- **Clippy lint configuration**: `[lints.clippy]` with `pedantic`, `nursery`, - and `cargo` groups (all `warn`), plus `[lints.rust] unsafe_code = "deny"`. +- `#![deny(unsafe_code)]` at the crate root. The library has no `unsafe` + code. This lint enforces that at compile time. +- `#[inline]` on hot encode and decode paths. This covers `encode`, + `decode`, `encode_into`, `decode_into`, `encode_to_validated`, + `parse_encoded`, `Base::from_code`, `Base::code`, `Base::encode`, + `Base::decode`, `Base::decode_into`, `EncodedString::base`, + `EncodedString::as_str`, `EncodedString::decode`, + `EncodedString::decode_with_strictness`, and the macro-generated + `BaseCodec` implementations. +- `const fn` on `Base::from_code`, `Base::code`, and + `EncodedString::base`. +- `#[must_use]` on `EncodedString::base`, `as_str`, and `into_inner`. +- `# Errors` doc sections on `decode()` and `Base::from_code`. +- `# Panics` doc section on `encode_to_validated()`. +- MSRV declared as `rust-version = "1.85"` in `Cargo.toml`. CI verifies + the MSRV with a dedicated job. +- `cargo audit` job in CI. +- Clippy lint configuration. `[lints.clippy]` sets `pedantic`, + `nursery`, and `cargo` groups to `warn`. `[lints.rust]` sets + `unsafe_code` to `deny`. ### Changed -- **Edition 2024**: Updated from Rust 2021. Required escaping the now-reserved - `gen` keyword (`rng.r#gen()`) in benchmarks. -- **CLI fixed**: `cli/Cargo.toml` and `cli/main.rs` updated from the legacy - `multibase` dependency name to `multi-base`. The CLI now builds. -- **Fuzz targets fixed**: `fuzz/Cargo.toml` and fuzz targets updated from the - legacy `multibase` name to `multi-base`, with edition 2024 and lint config. -- **`Error::eq`**: Merged identical match arms for unit variants into a single - pattern. -- **Doc comments**: Backticked `rfc4648` in base-variant doc comments. +- Edition updated to 2024 from 2021. The `gen` keyword is now reserved. + Benchmarks use `rng.r#gen()`. +- CLI fixed. `cli/Cargo.toml` and `cli/main.rs` updated from the + legacy `multibase` dependency name to `multi-base`. +- Fuzz targets fixed. `fuzz/Cargo.toml` and fuzz targets updated from + the legacy `multibase` name to `multi-base`. Edition 2024 and lint + config added. +- `Error::eq` merged identical match arms into a single pattern. +- Backticked `rfc4648` in base-variant doc comments. ## [1.0.1] - 2026-07-13 ### Changed -- Synced from bettersign workspace (bs-multibase 0.7.0). -- Renamed crate from `bs-multibase` to `multi-base`. -- Reimplemented `Base256Emoji` inline (dropped external `base256emoji` dep). +- Synced from the bettersign workspace (`bs-multibase` 0.7.0). +- Renamed the crate from `bs-multibase` to `multi-base`. +- Reimplemented `Base256Emoji` inline. Dropped the external + `base256emoji` dependency. - Initial published release on crates.io as `multi-base`. ### Added -- **Zero-copy APIs**: `encode_into()` and `decode_into()` for buffer reuse. -- **`encode()` optimization**: Pre-allocates exact capacity instead of +- Zero-copy APIs: `encode_into()` and `decode_into()` for buffer reuse. +- `encode()` optimization. Pre-allocates exact capacity instead of `insert(0, char)`. -- **`EncodedString` newtype**: Validated multibase-encoded string type - implementing `FromStr`, `TryFrom`, `TryFrom<&str>`, `AsRef`, - `Display`. Provides `decode()` and `decode_with_strictness()` methods. -- **Convenience functions**: `encode_to_validated()` and `parse_encoded()`. -- **Property-based tests**: 16 proptest cases for round-trip, determinism, and - buffer-reuse invariants. -- **Error handling tests**: Coverage for all base types. -- **Concurrency tests**: Parallel operation verification. -- **Benchmarks**: Enhanced benchmarking for all base types and sizes. -- **Macro documentation**: Comprehensive docs for `build_base_enum`, - `derive_base_encoding`, and `derive_base_x` macros. -- **Macro hygiene**: `$crate::` prefixes for proper hygiene. -- **Clap v4 migration**: CLI migrated from structopt 0.3 to clap v4. -- **CLI deduplication**: Unified Base↔string mappings with a macro. -- **CLI error messages**: Context added to all error paths; unknown bases list - all available options. -- **Security tests**: 17 tests covering large inputs, malformed input, - buffer-reuse safety, concurrent operations, and resource exhaustion. -- **Fuzzing infrastructure**: `cargo-fuzz` with `fuzz_decode`, `fuzz_encode`, - and `fuzz_roundtrip` targets. -- **SECURITY.md**: Security review findings, best practices, input size limit - recommendations, and vulnerability reporting process. -- **CONCURRENCY.md**: Thread safety guarantees, safe concurrent usage patterns, - and performance considerations. -- **Thread safety tests**: 20 tests with compile-time Send/Sync assertions and - a 2000-operation stress test. +- `EncodedString` newtype. A validated multibase-encoded string type. + Implements `FromStr`, `TryFrom`, `TryFrom<&str>`, `AsRef`, + and `Display`. Gives `decode()` and `decode_with_strictness()` + methods. +- Convenience functions: `encode_to_validated()` and `parse_encoded()`. +- 16 property-based tests for round-trip, determinism, and buffer-reuse + invariants. +- Error handling tests for all base types. +- Concurrency tests for parallel operation verification. +- Enhanced benchmarks for all base types and sizes. +- Macro documentation for `build_base_enum`, `derive_base_encoding`, + and `derive_base_x` macros. +- Macro hygiene with `$crate::` prefixes. +- CLI migrated from structopt 0.3 to clap v4. +- CLI deduplication. Unified Base and string mappings with a macro. +- CLI error messages. Context added to all error paths. Unknown bases + list all available options. +- 17 security tests: large inputs, malformed input, buffer-reuse + safety, concurrent operations, and resource exhaustion. +- Fuzzing infrastructure with `cargo-fuzz`. Targets: `fuzz_decode`, + `fuzz_encode`, and `fuzz_roundtrip`. +- `SECURITY.md` with security review results, best practices, and + vulnerability reporting process. +- `CONCURRENCY.md` with thread safety guarantees and concurrent usage + patterns. +- 20 thread safety tests with compile-time `Send` and `Sync` assertions + and a 2000-operation stress test. ### Changed (1.0.1) -- **BREAKING**: Error type uses `thiserror` instead of manual implementation. - `#[non_exhaustive]` added for forward compatibility. -- **BREAKING**: Identity encoding uses lossy UTF-8 conversion instead of - panicking. Invalid UTF-8 bytes are replaced with U+FFFD and will not - round-trip perfectly. +- BREAKING: Error type uses `thiserror` instead of a manual + implementation. `#[non_exhaustive]` added for forward compatibility. +- BREAKING: Identity encoding uses lossy UTF-8 conversion instead of + panicking. Invalid UTF-8 bytes are replaced with U+FFFD. They do not + round-trip. - Updated from Rust 2018 to Rust 2021. - Added SPDX-License-Identifier headers to all source files. -- Enhanced all public item documentation with performance characteristics, +- Enhanced public item documentation with performance characteristics, usage examples, and error conditions. ### Fixed (1.0.1) -- Panic in Identity encoding on invalid UTF-8 (now uses lossy conversion). +- Panic in Identity encoding on invalid UTF-8. Now uses lossy + conversion. - O(n) string reallocation in `encode()`. - Error context lost during conversions. - Rustdoc warnings and broken links. @@ -127,11 +159,13 @@ Rust 1.85 (Edition 2024). ### Breaking Changes in 1.0.1 -1. Error type structure changed (uses `thiserror`). -2. Identity encoding no longer panics (uses lossy conversion). +1. Error type structure changed. Uses `thiserror`. +2. Identity encoding no longer panics. Uses lossy conversion. 3. `#[non_exhaustive]` added to `Error` enum. 4. Edition updated to 2021. +[1.0.4]: https://github.com/cryptidtech/multi-base/compare/v1.0.3...v1.0.4 +[1.0.3]: https://github.com/cryptidtech/multi-base/compare/v1.0.2...v1.0.3 [1.0.2]: https://github.com/cryptidtech/multi-base/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/cryptidtech/multi-base/releases/tag/v1.0.1 [1.0.0]: https://github.com/multiformats/rust-multibase/releases/tag/v1.0.0 \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 62da7f8..6089bff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,9 +61,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "bumpalo" @@ -79,9 +79,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.67" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "shlex", @@ -93,6 +93,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core 0.10.1", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -122,9 +133,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.2" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" +checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7" dependencies = [ "clap_builder", ] @@ -145,6 +156,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "criterion" version = "0.8.2" @@ -234,14 +254,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn", + "syn 2.0.119", ] [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "errno" @@ -255,9 +275,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" @@ -273,21 +293,21 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-core", "futures-task", @@ -295,17 +315,6 @@ dependencies = [ "slab", ] -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "getrandom" version = "0.3.4" @@ -327,6 +336,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", ] [[package]] @@ -368,9 +378,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -386,14 +396,14 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "multi-base" -version = "1.0.3" +version = "1.0.4" dependencies = [ "base-x", "criterion", "data-encoding", "data-encoding-macro", "proptest", - "rand 0.8.7", + "rand 0.10.2", "thiserror", ] @@ -473,9 +483,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -491,7 +501,7 @@ dependencies = [ "bitflags", "num-traits", "rand 0.9.5", - "rand_chacha 0.9.0", + "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -507,9 +517,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -526,35 +536,25 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -569,21 +569,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.3.4", ] [[package]] name = "rand_core" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_xorshift" @@ -685,9 +682,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -695,29 +692,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -749,6 +746,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -764,22 +772,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -823,12 +831,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -870,7 +872,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -947,22 +949,22 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bf1b335..6d61a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "multi-base" -version = "1.0.3" +version = "1.0.4" authors = ["Friedel Ziegelmayer ", "Dave Grantham "] edition = "2024" rust-version = "1.85" @@ -24,12 +24,16 @@ thiserror = { version = "2.0", default-features = false } [dev-dependencies] criterion = { version = "0.8", features = ["html_reports"] } proptest = "1.4" -rand = "0.8" +rand = "0.10" [lints.clippy] pedantic = { level = "warn", priority = -1 } nursery = { level = "warn", priority = -1 } cargo = { level = "warn", priority = -1 } +# `data-encoding-macro-internal` and `zerocopy-derive` pin `syn 2.x`, while +# `serde_derive` and `thiserror-impl` use `syn 3.x`. This will resolve when +# the remaining proc-macro crates upgrade to `syn 3`. +multiple_crate_versions = { level = "allow", priority = 1 } [lints.rust] unsafe_code = "deny" diff --git a/README.md b/README.md index af6fd80..f3ab451 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # multi-base -[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/made%20by-Cryptid%20Technologies-gold.svg?style=flat-square)](https://cryptid.tech/) [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) -[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) -[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) [![Build Status](https://github.com/cryptidtech/multi-base/workflows/build/badge.svg)](https://github.com/cryptidtech/multi-base/actions) [![License](https://img.shields.io/crates/l/multi-base?style=flat-square)](LICENSE) [![Crates.io](https://img.shields.io/crates/v/multi-base?style=flat-square)](https://crates.io/crates/multi-base) [![Documentation](https://docs.rs/multi-base/badge.svg?style=flat-square)](https://docs.rs/multi-base) -[![Dependency Status](https://deps.rs/repo/github/cryptidtech/multi-base/status.svg)](https://deps.rs/repo/github/cryptidtech/multi-base) [![Coverage Status](https://img.shields.io/codecov/c/github/cryptidtech/multi-base?style=flat-square)](https://codecov.io/gh/cryptidtech/multi-base) -> [multibase](https://github.com/multiformats/multibase) implementation in Rust. +A [multibase](https://github.com/multiformats/multibase) implementation in +Rust. The crate gives encoding and decoding with error handling, type safety, +and `no_std` support. -A multibase encoding/decoding library with error handling, type safety, and -`no_std` support. +The crate is published as `multi-base` on crates.io. Import it as +`multi_base` in Rust. ## Table of Contents @@ -38,43 +37,19 @@ A multibase encoding/decoding library with error handling, type safety, and ## Features -✨ **Stable** -- 142 tests (unit, integration, property-based, security, concurrency) -- Zero clippy warnings -- Thread safety verification -- `#![deny(unsafe_code)]` - -πŸš€ **Performance** -- Zero-copy buffer reuse APIs -- `#[inline]` on hot encode/decode paths -- Pre-allocated exact-capacity encoding - -πŸ”’ **Type Safety** -- Validated `EncodedString` newtype -- "Parse, don't validate" pattern -- Compile-time guarantees - -πŸ›‘οΈ **Security** -- No panics on untrusted input -- `#![deny(unsafe_code)]` enforced -- Fuzzing infrastructure -- Input validation at all boundaries - -🧡 **Thread Safe** -- All types are Send + Sync -- No interior mutability -- Verified with concurrent stress tests - -πŸ“š **Documented** -- API documentation with examples -- Security and concurrency guides -- [CHANGELOG.md](CHANGELOG.md) with migration notes - -🌐 **Flexible** -- 24 supported base encodings -- Strict and permissive decoding modes -- `no_std` support with `alloc` -- WebAssembly compatible +- 24 base encodings. +- Zero-copy buffer reuse APIs: `encode_into` and `decode_into`. +- `#[inline]` on hot encode and decode paths. +- Pre-allocated exact-capacity encoding. +- Validated `EncodedString` newtype. Parse, do not validate. +- `#![deny(unsafe_code)]` set at the crate root. +- No panics on untrusted input. +- Fuzzing infrastructure with `cargo-fuzz`. +- All types are `Send + Sync`. No interior mutability. +- `no_std` support with `alloc`. +- WebAssembly compatible. +- 142 tests: unit, integration, property-based, security, concurrency, and + documentation tests. ## Install @@ -82,20 +57,17 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -multi-base = "1.0" +multi-base = "1.1" ``` For `no_std` environments: ```toml [dependencies] -multi-base = { version = "1.0", default-features = false } +multi-base = { version = "1.1", default-features = false } ``` -> **Note:** the crate is published as `multi-base` on crates.io and imported -> as `multi_base` in Rust. The current published version is `1.0.1`. - -**MSRV**: Rust 1.85 (Edition 2024) +MSRV: Rust 1.85 (Edition 2024). ## Usage @@ -116,7 +88,8 @@ assert_eq!(data, b"hello world"); ### Buffer Reuse for Performance -When encoding/decoding multiple values, reuse buffers to avoid allocations: +When you encode or decode multiple values, reuse buffers to avoid +allocations: ```rust use multi_base::{Base, encode_into, decode_into}; @@ -125,13 +98,13 @@ let mut encode_buffer = String::new(); let mut decode_buffer = Vec::new(); for data in dataset { - // Encode into existing buffer (no allocation) + // Encode into the existing buffer. No allocation. encode_into(Base::Base64, data, &mut encode_buffer); - // Decode into existing buffer (no allocation) + // Decode into the existing buffer. No allocation. let base = decode_into(&encode_buffer, true, &mut decode_buffer)?; - // Process decoded data... + // Process the decoded data. } ``` @@ -145,7 +118,7 @@ use multi_base::{EncodedString, Base}; // Parse and validate at construction let encoded = EncodedString::new("zCn8eVZg")?; -// Base is known at compile time +// The base is known at compile time assert_eq!(encoded.base(), Base::Base58Btc); // Decode directly @@ -158,7 +131,7 @@ let encoded: EncodedString = "md29ybGQ".parse()?; ### Error Handling -The library provides comprehensive error types with context: +The crate gives error types with context: ```rust use multi_base::{decode, Error}; @@ -184,7 +157,7 @@ match decode(input, true) { ## Supported Bases -The library supports 24 base encodings: +The crate supports 24 base encodings: | Base | Code | Alphabet | |------|------|----------| @@ -215,40 +188,45 @@ The library supports 24 base encodings: ## Performance -**Encoding Performance**: Base32 and Base64 are orders of magnitude faster than other bases due to byte alignment. +Base32 and Base64 are faster than other bases. This is because of byte +alignment. + +Optimization tips: -**Optimization Tips**: -1. Use `encode_into()` and `decode_into()` for buffer reuse in loops -2. Prefer Base32 or Base64 for performance-critical applications -3. Use Base58 or Base16 when human readability is important +1. Use `encode_into()` and `decode_into()` for buffer reuse in loops. +2. Prefer Base32 or Base64 for performance-critical code. +3. Use Base58 or Base16 when human readability is important. -**Benchmarks**: Run `cargo bench` to see performance on your system. +Run `cargo bench` to see performance on your system. ## Security -- βœ… No panics on arbitrary untrusted input -- βœ… `#![deny(unsafe_code)]` enforced at compile time -- βœ… Input validation at all boundaries -- βœ… 17 dedicated security tests -- βœ… Fuzzing infrastructure with 4 targets (3 functional + 1 placeholder) +- No panics on untrusted input. +- `#![deny(unsafe_code)]` set at the crate root. +- Input validation at all boundaries. +- 17 security tests. +- Fuzzing infrastructure with 3 targets: `fuzz_decode`, `fuzz_encode`, + and `fuzz_roundtrip`. -**Best Practices**: -- For untrusted input, always use strict mode: `decode(input, true)` -- Implement application-level size limits (see [SECURITY.md](SECURITY.md)) -- For binary data preservation, avoid Identity encoding (use Base64 instead) +Best practices: -See [SECURITY.md](SECURITY.md) for detailed security information. +- For untrusted input, use strict mode: `decode(input, true)`. +- Set application-level size limits. See [SECURITY.md](SECURITY.md). +- For binary data preservation, do not use Identity encoding. Use Base64. + +See [SECURITY.md](SECURITY.md) for the full security policy. ## Concurrency -All public types are **fully thread-safe**: +All public types are thread-safe: + +- All types implement `Send + Sync`. +- No interior mutability. +- No data races. +- 20 thread safety tests verify these properties. -- βœ… All types implement `Send` + `Sync` -- βœ… No interior mutability -- βœ… No data races possible -- βœ… Verified with 20 thread safety tests +Concurrent usage: -**Concurrent Usage**: ```rust use std::sync::Arc; use std::thread; @@ -265,7 +243,7 @@ let handles: Vec<_> = (0..10) for handle in handles { let encoded = handle.join().unwrap(); - // All threads produce identical results + // All threads give the same result. } ``` @@ -273,16 +251,17 @@ See [CONCURRENCY.md](CONCURRENCY.md) for detailed concurrency information. ## CLI Tool -The crate includes a command-line tool for encoding/decoding, located in the -`cli/` directory. +The crate includes a command-line tool in the `cli/` directory. Build the CLI: + ```bash cd cli cargo build --release ``` Example usage: + ```bash # Encode data echo "hello world" | multibase encode --base base64 @@ -298,20 +277,21 @@ multibase encode --base base58btc --input "hello world" The crate has 142 tests: -- **142 tests total** (excluding ignored tests) - - 12 unit tests - - 63 integration tests - - 16 property-based tests (using proptest) - - 17 security tests - - 20 thread safety tests - - 14 documentation tests +- 12 unit tests. +- 63 integration tests. +- 16 property-based tests with proptest. +- 17 security tests. +- 20 thread safety tests. +- 14 documentation tests. Run all tests: + ```bash cargo test --all ``` Run specific test suites: + ```bash cargo test --test lib # Integration tests cargo test --test properties # Property-based tests @@ -320,11 +300,13 @@ cargo test --test thread_safety # Concurrency tests ``` Run benchmarks: + ```bash cargo bench ``` -Run fuzzing (requires cargo-fuzz): +Run fuzzing (requires `cargo-fuzz`): + ```bash cargo install cargo-fuzz cargo fuzz run fuzz_decode @@ -341,36 +323,30 @@ cargo doc --open ``` Additional documentation: -- [SECURITY.md](SECURITY.md) - Security review and best practices -- [CONCURRENCY.md](CONCURRENCY.md) - Thread safety analysis -- [CHANGELOG.md](CHANGELOG.md) - Version history and migration notes -## Maintainers +- [SECURITY.md](SECURITY.md) β€” Security review and best practices. +- [CONCURRENCY.md](CONCURRENCY.md) β€” Thread safety analysis. +- [CHANGELOG.md](CHANGELOG.md) β€” Version history and migration notes. -This Repo: [@dhuseby](https://github.com/dhuseby). +## Maintainers -Captain: [@dignifiedquire](https://github.com/dignifiedquire). +This repo: [@dhuseby](https://github.com/dhuseby). -Contributors: [@koushiro](https://github.com/koushiro), and [others](https://github.com/cryptidtech/multi-base/graphs/contributors). +Original author: [@dignifiedquire](https://github.com/dignifiedquire). ## Contribute -Contributions welcome! Please check out [the issues](https://github.com/cryptidtech/multi-base/issues). - -Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. - -Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). +Contributions are welcome. Please check out +[the issues](https://github.com/cryptidtech/multi-base/issues). ### Development Guidelines -- Run `cargo fmt` before committing -- Run `cargo clippy -- -D warnings` to check for issues -- Add tests for new features -- Update documentation for API changes -- Run full test suite: `cargo test --all` - -Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. +- Run `cargo fmt` before you commit. +- Run `cargo clippy -- -D warnings` to check for issues. +- Add tests for new features. +- Update documentation for API changes. +- Run the full test suite: `cargo test --all`. ## License -[MIT](LICENSE) Β© Friedel Ziegelmayer +[MIT](LICENSE) Β© Friedel Ziegelmayer \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index 4f34b63..3c0a220 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,62 +2,69 @@ ## Security Review -This document summarizes the security posture of the `multi-base` crate and -provides guidance for users regarding security considerations. +This document describes the security posture of the `multi-base` crate. It +gives guidance for users on security considerations. -## Security Review Findings +## Security Review Results -### βœ… No Critical Vulnerabilities Found +### No Critical Vulnerabilities Found -The codebase has been reviewed for common security issues and no critical vulnerabilities were identified. +The codebase was reviewed for common security issues. No critical +vulnerabilities were found. ### Areas Reviewed -1. **Integer Overflow** βœ“ SAFE - - All size calculations use Rust's default checked arithmetic in debug mode - - Capacity calculations for String/Vec use addition that would fail allocation before overflow - - Risk: Low - would require near-`usize::MAX` inputs, which would fail memory allocation first - -2. **Buffer Overflow/Underflow** βœ“ SAFE - - All string slicing uses `char::len_utf8()` for correct UTF-8 boundary detection - - No unsafe code or manual pointer arithmetic - - Rust's bounds checking prevents buffer overflows - - Risk: None - protected by Rust's safety guarantees - -3. **Panic Conditions (DoS Vectors)** βœ“ SAFE - - Identity encoding uses `String::from_utf8_lossy` (no panic on invalid UTF-8) - - All decoding operations return `Result` types - - Empty inputs handled with `Error::EmptyInput` - - Invalid base codes return `Error::UnknownBase` - - One documented `.expect()` in `encode_to_validated()` that cannot fail - - Risk: Minimal - library functions do not panic on arbitrary inputs - -4. **Resource Exhaustion Attacks** ⚠️ CONSIDER - - No hard limits on input size - - Large inputs (e.g., gigabytes) will consume proportional memory - - Memory allocation failures are handled by Rust's allocator - - Risk: Medium - applications should implement their own size limits if needed - - **Recommendation**: Applications processing untrusted input should enforce maximum size limits - -5. **Input Validation** βœ“ COMPLETE - - Empty strings: Validated with `Error::EmptyInput` - - Invalid base codes: Validated with `Error::UnknownBase` - - Malformed encoded data: Validated by base-specific decoders - - All validation through `Result` types - - Risk: None - input validation at all boundaries +1. Integer Overflow. Safe. + - All size calculations use checked arithmetic in debug mode. + - Capacity calculations for `String` and `Vec` use addition. The + allocation fails before overflow. + - Risk: Low. An attack would need near-`usize::MAX` inputs. Memory + allocation fails first. + +2. Buffer Overflow and Underflow. Safe. + - All string slicing uses `char::len_utf8()` for UTF-8 boundary + detection. + - No unsafe code or manual pointer arithmetic. + - Rust bounds checking prevents buffer overflows. + - Risk: None. Protected by Rust safety guarantees. + +3. Panic Conditions. Safe. + - Identity encoding uses `String::from_utf8_lossy`. No panic on + invalid UTF-8. + - All decode operations return `Result`. + - Empty inputs return `Error::EmptyInput`. + - Invalid base codes return `Error::UnknownBase`. + - One `.expect()` in `encode_to_validated()`. It cannot fail in + practice. + - Risk: Minimal. Library functions do not panic on arbitrary input. + +4. Resource Exhaustion Attacks. Consider. + - No hard limits on input size. + - Large inputs consume proportional memory. + - Memory allocation failures are handled by the Rust allocator. + - Risk: Medium. Applications should set their own size limits. + - Recommendation: Applications that process untrusted input should + enforce a maximum size limit. + +5. Input Validation. Complete. + - Empty strings return `Error::EmptyInput`. + - Invalid base codes return `Error::UnknownBase`. + - Malformed encoded data is rejected by base-specific decoders. + - All validation goes through `Result` types. + - Risk: None. Input validation at all boundaries. ## Security Best Practices for Users ### 1. Input Size Limits -For applications processing untrusted input, consider enforcing maximum size limits: +For applications that process untrusted input, set a maximum size limit: ```rust const MAX_INPUT_SIZE: usize = 10 * 1024 * 1024; // 10 MB fn safe_decode(input: &str) -> Result<(Base, Vec), Error> { if input.len() > MAX_INPUT_SIZE { - return Err(Error::InvalidBaseString); // or custom error + return Err(Error::InvalidBaseString); } multi_base::decode(input, true) } @@ -65,7 +72,7 @@ fn safe_decode(input: &str) -> Result<(Base, Vec), Error> { ### 2. Error Handling -Always handle errors properly and avoid exposing detailed error messages to untrusted parties: +Handle errors. Do not expose detailed error messages to untrusted parties: ```rust match multi_base::decode(untrusted_input, true) { @@ -73,34 +80,35 @@ match multi_base::decode(untrusted_input, true) { // Process data } Err(_) => { - // Log error internally, return generic error to user + // Log the error internally. Return a generic error to the user. eprintln!("Invalid multibase input"); } } ``` -### 3. Identity Encoding Considerations +### 3. Identity Encoding The Identity encoding (`\0` prefix) uses lossy UTF-8 conversion: -- Invalid UTF-8 bytes are replaced with the Unicode replacement character (U+FFFD) -- This prevents panics but means invalid UTF-8 won't round-trip perfectly -- For binary data that must round-trip exactly, use a different base encoding (e.g., Base64) +- Invalid UTF-8 bytes are replaced with the Unicode replacement character + (U+FFFD). +- This prevents panics. Invalid UTF-8 does not round-trip. +- For binary data that must round-trip, use a different base encoding. ```rust -// For exact binary data preservation, use Base64 or Base58 +// For binary data preservation, use Base64 or Base58 let encoded = multi_base::encode(Base::Base64, binary_data); -// Identity is only appropriate for UTF-8 text +// Identity is for UTF-8 text only let text_encoded = multi_base::encode(Base::Identity, "valid utf-8 text".as_bytes()); ``` ### 4. Strict vs Permissive Decoding -Use strict decoding (`true`) for untrusted input to ensure stricter validation: +Use strict decoding for untrusted input: ```rust -// For untrusted input, always use strict mode +// For untrusted input, use strict mode let (base, data) = multi_base::decode(untrusted, true)?; // Permissive mode allows case-insensitive decoding for some bases @@ -109,96 +117,103 @@ let (base, data) = multi_base::decode(trusted, false)?; ## Security Testing -The crate includes security tests covering: +The crate has 17 security tests in `tests/security.rs`. They cover: -- **17 security-focused tests** in `tests/security.rs` -- Large input handling (up to 1 MB tested) -- Malformed and malicious input patterns -- Buffer reuse safety -- Concurrent operation safety -- Resource exhaustion resistance -- Integer overflow safety -- Invalid UTF-8 handling -- Empty and truncated input handling +- Large input handling (up to 1 MB). +- Malformed and malicious input patterns. +- Buffer reuse safety. +- Concurrent operation safety. +- Resource exhaustion resistance. +- Integer overflow safety. +- Invalid UTF-8 handling. +- Empty and truncated input handling. + +Run security tests: -Run security tests with: ```bash cargo test --test security ``` ## Fuzzing -The crate can be fuzzed using `cargo-fuzz`. Fuzzing targets are recommended for: +The crate can be fuzzed with `cargo-fuzz`. Fuzz targets: -1. **Decoding arbitrary strings** - Ensures no panics on any input -2. **Encoding arbitrary bytes** - Ensures no panics on any binary data -3. **Round-trip operations** - Verifies encode/decode consistency +1. `fuzz_decode` β€” Decode arbitrary strings. Checks for panics on any + input. +2. `fuzz_encode` β€” Encode arbitrary bytes. Checks for panics on any + binary data. +3. `fuzz_roundtrip` β€” Encode and decode round-trip. Checks encode and + decode consistency. -### Setting Up Fuzzing +### Set Up Fuzzing ```bash # Install cargo-fuzz cargo install cargo-fuzz -# Initialize fuzzing (if not already done) -cargo fuzz init - # Run fuzz tests -cargo fuzz run fuzz_decode # Fuzz decoding operations -cargo fuzz run fuzz_encode # Fuzz encoding operations -cargo fuzz run fuzz_roundtrip # Fuzz full round-trips +cargo fuzz run fuzz_decode +cargo fuzz run fuzz_encode +cargo fuzz run fuzz_roundtrip ``` ## Dependency Security -The crate depends on well-maintained libraries: +The crate depends on: -- `base-x` (0.2.7) - Variable-radix base encoding -- `data-encoding` (2.3.1) - Standard base encodings -- `thiserror` (2.0) - Error handling +- `base-x` (0.2.7) β€” Variable-radix base encoding. +- `data-encoding` (2.3.1) β€” Standard base encodings. +- `thiserror` (2.0) β€” Error handling. -The Base256Emoji codec is implemented inline within this crate (it was -previously provided by the external `base256emoji` crate; the dependency was -dropped β€” see `CHANGELOG.md`). +The `Base256Emoji` codec is implemented inline. The external +`base256emoji` dependency was dropped. See `CHANGELOG.md`. -All dependencies are actively maintained and widely used in the Rust ecosystem. +All dependencies are maintained and used in the Rust ecosystem. ## Reporting Security Issues -If you discover a security vulnerability in the multibase crate, please report it privately: +If you find a security vulnerability in the `multi-base` crate, report it +as follows: -1. **Do not** open a public GitHub issue -2. Contact the maintainers via email (check Cargo.toml for contact information) -3. Provide detailed information about the vulnerability -4. Allow reasonable time for a fix before public disclosure +1. Do not open a public GitHub issue. +2. Contact the maintainers by email. See `Cargo.toml` for the address. +3. Give detailed information about the vulnerability. +4. Allow reasonable time for a fix before public disclosure. ## Security Guarantees -### What This Crate Guarantees +### What the Crate Guarantees -- βœ… No panics on arbitrary untrusted input -- βœ… Memory safety (no unsafe code used) -- βœ… Comprehensive input validation -- βœ… Thread-safe operations (all types are Send + Sync where appropriate) -- βœ… Error information without exposing internal state +- No panics on arbitrary untrusted input. +- Memory safety. No unsafe code is used. +- Input validation at all boundaries. +- Thread-safe operations. All types are `Send + Sync`. +- Error information without exposure of internal state. -### What This Crate Does NOT Guarantee +### What the Crate Does Not Guarantee -- ❌ Protection against resource exhaustion (application responsibility) -- ❌ Constant-time operations (not designed for cryptographic use) -- ❌ Perfect round-tripping of invalid UTF-8 in Identity encoding +- Protection against resource exhaustion. This is an application + responsibility. +- Constant-time operations. The crate is not for cryptographic use. +- Perfect round-trip of invalid UTF-8 in Identity encoding. ## Changelog of Security-Related Changes -### Version 1.0.1 -- Fixed Identity encoding panic risk (now uses lossy UTF-8 conversion) -- Migrated to thiserror for better error handling -- Added 17 security-focused tests -- Added this SECURITY.md document +### Version 1.0.3 +- Updated `SECURITY.md` code examples to use `multi_base::` instead of + the old `multibase::` module path. +- Corrected the stale `base256emoji` dependency reference. -### Version 1.0.0 -- Initial release with basic security considerations +### Version 1.0.2 +- Added `#![deny(unsafe_code)]` at the crate root. +- Added clippy lint configuration with `unsafe_code = "deny"`. +- Added `cargo audit` job in CI. -## Last Updated +### Version 1.0.1 +- Fixed Identity encoding panic risk. Now uses lossy UTF-8 conversion. +- Migrated to `thiserror` for error handling. +- Added 17 security tests. +- Added this `SECURITY.md` document. -2026-07-15 +### Version 1.0.0 +- Initial release with basic security considerations. \ No newline at end of file diff --git a/benches/multibase.rs b/benches/multibase.rs index 11bf73a..c991eb1 100644 --- a/benches/multibase.rs +++ b/benches/multibase.rs @@ -1,14 +1,14 @@ use std::hint::black_box; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; -use rand::Rng; +use rand::RngExt; use multi_base::{Base, decode, decode_into, encode, encode_into, encode_to_validated}; fn bench_encode(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data_large: Vec = (0..1024).map(|_| rng.r#gen()).collect(); - let data_small: Vec = (0..32).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data_large: Vec = (0..1024).map(|_| rng.random()).collect(); + let data_small: Vec = (0..32).map(|_| rng.random()).collect(); let mut group = c.benchmark_group("encode"); @@ -50,8 +50,8 @@ fn bench_encode(c: &mut Criterion) { } fn bench_decode(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..1024).map(|_| rng.r#gen::() as usize).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..1024).map(|_| rng.random::() as usize).collect(); let base32 = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; let base58 = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -94,8 +94,8 @@ fn bench_decode(c: &mut Criterion) { // Benchmark zero-copy encode_into API fn bench_encode_into(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..1024).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..1024).map(|_| rng.random()).collect(); let mut group = c.benchmark_group("encode_into"); @@ -118,8 +118,8 @@ fn bench_encode_into(c: &mut Criterion) { // Benchmark zero-copy decode_into API fn bench_decode_into(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..1024).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..1024).map(|_| rng.random()).collect(); let encoded_base16 = encode(Base::Base16Lower, &data); let encoded_base32 = encode(Base::Base32Lower, &data); @@ -156,8 +156,8 @@ fn bench_decode_into(c: &mut Criterion) { // Benchmark roundtrip operations fn bench_roundtrip(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..256).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..256).map(|_| rng.random()).collect(); let mut group = c.benchmark_group("roundtrip"); @@ -185,13 +185,13 @@ fn bench_roundtrip(c: &mut Criterion) { // Benchmark various data sizes fn bench_data_sizes(c: &mut Criterion) { - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let sizes = vec![0, 1, 16, 64, 256, 1024, 4096]; let mut group = c.benchmark_group("data_sizes"); for size in sizes { - let data: Vec = (0..size).map(|_| rng.r#gen()).collect(); + let data: Vec = (0..size).map(|_| rng.random()).collect(); group.bench_with_input(BenchmarkId::new("base64_encode", size), &data, |b, data| { b.iter(|| { @@ -205,8 +205,8 @@ fn bench_data_sizes(c: &mut Criterion) { // Benchmark all base types fn bench_all_bases(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..128).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..128).map(|_| rng.random()).collect(); let bases = vec![ Base::Base2, @@ -242,8 +242,8 @@ fn bench_all_bases(c: &mut Criterion) { // Benchmark EncodedString operations fn bench_encoded_string(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..256).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..256).map(|_| rng.random()).collect(); let mut group = c.benchmark_group("encoded_string"); @@ -296,8 +296,8 @@ fn bench_base_from_code(c: &mut Criterion) { // Benchmark comparison: encode vs encode_into fn bench_encode_comparison(c: &mut Criterion) { - let mut rng = rand::thread_rng(); - let data: Vec = (0..1024).map(|_| rng.r#gen()).collect(); + let mut rng = rand::rng(); + let data: Vec = (0..1024).map(|_| rng.random()).collect(); let mut group = c.benchmark_group("encode_comparison");