Skip to content

Conversation

@patrick-ogrady
Copy link
Contributor

@patrick-ogrady patrick-ogrady commented Nov 8, 2025

Summary

Fixes: #2182

Adds TDH2-based Threshold Encryption support for BLS12-381 (with support for batch decryption via DLEQ).

Initial Benchmarks

cargo bench -p commonware-cryptography --bench bls12381 -- bte_encrypt
  bls12381::bte_encrypt               ≈ 0.69 ms per ciphertext

cargo bench -p commonware-cryptography --bench bls12381 -- bte_decrypt

Participants n = 10:
  Threads = 1  -> 10 cts ≈ 31.7 ms | 100 cts ≈ 236 ms  | 1000 cts ≈ 1.96 s
  Threads = 8  -> 10 cts ≈ 9.84 ms | 100 cts ≈ 66.5 ms | 1000 cts ≈ 0.48 s

Participants n = 100:
  Threads = 1  -> 10 cts ≈ 216 ms  | 100 cts ≈ 1.54 s  | 1000 cts ≈ 12.4 s
  Threads = 8  -> 10 cts ≈ 38.9 ms | 100 cts ≈ 0.27 s  | 1000 cts ≈ 2.34 s

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 8, 2025

Deploying monorepo with  Cloudflare Pages  Cloudflare Pages

Latest commit: f973caa
Status: ✅  Deploy successful!
Preview URL: https://a781e8fa.monorepo-eu0.pages.dev
Branch Preview URL: https://batched-tdh.monorepo-eu0.pages.dev

View logs

for &participants in PARTICIPANTS.iter() {
let threshold = quorum(participants);
for &size in SIZES.iter() {
let id = format!("bte_decrypt/n={participants}/threads={threads}");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

fix the formatting of this

let public = PublicKey::<MinSig>::new(*commitment.constant());
let message = vec![0x42u8; 64];

c.bench_function(module_path!(), |b| {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

fix the formatting of this

use thiserror::Error;

/// Transcript namespace for ciphertext Chaum–Pedersen proofs.
const CT_TRANSCRIPT: &[u8] = b"commonware.bls12381.bte.ct";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

use - here?


/// Public key for TDH encryption (the commitment's constant term).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PublicKey<V: Variant> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nit: shouldn't need this

//! The resulting `h^r` values are re-used in the TDH KDF to unmask the original
//! plaintexts. Malformed ciphertexts simply appear as missing indices in the
//! canonical batch, so a byzantine sender cannot block honest decryptions.
//!
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nit: add a "usage" section that defines how you would use this in a p2p network (each server generates request/response from a block and then sends response to all other players).

});
});
}
#[cfg(not(feature = "std"))]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

do we need to support no-std?

.collect()
}

fn keystream<V: Variant>(hr: &V::Public, label: &[u8], len: usize) -> Vec<u8> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nit: move these to proper utilities (may be useful elsewhere)

}

let mut sorted = share_indices.to_vec();
sorted.sort_unstable();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

use .sort() instead

const PARTICIPANTS: [u32; 2] = [10, 100];
const THREADS: [usize; 2] = [1, 8];

struct BenchmarkData {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nit: just call this Data

pub struct BatchResponse<V: Variant> {
pub index: u32,
/// Positions of ciphertexts (0-indexed) that the server included in this proof.
pub valid_indices: Vec<u32>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nit: BitVec?

@patrick-ogrady patrick-ogrady changed the title [cryptography/bls12381] Batched Threshold Decryption [cryptography/bls12381] Batched Threshold Encryption Nov 8, 2025
@codecov
Copy link

codecov bot commented Nov 9, 2025

Codecov Report

❌ Patch coverage is 92.98469% with 55 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.22%. Comparing base (fc5702b) to head (f973caa).

Files with missing lines Patch % Lines
cryptography/src/bls12381/bte.rs 92.98% 55 Missing ⚠️
@@           Coverage Diff            @@
##             main    #2184    +/-   ##
========================================
  Coverage   92.21%   92.22%            
========================================
  Files         316      317     +1     
  Lines       86325    87109   +784     
========================================
+ Hits        79607    80336   +729     
- Misses       6718     6773    +55     
Files with missing lines Coverage Δ
cryptography/src/bls12381/bte.rs 92.98% <92.98%> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fc5702b...f973caa. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cronokirby
Copy link
Collaborator

This made me think of #2187 (comment) as a better way to handle parallelism in general.

/// Derive the per-ciphertext batching scalars used for the Chaum–Pedersen folding trick.
///
/// The transcript binds:
/// * `context` – caller-chosen batch domain (e.g., request id) so coefficients cannot be replayed
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Confirm this has equivalent security to https://eprint.iacr.org/2025/279

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The proof of correctness may be tied to a context but the actual partials could be useful without it?

.valid_indices
.iter()
.map(|idx| {
let mut partial = request.ciphertexts[*idx as usize].header;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This permits the partial to be used across requests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(meaning we could only release once we know for sure it is finalized -> can't do during our finalize vote)

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.

[cryptography/bls12381] Add Support for Batched Threshold Decryption

3 participants