Skip to content

feat(str0m): rind-str0m, a str0m CryptoProvider on rind - #467

Merged
kixelated merged 6 commits into
mainfrom
quest/m2/adoption/rind-str0m
Nov 7, 2026
Merged

kixelated merged 6 commits into
mainfrom
quest/m2/adoption/rind-str0m

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

str0m (2.9M downloads) defaults to aws-lc-rs, and its only pure-Rust backend is the slower RustCrypto one. Its CryptoProvider is now released (str0m 0.25, str0m-proto 0.8), so rind can fill it with no upstream change. The maintainer reversed the 2026-10-31 call to drop the adapter on 2026-11-04 (quest).

Decisions

Settled by the maintainer on 2026-11-07, on the two findings the Codex review kept:

  1. srtp_aes_{128,256}_ecb_round panics on wrong-length slices: ✅ accept, documented on the methods (rejected: hold until str0m changes the trait). str0m-proto's trait returns (), so there is no error to return; str0m's only caller passes fixed arrays (src/rtp/srtp.rs:803-832).
  2. A generated certificate's private key lives in dimpl's DtlsCertificate (pub private_key: Vec<u8>, no Drop, dimpl 0.9.0 src/lib.rs:282-298) and isn't wiped when str0m drops it: ✅ accept, documented in the README (rejected: return None from generate_certificate; hold until dimpl wipes it). It's an upstream type the adapter must use as is, and rind's own wiping is becoming opt-in (moq-dev/rind#461).

Checked against moq-dev/rind#461: the adapter wipes nothing itself, so there is no unconditional wipe to put under rind's wipe feature. The keys it builds (SRTP, HMAC, the rcgen key pair) are rind types and follow rind's rule; the one always-on wipe it touches is rind-rcgen's to_pkcs8 Zeroizing, which #461 keeps on purpose.

Approach

  • Restores rind-str0m, which moq-dev/rind#138 deleted while str0m's interface was unreleased, and ports it to the published str0m 0.25 and today's rind API.
  • DTLS 1.2 and 1.3 run through dimpl on rind-dimpl. The self-signed ECDSA P-256 certificate comes from rind-rcgen, shaped like dimpl's own generator: random serial, one-year validity, and key identifiers set as rcgen's crypto feature would.
  • SRTP: AES-128-CM on rind::cipher::AesCtr, AEAD-AES-128/256-GCM on rind::aead::Key, key derivation on rind::cipher::Aes. HMAC-SHA1 (SRTP auth, STUN), SHA-1, and SHA-256 come from rind.
  • Malformed input from a caller is a CryptoError, not a panic: SRTP buffers too short or shorter than the tag, and an empty DTLS certificate or a private key rind-dimpl cannot load (dimpl itself panics on both). The only expects are on lengths str0m fixes by type or from its own key schedule; srtp_aes_*_ecb_round has no error path in the trait, so their doc comments state the panic (Decision 1).
  • Docs: the README says a generated certificate's private key sits unwiped in dimpl's DtlsCertificate (Decision 2).
  • CI: added to the test lists in build.yml and sh/armv7.sh. admin/ctgrind's adapter suite already runs every workspace crate's tests/ctgrind.rs.
  • Docs: README adapter table, MIGRATE.md, and MIGRATING.md (str0m moves from "removed" to rind-str0m, with a mapping row). The quest file is deleted and the MoQ epic's note updated.

Choices made without asking

  • Depends on str0m (default features off), not str0m-proto. sh/adapter-versions.sh then checks the 0.25 match against the dependency named after the crate directly, and users see str0m::crypto::* types. str0m is a dependency every user already has. The cost: if str0m ever ships a breaking release without bumping str0m-proto, rind-str0m still needs a release to follow.
  • AES-CM counts over all 128 bits (AesCtr::new(.., 128)), RFC 3711's IV + i mod 2^128, matching aws-lc-rs for every IV. The deleted crate refused a carry out of rind's 32-bit counter instead; str0m's IVs never carry, so the difference is only for other callers. differential.rs now checks the carry against aws-lc-rs, replacing the old refusal test.
  • No dangerously_set_rng_seed under is_test(), unlike str0m-aws-lc-rs. str0m's full suite passes without it (below), so rind doesn't take on a seeded-RNG path.
  • str0m's own suite is a one-off local run, not a CI job. tests/rtc.rs keeps the live interop path (ICE, DTLS 1.2/1.3 in both roles, SRTP/SRTCP both ways against aws-lc-rs) in every CI run, so a nightly job building str0m's 79 test binaries wouldn't catch much more.

Impact

  • New published crate rind-str0m 0.25.0: default_provider() -> Result<str0m::crypto::CryptoProvider, MissingFeature> and MissingFeature. It re-exports no rind types.
  • No change to rind or any other crate's API.

