Subject
base64ct is a pure-Rust, no_std, allocation-optional Base64 encoder/decoder published by the RustCrypto project. It implements the standard, URL-safe, bcrypt, crypt(3) (SHA-crypt and a deprecated big-endian variant), and PBKDF2 alphabets in both padded and unpadded forms, and provides streaming Encoder / Decoder types with optional line wrapping. The crate is targeted at parsing PEM-format cryptographic material (used by pem-rfc7468, pkcs8, ssh-key, etc.), and uses branchless integer arithmetic on every per-symbol code path so that decode timing depends only on input length, not on input bytes. The crate does not itself perform cryptography (no hashing, signing, or encryption), justifying uses-crypto and impl-crypto; the constant-time design exists to protect callers that handle secrets immediately after decoding.
Methodology
The published .crate archive was compared against the upstream Git checkout at the commit recorded in .cargo_vcs_info.json. All source under src/ (~1500 lines across the alphabet definitions, the shared Alphabet trait with the branchless encode/decode core, the in-place and streaming codecs, the line-ending helpers, the buffered Decoder line reader, and the error types) was read in full, and all integration tests under tests/ (alphabet-specific test vectors plus property-based equivalence tests against the base64 reference crate) were surveyed. Cargo.toml was reviewed for build-time hooks, proc macros, default features, and dependency surface. The .github/workflows/base64ct.yml CI workflow in the parent repository was inspected. No code was compiled or executed.
Results
The comparison between the published crate contents and the upstream Git checkout shows that all source files match byte-for-byte; the only differences are the added Cargo.lock and .cargo_vcs_info.json, plus cargo's standard Cargo.toml normalisation.
The crate ships no binary artefacts, no build.rs, and is not a proc-macro crate (Cargo.toml declares only [features] alloc/std, no [lib] proc-macro = true), justifying has-binaries, has-build-exec, and has-install-exec. Runtime capabilities are limited to in-memory buffer manipulation: the source contains no std::net, std::fs, std::env, std::process, or threading code, justifying uses-network, uses-filesystem, uses-environment, uses-exec, uses-jit, uses-interpreter, and uses-concurrency. The crate has only two dev-dependencies (base64 for equivalence testing and proptest); both are non-shipping. The code is benign in intent (RustCrypto upstream, no obfuscation, no telemetry), justifying is-benign.
The crate implements both a Base64 parser (decoder for several alphabets, with documented constant-time properties) and a non-trivial branchless encoding algorithm (justifying impl-parser and impl-algorithm); it does not implement a data structure, interpreter, JIT, protocol, or concurrency primitive, justifying impl-datastructure, impl-interpreter, impl-jit, impl-protocol, and impl-concurrency. The branchless decode-step combiner in src/alphabet.rs is the security-relevant primitive: each candidate range or equality match is folded into a 6-bit lane via signed-mask arithmetic, the per-symbol output is OR-accumulated into an error word that is only branched on after all data-dependent work is done, and validation of the last block round-trips through the encoder using a non-short-circuiting fold-XOR comparison. The test suite covers each alphabet with hand-curated vectors plus property-based equivalence against the upstream base64 crate for the standard and unpadded standard alphabets (256-byte inputs, incremental encode/decode, line-wrapped decode at variable widths). In-module #[cfg(test)] blocks cover each alphabet's encode/decode paths and the streaming Encoder/Decoder edge cases, with integration tests in tests/ providing the alphabet-specific vectors and property-based equivalence against the upstream base64 crate, justifying has-unit-tests and has-integration-tests. The branchless decode-step combiner, validated lengths feeding into decode_in_place, and OR-accumulated error word together justify parser-impl-safe. There are no fuzz tests (has-fuzz-tests = false), but the proptest coverage and the cited C++ reference implementation give reasonable assurance of correctness, justifying parser-impl-correct, parser-impl-tested, algorithm-impl-correct, and algorithm-impl-tested. Per-symbol cost is constant and per-input cost is linear in the input length, with no recursion or unbounded loops, justifying algorithm-impl-bounds.
Three unsafe blocks were reviewed, all in src/encoding.rs: aliased raw-pointer reads/writes in decode_in_place (sound because the 4-byte read window and 3-byte write window for each chunk are sequenced and never concurrent, with bounds guarded by debug assertions and prior length math), a get_unchecked on the resulting prefix (bounds derived from the validated dlen), and two from_utf8_unchecked calls that wrap output produced exclusively by the alphabet's encode function (whose output is by construction printable ASCII). All have SAFETY comments; usage is minimal and matches the pattern needed to avoid bounds-check overhead on the hot decode path. Round-trip property tests exercise these paths, justifying uses-unsafe, unsafe-safe, unsafe-documented, unsafe-minimal, unsafe-tested. There is no FFI, no Miri job in the upstream CI workflow inspected.
Two low-severity findings were filed. FINDING-1 documents that Encoding::decode_in_place does not call validate_last_block and therefore diverges from Encoding::decode by accepting non-canonical unpadded inputs (e.g. "Mi"); the surrounding doc comment notes a narrower padding-validation TODO but not this broader divergence. FINDING-2 notes that Encoding::encoded_len silently returns 0 on usize overflow rather than signalling an error.
Conclusion
The crate is small, focused, well-documented, and built around a security goal (sidechannel resistance for Base64-encoded cryptographic material) that is faithfully reflected in the implementation. The published source matches the upstream Git tree, ships no binary artefacts or build-time code, and exercises no I/O or OS-facing capabilities. The two findings are minor and do not affect the documented security properties.