Skip to main content

confium_tc_ml_kem/
lib.rs

1//! Threshold ML-KEM (FIPS 203) — research prototype.
2//!
3//! **No production-quality threshold ML-KEM exists today.** This crate
4//! is a research prototype. Production use requires academic collaborator
5//! engagement (see `TODO.roadmap/26`).
6//!
7//! Research questions:
8//! - Proactive share refresh for lattice schemes
9//! - Threshold decryption ceremony with audit trail
10//! - Re-encryption for quorum composition changes
11//! - Composition with AEAD for symmetric encryption
12//! - Cross-tier re-encryption (IA → BIML without plaintext exposure)
13
14#![forbid(unsafe_code)]
15#![allow(missing_docs)] // TODO: document before 1.0
16
17use serde::{Deserialize, Serialize};
18
19/// ML-KEM parameter sets (FIPS 203).
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(rename_all = "kebab-case")]
22pub enum ParameterSet {
23    /// ML-KEM-512 (NIST Level 1 / AES-128 equivalent).
24    MlKem512,
25    /// ML-KEM-768 (NIST Level 3 / AES-192 equivalent). Recommended default.
26    MlKem768,
27    /// ML-KEM-1024 (NIST Level 5 / AES-256 equivalent).
28    MlKem1024,
29}
30
31impl ParameterSet {
32    /// Public key size in bytes for this parameter set.
33    pub fn public_key_size(&self) -> usize {
34        match self {
35            ParameterSet::MlKem512 => 800,
36            ParameterSet::MlKem768 => 1184,
37            ParameterSet::MlKem1024 => 1568,
38        }
39    }
40
41    /// Ciphertext (encapsulated key) size.
42    pub fn ciphertext_size(&self) -> usize {
43        match self {
44            ParameterSet::MlKem512 => 768,
45            ParameterSet::MlKem768 => 1088,
46            ParameterSet::MlKem1024 => 1568,
47        }
48    }
49
50    /// Shared secret size (always 32 bytes).
51    pub fn shared_secret_size(&self) -> usize {
52        32
53    }
54}
55
56/// Threshold ML-KEM public key.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct ThresholdPublicKey {
59    /// Parameter set.
60    pub params: ParameterSet,
61    /// Public key bytes.
62    pub bytes: Vec<u8>,
63}
64
65/// Share of the threshold ML-KEM secret key.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct Share {
68    /// Parameter set.
69    pub params: ParameterSet,
70    /// Party index.
71    pub party_index: u32,
72    /// Share bytes.
73    pub bytes: Vec<u8>,
74}
75
76/// Errors during threshold ML-KEM operations.
77#[derive(Debug, thiserror::Error)]
78pub enum MlKemError {
79    /// Parameter mismatch.
80    #[error("parameter set mismatch")]
81    ParamMismatch,
82    /// Threshold not met.
83    #[error("threshold not met: have {have}, need {need}")]
84    ThresholdNotMet {
85        /// Have count.
86        have: usize,
87        /// Need count.
88        need: u32,
89    },
90    /// Research-only operation.
91    #[error("operation requires research collaborator engagement: {0}")]
92    ResearchOnly(String),
93}
94
95/// Construct a placeholder public key (research only).
96pub fn placeholder_public_key(params: ParameterSet) -> ThresholdPublicKey {
97    ThresholdPublicKey {
98        params,
99        bytes: vec![0u8; params.public_key_size()],
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn parameter_set_sizes() {
109        assert_eq!(ParameterSet::MlKem512.public_key_size(), 800);
110        assert_eq!(ParameterSet::MlKem768.public_key_size(), 1184);
111        assert_eq!(ParameterSet::MlKem1024.public_key_size(), 1568);
112    }
113
114    #[test]
115    fn shared_secret_always_32() {
116        for params in [
117            ParameterSet::MlKem512,
118            ParameterSet::MlKem768,
119            ParameterSet::MlKem1024,
120        ] {
121            assert_eq!(params.shared_secret_size(), 32);
122        }
123    }
124}