Skip to content

comnet building blocks: ML-DSA seed/context/fingerprints, PRF, Shamir, STREAM, multi-recipient seal, key lifecycle - #24

Draft
mm9942 wants to merge 9 commits into
mainfrom
claude/enterprise-pqc-comnet-bklgym
Draft

mm9942 wants to merge 9 commits into
mainfrom
claude/enterprise-pqc-comnet-bklgym

Conversation

@mm9942

@mm9942 mm9942 commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

This PR adds the crypt_guard building blocks for the comnet v2 post-quantum messenger, in two milestones. All changes are additive: no existing signatures change, and no variants are added to exhaustive enums.

  • M0-min (94471f9): seed-based, panic-free ML-DSA key handling; FIPS 204 signing with a context string; key fingerprints; SECURITY.md. This commit is verified locally.
  • M0-ext (10add53): PRF, Shamir secret sharing, STREAM chunked AEAD, a multi-recipient seal bound to the recipient set, pinned-suite open, try_network_handle, and key lifecycle metadata. The user asked for this code to be written without compiling, so it is unverified until CI runs.

Changes

M0-min

  • MlDsa{44,65,87}Impl gains:
    • generate_seed, keypair_from_seed, try_keypair, verifying_key_from_seed;
    • sign_with_context (hedged, via TryOsRng), sign_with_context_deterministic (for KATs), verify_with_context;
    • fingerprint and the constants PARAMETER_SET_ID, VERIFYING_KEY_LEN, SIGNATURE_LEN.
  • RecipientPublicKey::fingerprint.
  • New vector file: the LAMPS / RFC 9881 example keys (seed 00..1f), listed in SHA256SUMS.
  • SECURITY.md is now tracked; it was previously excluded by *.md.
  • release_checks.sh runs cargo semver-checks.

M0-ext

  • prf: SHAKE256 PRF with domain label and length prefixes (prf, prf32). The output length is bound into the input.
  • sharing: Shamir over GF(2^8) with constant-time field arithmetic (split, combine). Share commitments make a bad share attributable to whoever submitted it; this is for break-glass M-of-N.
  • pq_stream: STREAM construction over ChaCha20-Poly1305. The nonce is prefix ‖ counter ‖ last-flag, so reordering, truncation and extension are detected. Also derive_stream_key and random_prefix.
  • pq_multi:
    • seal_multi / open_multi: one content key, wrapped per recipient with pq_hpke. The info and the body AAD are bound to the hash of the recipient set.
    • dummy_wrap, for padding the wrap count.
  • HpkeEnvelope::open_pinned_zeroizing: rejects a substituted wire suite before decapsulation.
  • sha3 is built with zeroize.
  • Service:
    • try_network_handle: returns Unavailable when no runtime is present and Malformed for unusable bounds.
    • KeyLifecycle and CryptoOperation::DescribeLifecycle / CryptoResponse::Lifecycle.
    • Per-version creation timestamps in InMemoryProvider, plus with_clock for tests.
  • One behaviour change: OpSet::READ_PUBLIC and ALL now include the new DescribeLifecycle bit. Grants built from Describe alone must add it. This is recorded in the CHANGELOG under "Changed".

Testing

  • M0-min: all lanes green locally: fmt, clippy -D warnings with all features and with default features, the facade/core, service and hyper tests, dependency gates, provenance. Details are in the first commit.

  • M0-ext: tests/m0_ext_primitives.rs and new unit tests:

    • PRF / Shamir / stream / set-hash known-answer vectors, computed independently with Python hashlib;
    • GF(256) checks over every pair of field elements;
    • negative tests for reordering, truncation, tampering, a substituted suite and an outsider recipient;
    • service lifecycle and try_network_handle tests.

    None of these have been run yet. See the CI note in the PR comments: Actions runners currently are not assigned to any job.

Security notes

  • Fail-closed throughout: typed errors and no panics on the new paths.
  • Known exception, documented in SECURITY.md: KEM encapsulation inside HpkeEnvelope::seal still uses rand::rngs::OsRng, which panics on entropy failure. This affects seal_multi and dummy_wrap.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd

Additive, panic-free API for seed-based key management, needed by comnet's
local vault (private keys stay on the device, only 32-byte seeds are stored):

- MlDsa{44,65,87}Impl::generate_seed / keypair_from_seed / try_keypair /
  verifying_key_from_seed: no path through the panicking OsRng.