Validation

  • just check: pass (rs, msrv, cross, deny, versions, fixtures, patch, gh, shell, nix).

  • just check on 2bd6ec82, the docs commit merged with main (00610aff): pass, every module.

  • cargo test -p rind-str0m: 22 tests and the crate-doc example. RFC 3711 (AES-CM keystream, key derivation) and RFC 7714 (GCM for SRTP and SRTCP) vectors, differential against str0m-aws-lc-rs over every length up to 300 B plus random ones up to MTU, forgeries refused with no plaintext released, short buffers and unusable DTLS certificates as errors, and str0m sessions between a rind peer and an aws-lc-rs peer over DTLS 1.2 and 1.3 in both roles.

  • str0m 0.25.0's own suite (crates.io source, --features _internal_test_exports, a rind arm added to tests/common.rs's provider switch): 1312 passed, 0 failed, 3 ignored, in each of three runs: rind as the process default; L_CRYPTO=rind R_CRYPTO=aws-lc-rs; and L_CRYPTO=aws-lc-rs R_CRYPTO=rind.

  • SRTP benchmark through the trait, rind vs str0m-aws-lc-rs on an i7-8700 (no SHA-NI) under load 8 to 15, so indicative only:

    op 100 B 1200 B
    AES-128-CM encrypt 58 vs 416 ns 210 vs 494 ns
    AEAD-AES-128-GCM encrypt 101 vs 160 ns 282 vs 391 ns
    AEAD-AES-128-GCM decrypt 74 vs 165 ns 247 vs 373 ns
    AEAD-AES-256-GCM encrypt 116 vs 205 ns 415 vs 504 ns
    key derivation round 72 vs 285 ns
    HMAC-SHA1 within noise (two runs disagreed) within noise

    str0m-aws-lc-rs re-keys AES-CM and ECB on every call, which accounts for most of the CM and key-derivation gap. For HMAC-SHA1, rind-bench's own hmac-sha1/*/1200B on the same host shows rind 1.57 µs vs aws-lc-rs 1.56 µs, and BENCHMARKS.md has rind ahead on every runner.

  • admin/ctgrind adapters under valgrind: pass, including rind-str0m's 4 tests (AES-CM, both GCMs, key derivation, STUN HMAC).

Follow-ups

  • str0m README lists rind-str0m (post outside moq-dev, so for the maintainer). str0m has no AI-contribution policy; its README's backend table lists only in-repo crates. Draft: add a "third-party backends" note after the table naming rind-str0m (dimpl + rind, pure Rust, x86_64/aarch64/armv7), PR title "README: list rind-str0m as a third-party crypto backend". Recommendation: plan it after rind-str0m 0.25.0 is on crates.io, the same way rustls lists rind-rustls is tracked.
  • dimpl wipes DtlsCertificate's private key on drop (post to algesten/dimpl, for the maintainer). Recommendation: plan it, per Decision 2; it also covers rind-dimpl's users.
  • str0m's ECB key-derivation methods take fixed-size arrays (post to algesten/str0m, for the maintainer). Recommendation: plan it, per Decision 1.
  • Nightly job running str0m's own suite on rind: drop. tests/rtc.rs covers the live path in every CI run.

Review

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, high effort
  • Session: 01a1b504-abb6-7f92-8460-d1b2ab8d38ca, round 3
  • Head: 5f9eb29
  • Base: origin/main at 00610af
  • Score: 10/10, No remaining findings after applying the recorded maintainer decisions; source inspection and diff checks found no additional issues, and runtime tests were not rerun.
  • Findings: none
  • The last round's findings, at 4e00d41:
    • Refuted: medium, rind-str0m/src/srtp.rs:42: Remove caller-reachable panics from the ECB boundary; The saved PR body at .tmp/body.md, Decisions item 1, records maintainer acceptance of wrong-length ECB panics, now documented in rind-str0m/src/srtp.rs:30-44.
    • Refuted: medium, rind-str0m/src/dtls.rs:95: Ensure exported DTLS private keys are wiped on drop; The saved PR body at .tmp/body.md, Decisions item 2, records maintainer acceptance of upstream-owned unwiped DTLS keys, now documented in rind-str0m/README.md:28.

The head 2bd6ec82 adds only a conflict-free merge of main (00610aff) to the reviewed head 5f9eb293, so round 3 covers it.

Models

Size: M
Implementer: Claude Code, claude-opus-5-5, high effort

(Written by Claude Opus 5.5 with high)

kixelated and others added 3 commits November 6, 2026 23:12
Restores the crate #138 removed, ported to the published str0m 0.25,
with its version set to 0.25.0 and a `MissingFeature` from
`default_provider()`. AES-CM counts over the full 128-bit block, as
RFC 3711 and aws-lc-rs do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title rind-str0m feat(str0m): rind-str0m, a str0m CryptoProvider on rind Nov 7, 2026
dimpl's constructors assert a nonempty certificate and expect a key they
can load, and str0m's RtcConfig::set_dtls_cert takes both from the caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 18:53
@pullfrog

pullfrog Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Your OpenCode Go account is out of credit. Top it up, then re-trigger Pullfrog.

OpenCode Go's response
provider error: Insufficient account funds

Top up OpenCode Go →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using glm-5.3

@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 47 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7cf5f8d4-2910-440f-92ac-80fe553f4060
Commits

Reviewing files that changed from the base of the PR and between 00610af and 2bd6ec8.

Files ignored due to path filters: 1

  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 20

  • .github/workflows/build.yml
  • Cargo.toml
  • MIGRATE.md
  • MIGRATING.md
  • README.md
  • quest/m1/moq/README.md
  • quest/m2/adoption/README.md
  • quest/m2/adoption/rind-str0m.md
  • rind-str0m/Cargo.toml
  • rind-str0m/LICENSE
  • rind-str0m/README.md
  • rind-str0m/src/dtls.rs
  • rind-str0m/src/hash.rs
  • rind-str0m/src/lib.rs
  • rind-str0m/src/srtp.rs
  • rind-str0m/tests/ctgrind.rs
  • rind-str0m/tests/differential.rs
  • rind-str0m/tests/rtc.rs
  • rind-str0m/tests/vectors.rs
  • sh/armv7.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated
kixelated added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit ee3d5d1 Nov 7, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant