Skip to content

Security: cryptidtech/multi-util

Security

SECURITY.md

Security Policy

Overview

The multi-util crate gives utility traits and types to the multiformats stack. These types are BaseEncoded, Varuint, Varbytes, CodecInfo, and EncodingInfo. This document describes the security properties of the crate.

no_std Support

The crate works in no_std environments with alloc. Disable the default features to remove the std dependency:

[dependencies]
multi-util = { version = "1.1", default-features = false }

To use serde under no_std, enable only the serde feature:

[dependencies]
multi-util = { version = "1.1", default-features = false, features = ["serde"] }

The std feature is on by default. It enables multi-base/std, multi-codec/std, multi-trait/std, thiserror/std, and serde?/std. A CI ensure_no_std job builds the no_std target on each push and pull request.

Memory Safety

  • No unsafe code. #![deny(unsafe_code)] is set at the crate root. [lints.rust] unsafe_code = "deny" in Cargo.toml enforces it too.
  • All errors return Result. No path panics on invalid input.

Decoded-Size Caps

Varbytes::try_decode_from and the serde Varbytes path enforce two caps on untrusted wire data. These caps mitigate CWE-400 and CWE-125.

  • DEFAULT_MAX = 16 MiB. This is the most bytes a single decoded Varbytes value can allocate. A length prefix that claims more returns Error::InputTooLarge { claimed, max }.
  • Buffer-length check. A length prefix that claims more bytes than remain returns Error::InsufficientData { expected, actual }. This prevents an out-of-bounds read and a panic.

The serde path routes all four visitor impls through a shared decode_varbytes(input, max) helper. The helper runs both checks before it slices the buffer.

Configurable Cap (A4)

The decoded-size cap is configurable at the type level. VarbytesMax<const MAX: usize> is the generic struct. Varbytes is a type alias for VarbytesMax<DEFAULT_MAX> (16 MiB). A caller that needs a different bound has two options:

  1. Use a distinct type. Instantiate VarbytesMax<N> with the desired cap. The try_decode_from and Deserialize impls enforce N.
  2. Use a per-field override. The crate exports deserialize_varbytes_with_max(deserializer, max). Use it with #[serde(deserialize_with = "...")] when a field needs a tighter or looser bound than the default.

The crate keeps MAX_DECODED_SIZE as a deprecated alias for DEFAULT_MAX. New code should use DEFAULT_MAX or VarbytesMax<N>.

Display/Hash Allocation

The Hash impl hashes the raw encoded bytes directly. It does not go through Display or String. This removes the Display and String allocation overhead. The Display impl keeps one Vec<u8> allocation. It needs &[u8] to pass the bytes to the base encoder. A zero-allocation AsRef<[u8]> path was not adopted. It would need a breaking bound change on the T type parameter of BaseEncoded. That change would break Varuint<T> because its inner value is not a contiguous byte buffer. It would also need language specialization, which is not stable. The source documents this trade-off in src/base_encoded.rs.

Reporting Vulnerabilities

Report security issues through the GitHub issue tracker. You can also report them privately to the maintainers.

There aren't any published security advisories