Skip to main content

confium_tc_fhe_bfv/
lib.rs

1//! Threshold BFV fully homomorphic encryption — research prototype (P3).
2//!
3//! Computation on encrypted data without decryption, plus threshold:
4//! computation requires quorum agreement. Long horizon research.
5//!
6//! For OIML: statistical analysis of test reports without decrypting
7//! individual reports. Compute aggregate quality metrics across
8//! manufacturers without revealing individual measurements.
9//!
10//! See `TODO.roadmap/40-threshold-fhe.md` for full spec.
11
12#![forbid(unsafe_code)]
13#![allow(missing_docs)] // TODO: document before 1.0
14
15use serde::{Deserialize, Serialize};
16
17/// BFV parameters.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct BfvParams {
20    /// Polynomial degree (typically 4096-32768).
21    pub polynomial_degree: usize,
22    /// Plaintext modulus.
23    pub plaintext_modulus: u64,
24    /// Coefficient modulus chain (one per level).
25    pub coefficient_modulus: Vec<u64>,
26    /// Security level in bits (128 = current standard).
27    pub security_level: u32,
28}
29
30impl BfvParams {
31    /// Recommended parameters for 128-bit security with moderate performance.
32    pub fn recommended_128() -> Self {
33        Self {
34            polynomial_degree: 4096,
35            plaintext_modulus: 65537,
36            coefficient_modulus: vec![0xFFFFFFFFFFFFFFF7u64],
37            security_level: 128,
38        }
39    }
40}
41
42/// BFV public key.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct BfvPublicKey {
45    /// Parameters used to generate the key.
46    pub params: BfvParams,
47    /// Public key bytes (serialized polynomial pair).
48    pub bytes: Vec<u8>,
49}
50
51/// Secret key share (held by one party).
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct BfvSecretKeyShare {
54    /// Party index.
55    pub party_index: u32,
56    /// Share bytes (one polynomial per share).
57    pub bytes: Vec<u8>,
58}
59
60/// BFV ciphertext (pair of polynomials).
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct BfvCiphertext {
63    /// C1 component.
64    pub c1: Vec<u8>,
65    /// C2 component.
66    pub c2: Vec<u8>,
67}
68
69/// Errors during BFV operations.
70#[derive(Debug, thiserror::Error)]
71pub enum BfvError {
72    /// Parameters incompatible.
73    #[error("parameters incompatible: {0}")]
74    IncompatibleParams(String),
75    /// Threshold not met for decryption.
76    #[error("threshold not met: have {have}, need {need}")]
77    ThresholdNotMet {
78        /// Have count.
79        have: usize,
80        /// Need count.
81        need: u32,
82    },
83    /// Operation requires academic collaborator.
84    #[error("threshold BFV is research-only (P3): {0}")]
85    ResearchOnly(String),
86}
87
88/// Validate that BFV parameters are reasonable.
89pub fn validate_params(params: &BfvParams) -> Result<(), BfvError> {
90    if params.polynomial_degree < 1024 {
91        return Err(BfvError::IncompatibleParams(format!(
92            "polynomial_degree too small: {}",
93            params.polynomial_degree
94        )));
95    }
96    if !params.polynomial_degree.is_power_of_two() {
97        return Err(BfvError::IncompatibleParams(format!(
98            "polynomial_degree must be power of two: {}",
99            params.polynomial_degree
100        )));
101    }
102    if params.security_level < 128 {
103        return Err(BfvError::IncompatibleParams(format!(
104            "security_level below 128: {}",
105            params.security_level
106        )));
107    }
108    Ok(())
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn recommended_params_validate() {
117        let params = BfvParams::recommended_128();
118        validate_params(&params).unwrap();
119    }
120
121    #[test]
122    fn non_power_of_two_rejected() {
123        let params = BfvParams {
124            polynomial_degree: 5000, // not power of two
125            ..BfvParams::recommended_128()
126        };
127        let result = validate_params(&params);
128        assert!(matches!(result, Err(BfvError::IncompatibleParams(_))));
129    }
130
131    #[test]
132    fn low_security_rejected() {
133        let params = BfvParams {
134            security_level: 64,
135            ..BfvParams::recommended_128()
136        };
137        let result = validate_params(&params);
138        assert!(matches!(result, Err(BfvError::IncompatibleParams(_))));
139    }
140}