- sign_with_context (hedged FIPS 204 randomized variant via TryOsRng),
  sign_with_context_deterministic (KATs only), verify_with_context.
- MlDsa*Impl::fingerprint and RecipientPublicKey::fingerprint: SHA3-256
  key ids bound to the parameter set / KEM.
- Known-answer test against the LAMPS / RFC 9881 example keys (seed 00..1f)
  and fingerprint KATs computed independently with Python hashlib.
- SECURITY.md is now tracked (it was excluded by `*.md` in .gitignore).
- release_checks.sh requires `cargo semver-checks check-release`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd

mm9942 commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

CI status: every job in RustTests failed on run 41263658257, on both the first attempt and the re-run (attempt 2). That covers all build lanes, clippy, feature-matrix, dependency-gates and supply-chain. These failures don't come from this PR's code:

  • Each job ended 2–3 seconds after it was created, with runner_id: 0 and an empty runner name. No runner was ever assigned, so no step ran, and nothing was checked out, built or tested.
  • The GitHub-managed CodeQL run for this PR (41263656720) failed the same way within 4 seconds.
  • The last RustTests and CodeQL runs on main (d9c9417) were green.

The usual cause is an account-level GitHub Actions limit, such as exhausted minutes or a spending limit, or Actions being disabled for this repository. A code change can't fix that. Before pushing, the same checks passed locally on this head:

  • cargo fmt --check
  • cargo clippy --workspace --all-targets -D warnings, with all features and with default features
  • the facade/core, service and hyper test lanes
  • check_dep_gates.sh and verify_provenance.sh

Once runners are available again, CI needs to run on this head.


Generated by Claude Code

…n, key lifecycle

Additive building blocks for comnet v2 (written without compiling, per the
user's directive; needs a CI run once Actions runners are available):

core
- prf: labelled SHAKE256 PRF/KDF (prf, prf32), output length bound in.
- sharing: Shamir over GF(2^8) with constant-time field arithmetic, split /
  combine, share commitments for attributable bad shares (break-glass M-of-N).
- pq_stream: STREAM chunked ChaCha20-Poly1305 (prefix | counter | last flag),
  detects reordering, truncation, extension; derive_stream_key, random_prefix.
- pq_multi: multi-recipient seal bound to the recipient set (seal_multi,
  open_multi, recipient_set_hash, dummy_wrap for wrap-count padding).
- HpkeEnvelope::open_pinned_zeroizing: rejects a substituted wire suite before
  decapsulation.
- sha3 built with `zeroize`.

service
- try_network_handle: panic-free network_handle (Unavailable without a Tokio
  runtime, Malformed for unusable bounds).
- KeyLifecycle + CryptoOperation::DescribeLifecycle / CryptoResponse::Lifecycle,
  per-version creation timestamps in InMemoryProvider (with_clock for tests).

Docs: CHANGELOG [Unreleased], README recipes, SECURITY.md entropy notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd
@mm9942 mm9942 changed the title ML-DSA: seed-derived keys, context signing and key fingerprints (3.2.0 prep) comnet building blocks: ML-DSA seed/context/fingerprints, PRF, Shamir, STREAM, multi-recipient seal, key lifecycle Nov 1, 2026
…, AES-256-GCM-SIV inner) in progress

Written without compiling (user directive).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd
- pq_multi: back to the 10add53 state. The last WIP commit made it depend
  on a `pq_cascade` module that was never committed, so the whole core
  crate failed to compile. The pq-cascade KAT vectors stay in place for
  when the module lands.
- tests/ml_dsa_seed_context.rs: gated on `ml-dsa-backend`; the
  `legacy-pqclean` lane builds without that feature.
- core/lib.rs: rustfmt module order.
- docs/plans/crypt-guard-pyo3-contract.md: contract draft for the Python
  bindings crate; .gitignore now tracks docs/**/*.md.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkDG5sDvfZRz5J21a2N
…rate layout, lints)

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkDG5sDvfZRz5J21a2N
…s) into the comnet branch

pq_multi.rs keeps the cascade functions (seal_multi_cascade, open_multi_cascade,
dummy_cascade_wrap): pq_cascade now exists, so the reason for the revert in
993f714 is gone. lib.rs takes the rustfmt module order from 993f714.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uh7jkEmwFarxC573DQgybd
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.

2 participants