base64-ng 2.0.1

no_std-first Base64 encoding and decoding with strict RFC 4648 APIs and optional SIMD
Documentation

base64-ng

base64-ng provides strict RFC 4648 Base64, no_std operation, caller-owned and allocating APIs, incremental and in-place transforms, optional admitted SIMD backends, and separately named compatibility policies. Zero external runtime or development dependencies in Cargo.toml.

This source tree defines the synchronized 2.0.1 package family. This patch reduces published package and README size. Runtime behavior and the public 2.0 API remain unchanged from 2.0.0; repository-only release scripts, evidence, API snapshots, and engineering ledgers remain available on GitHub instead of being copied into the crates.io archive.

Quick Start

[dependencies]
base64-ng = "2.0.1"

For ordinary Standard Base64 with canonical padding:

let encoded = base64_ng::encode(b"hello").unwrap();
assert_eq!(encoded, "aGVsbG8=");

let decoded = base64_ng::decode(encoded.as_bytes()).unwrap();
assert_eq!(decoded, b"hello");

Use an explicit preset when the alphabet and padding policy should be visible:

use base64_ng::STRICT_URL_SAFE_UNPADDED;

let encoded = STRICT_URL_SAFE_UNPADDED
    .encode_to_string(b"hello")
    .unwrap();
assert_eq!(encoded, "aGVsbG8");
assert_eq!(
    STRICT_URL_SAFE_UNPADDED.decode_to_vec(encoded.as_bytes()).unwrap(),
    b"hello"
);

The strict presets are:

  • STRICT_STANDARD_PADDED
  • STRICT_STANDARD_UNPADDED
  • STRICT_URL_SAFE_PADDED
  • STRICT_URL_SAFE_UNPADDED

Strict decoders reject whitespace, mixed alphabets, impossible lengths, malformed padding, trailing data after padding, and non-canonical unused bits. WHATWG-forgiving, legacy-whitespace, wrapped, MIME, PEM, and other protocol behavior is available only through explicitly named APIs or companion crates.

Choosing An API

Need API
Ordinary owned Standard Base64 encode and decode
Explicit alphabet and padding A STRICT_* preset
Transactional caller-owned output encode_into and decode_into
Heapless incremental processing encoder() and decoder()
In-place transformation encode_in_place and decode_in_place
Policy-carrying encoded text Base64String<S>
Compile-time fixed data encode_array and decode_array
Fixed-capacity runtime output encode_bounded and decode_bounded
Secret-bearing frames secret::{SecretArrayFrame, SecretVecFrame}
Synchronous I/O Enable stream
Async I/O base64-ng-tokio
Serde fields base64-ng-serde

base64_ng::prelude contains the focused ordinary API imports. It deliberately does not import secret, compatibility, protocol, or historical surfaces.

Caller-Owned Buffers

One-shot *_into operations are transactional: an error leaves the complete destination unchanged.

use base64_ng::STRICT_STANDARD_PADDED;

let mut encoded = [0u8; 8];
let encoded_len = STRICT_STANDARD_PADDED
    .encode_into(b"hello", &mut encoded)
    .unwrap();
assert_eq!(&encoded[..encoded_len], b"aGVsbG8=");

let mut decoded = [0u8; 5];
let decoded_len = STRICT_STANDARD_PADDED
    .decode_into(&encoded[..encoded_len], &mut decoded)
    .unwrap();
assert_eq!(&decoded[..decoded_len], b"hello");

For in-place encoding, reserve the encoded capacity first:

use base64_ng::STRICT_STANDARD_PADDED;

let mut buffer = [0u8; 8];
buffer[..5].copy_from_slice(b"hello");
let written = STRICT_STANDARD_PADDED
    .encode_in_place(&mut buffer, 5)
    .unwrap();
assert_eq!(&buffer[..written], b"aGVsbG8=");

Incremental Processing

Incremental states retain at most partial Base64 quanta and report exact input and output progress. Call finish to validate or emit the final tail.

use base64_ng::{Status, STRICT_STANDARD_PADDED};

let mut encoder = STRICT_STANDARD_PADDED.encoder();
let mut output = [0u8; 8];
let step = encoder.update(b"hello", &mut output).unwrap();
let mut written = step.progress().output_produced();
let final_step = encoder.finish(&mut output[written..]).unwrap();
written += final_step.progress().output_produced();

assert_eq!(final_step.status(), Status::Complete);
assert_eq!(&output[..written], b"aGVsbG8=");

The decoder uses the same lifecycle and progress contract. See the incremental encoder and decoder finalization guides for fragmented-input examples.

Encoded Strings And Formatting

Base64String<S> stores validated ordinary Base64 together with its exact codec policy:

use base64_ng::{Base64String, STRICT_STANDARD_PADDED};

let stored = Base64String::encode(STRICT_STANDARD_PADDED, b"hello").unwrap();
assert_eq!(stored.as_str(), "aGVsbG8=");
assert_eq!(stored.decode().unwrap(), b"hello");

It is printable and non-wiping; it is not a secret container. Allocation-free display, rollback-safe encode_append, and encoded_chunks are available from the same strict presets.

Validation And Compatibility

Validate without producing decoded output:

use base64_ng::{STRICT_STANDARD_PADDED, STRICT_URL_SAFE_UNPADDED};

STRICT_STANDARD_PADDED.validate(b"aGVsbG8=").unwrap();
assert!(STRICT_STANDARD_PADDED.validate(b"aGVsbG8").is_err());
STRICT_URL_SAFE_UNPADDED.validate(b"-_8").unwrap();

Exact WHATWG forgiving decode is separate from strict RFC 4648:

use base64_ng::{web, STRICT_STANDARD_PADDED};

assert_eq!(web::FORGIVING.decode_to_vec(" Z h = = ").unwrap(), b"f");
assert!(STRICT_STANDARD_PADDED.decode_to_vec(b" Z h = = ").is_err());

Legacy ASCII-whitespace decode is also explicit:

use base64_ng::{legacy, STRICT_STANDARD_PADDED};

let mut output = [0u8; 5];
let written = legacy::ASCII_WHITESPACE
    .decode_into(&STRICT_STANDARD_PADDED, b" aG\r\nVs\tbG8= ", &mut output)
    .unwrap();
assert_eq!(&output[..written], b"hello");

Forgiving and legacy policies are ordinary-data APIs and are unavailable to secret frames.

Features

Feature Default Purpose
alloc yes Vec, String, and owned convenience APIs
std yes Standard error and runtime support
simd no Admitted Standard/URL-safe encode and strict-decode acceleration
stream no Synchronous std::io adapters
secrets no Bounded secret storage, fixed-work transforms, and explicit exposure
checked-backend no SIMD plus bounded scalar verification, quarantine, and scalar retry
allow-wasm32-best-effort-wipe no Acknowledge wasm cleanup limitations for secrets builds
allow-compiler-fence-only-wipe no Acknowledge unsupported-native cleanup limitations
tokio no Reserved, currently inert and dependency-free; use base64-ng-tokio
kani no Reserved verifier integration
fuzzing no Reserved fuzz integration

Disable defaults for core-only embedded use:

[dependencies]
base64-ng = { version = "2.0.1", default-features = false }

Enable ordinary SIMD dispatch without changing the public codec API:

[dependencies]
base64-ng = { version = "2.0.1", features = ["simd"] }

Scalar by default; std x86/x86_64 encode selects SSSE3/SSE4.1, AVX2, or AVX-512 VBMI by length, strict decode selects SSSE3/SSE4.1 or AVX2. Admitted little-endian AArch64 NEON, wasm simd128, and exact-profile Linux/SpacemiT X60 RVV paths are selected only inside their documented scopes. Unsupported CPUs, custom alphabets, compatibility policies, and secret operations retain scalar behavior.

Runtime selection can be inspected with runtime::backend_report().

Secret-Bearing Data

Ordinary strict decoding returns detailed errors and may exit early. For keys, tokens, passwords, or other secret-bearing frames, enable secrets and use the bounded fixed-work API:

[dependencies]
base64-ng = { version = "2.0.1", default-features = false, features = ["secrets"] }
use base64_ng::{secret::{SecretArrayFrame, SecretInput}, STRICT_STANDARD_PADDED};

let mut frame = SecretArrayFrame::<5>::new(&STRICT_STANDARD_PADDED).unwrap();
frame.update(&SecretInput::new(b"aGVsbG8=")).unwrap();
let secret = frame.finish().unwrap();
assert_eq!(secret.expose_secret().as_bytes(), b"hello");

This is a constant-time-oriented scalar boundary with bounded private staging, opaque validity results, and best-effort cleanup. It has project timing, assembly, Kani, Miri, and test evidence, but no formal cryptographic guarantee. It must not be documented as a formally verified cryptographic constant-time API. Protected-memory claims additionally require an admitted provider and runtime assurance token. Read the secret decoding and assurance guides before deployment.

Companion Packages

Optional ecosystem and protocol dependencies remain outside the zero-dependency core.

Package Purpose
base64-ng-sanitization Protected and locked secret integration
base64-ng-derive Base64Secret derive for fixed secret newtypes
base64-ng-serde Explicit Serde field adapters
base64-ng-bytes Bytes, Buf, and BufMut helpers
base64-ng-subtle Sealed subtle::ConstantTimeEq integration
base64-ng-tokio Async helpers and streaming adapters
base64-ng-imap RFC 3501 modified-Base64 payload transforms
base64-ng-mime RFC 2045 Base64 body transforms
base64-ng-multibase Registered Base64-family multibase prefixes
base64-ng-password Passlib PBKDF2 and SHA-crypt field transforms
base64-ng-openpgp RFC 9580 ASCII armor
base64-ng-pem RFC 7468 textual encoding
@valkyoth/base64-ng-wasm-loader Scalar/simd128 JavaScript loader

Each Rust companion has its own crate README and examples. The package topology defines their boundaries.

Install the supported JavaScript loader with:

npm install @valkyoth/base64-ng-wasm-loader
import { Codecs, createBase64Ng } from "@valkyoth/base64-ng-wasm-loader";

const base64 = await createBase64Ng();
const encoded = base64.encode(new TextEncoder().encode("hello"), Codecs.URL_SAFE_NO_PAD);
const decoded = base64.decode(encoded, Codecs.URL_SAFE_NO_PAD);
base64.dispose();

Verification Status

"Project validated" means the named repository tests and evidence gates passed on the stated systems. It does not mean certification, independent review, whole-crate formal proof, or a portable performance guarantee.

Backend Project evidence Dispatch status
Portable scalar Native x86-64, AArch64, RISC-V; QEMU big-endian targets Admitted
x86 SSSE3/SSE4.1, AVX2, AVX-512 VBMI Native differential, kernel, assembly, and benchmark evidence Admitted within documented thresholds
AArch64 NEON Apple Silicon and AWS Neoverse-N1 evidence Admitted
wasm simd128 Node, Wasmtime, Chromium, Firefox, and Safari evidence Admitted artifact
RISC-V RVV 1.0 Native SpacemiT X60 plus QEMU vector-length evidence Admitted only for the exact documented X60 profile
AArch64 SVE QEMU and assembly evidence only Not admitted
Big-endian SIMD No accelerated backend Scalar only
Secret encode/decode Scalar fixed-work evidence Separate from ordinary SIMD

The full backend matrix, exact machines, thresholds, non-claims, and evidence links live in the Trust Dashboard and dispatch matrix.

Rust Support

MSRV remains Rust 1.90.0. The active release toolchain is Rust 1.97.1.

Rust Evidence
1.90.0 MSRV compatibility check
1.91.0 - 1.97.0 cargo check --all-features
1.97.1 Active release toolchain and full release checks

New deployments should prefer the latest tested stable Rust.

Security And Engineering Documentation

Repository CI includes formatting, Clippy, tests, rustdoc, dependency policy, fuzz and formal-verification harnesses, hardware-specific execution, SBOM, and reproducible build check. These engineering artifacts remain in the GitHub repository and are intentionally excluded from the crates.io package.

License

Licensed under either Apache-2.0 or MIT, at your option.

Contributions and release policy are documented in CONTRIBUTING.md and docs/RELEASE.md.