Skip to content

fix(sign): keep unbounded hashing and signing from starving the async executor - #183

Merged
wolfv merged 1 commit into
mainfrom
artifact/prehashed-signing
Aug 17, 2026
Merged

wolfv merged 1 commit into
mainfrom
artifact/prehashed-signing

Conversation

@wolfv

@wolfv wolfv commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes TOB-SIGSTORE-8: unbounded artifact hashing and ECDSA signing no longer monopolize an async executor thread or hash the same input twice.

  • Hashes blobs and DSSE PAE input incrementally in 64 KiB chunks.
  • Yields between chunks without coupling the library to a runtime.
  • Adds prehashed ECDSA signing, preserving signatures equivalent to ECDSA-SHA256 over the complete message.
  • Avoids materializing a second full-size DSSE PAE buffer.

Stack

  1. This PR: incremental/prehashed signing foundation
  2. refactor(artifact)!: make artifact digests typed #184: typed blob/digest artifact API
  3. feat(artifact)!: stream artifacts from sync and async readers #185: synchronous and asynchronous reader APIs

Supersedes #169.

Signed-off-by: Wolf Vollprecht w.vollprecht@gmail.com

… executor (TOB-SIGSTORE-8)

Trail of Bits finding TOB-SIGSTORE-8 (informational, denial of service):
Signer::sign synchronously hashed and ECDSA-signed unbounded
Artifact::Bytes input inside an async fn, so a large artifact could
occupy an executor worker thread for its entire duration and starve
other tasks. KeyPair::sign additionally hashed the full message a
second time inside aws-lc-rs before producing the signature.

Mechanism:

- sigstore-crypto grows KeyPair::sign_prehashed(Sha256Hasher), built on
  aws-lc-rs EcdsaKeyPair::sign_digest. It signs the incrementally
  computed SHA-256 digest directly, producing a signature that verifies equivalently
  to ECDSA-SHA256 over the raw message while making the
  signing step O(1) in the message size (and dropping the SystemRandom
  handle the full-message path constructs).
- sigstore-sign hashes caller-controlled input (artifact bytes and DSSE PAE bytes) in 64 KiB chunks without materializing a full PAE copy, yielding to the executor between chunks
  via a small runtime-agnostic yield_now future (no new dependency,
  works on any executor), then signs the precomputed digest.
- Signer::sign and Signer::sign_raw_statement document the behavior;
  statement JSON validation stays synchronous since statements carry
  metadata, not artifact contents.

Tests cover chunked-vs-one-shot digest equivalence and that prehashed
signatures verify as ECDSA-SHA256 over the full message.

Signed-off-by: Wolf Vollprecht <w.vollprecht@gmail.com>
@wolfv wolfv changed the title artifact/prehashed signing fix(sign): keep unbounded hashing and signing from starving the async executor Aug 14, 2026
@wolfv
wolfv marked this pull request as ready for review August 14, 2026 14:39
@jku

jku commented Aug 17, 2026

Copy link
Copy Markdown
Member

Preserving the discussion from earlier PR

I don't quite undertand why the sign_prehashed() argument is Sha256Hasher and not the digest

  • we call finish_digest() anyway as the first thing in sign_prehashed
  • when we call sha256_yielding() we always await anyway

It's possible I don't fully grasp the async rust model... I guess what I'm asking is : Does this design have an advantage over just adding sigstore_crypto::sha256_yielding() that has the same signature as sigstore_crypto::sha256 but yields

There is one important advantage to carrying Sha256Hasher: if a yielding helper returned only Sha256Hash, calling KeyPair::sign(bytes) would synchronously hash the complete input a second time. aws-lc-rs::sign_digest needs its digest object, so sign_prehashed() finalizes and signs the same cooperatively-computed digest without another pass. Awaiting controls scheduling but does not make the finalized typed hash directly usable by sign_digest. I rebased this onto current main and preserved this behavior.

@jku jku left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, looks reasonable

@wolfv
wolfv merged commit f75984d into main Aug 17, 2026
17 checks passed
@wolfv
wolfv deleted the artifact/prehashed-signing branch August 17, 2026 11:40
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