1. 引言

前序博客见:

Zama团队的Marc Joye 2021年论文 Guide to Fully Homomorphic Encryption over the [Discretized] Torus,开源代码实现见:

在该论文中,标记规则为:
在这里插入图片描述

  • 现代架构中的位精度通常为32位或64位,本代码实现选择的是64位,即 q = 2 64 q=2^{64} q=264
  • 本代码实现中,取明文modulus p = 2 4 = 16 p=2^{4}=16 p=24=16,对应的取值范围为 [ − 8 , 7 ) [-8,7) [8,7) 0 ∽ 15 0\backsim 15 015
  • LWE代码实现中取 n = 630 n=630 n=630;GLWE代码实现中,取 N = 1024 , k = 1 N=1024,k=1 N=1024,k=1
    在这里插入图片描述
  • 由于 B ∈ { 0 , 1 } \mathbb{B}\in\{0,1\} B{0,1},实际TLWE加密密钥中 s i s_i si值要么为0,要么为1;实际TGLWE加密密钥中 s i \mathfrak{s}_i si值为多项式 B N [ X ] = B [ X ] / ( X N + 1 ) = { 0 , 1 } [ X ] / ( X N + 1 ) \mathbb{B}_N[X]=\mathbb{B}[X]/(X^N+1)=\{0, 1\}[X]/(X^N + 1) BN[X]=B[X]/(XN+1)={0,1}[X]/(XN+1)
  • TLWE实际代码中,取 ℓ = 4 , B = 2 4 = 16 , q = 2 64 \ell=4,B=2^4=16,q=2^{64} =4B=24=16q=264;TGGSW实际代码中,取 ℓ = 2 , B = 2 8 = 256 , q = 2 64 \ell=2,B=2^8=256,q=2^{64} =2B=28=256q=264

具体各相关常量值定义为:

/// Decomposition basis for the external product. This value is used implicitely.
// pub const B: usize = 256;

/// Ciphertext modulus. This value is used implicitely.
// pub const Q: usize = 2^64;

/// Plaintext modulus
pub const P: usize = 16;

/// Number of decomposition layers for the external product.
pub const ELL: usize = 2;

/// GLWE dimension
#[allow(non_upper_case_globals)]
pub const k: usize = 1;

/// Degree `N` of irreducible polynomial X^N + 1
pub const N: usize = 1024;

/// Dimension of LWE ciphertexts
pub const LWE_DIM: usize = 630;

2. encode/decode

加密算法以(discretized)torus元素,或更准确来说,以 P \mathcal{P} P中元素为输入。Encoding/Decoding编解码的目的,是为了支持更多的输入形式。

M \mathcal{M} M为任意有限消息空间,其cardinality # M = p \#\mathcal{M}=p #M=p,且 p = 2 v p=2^v p=2v。明文空间 P = T p ⊂ T \mathcal{P}=\mathbb{T}_p\sub\mathbb{T} P=TpT,且 q = 2 Ω q=2^{\Omega} q=2Ω

  • 编码函数为: E n c o d e : M → P Encode:\mathcal{M}\rightarrow \mathcal{P} Encode:MP,将消息 m ∈ M m\in\mathcal{M} mM映射为 μ ∈ P \mu\in\mathcal{P} μP元素。编码用在加密之前。
  • 解码函数为: D e c o d e : P → M Decode:\mathcal{P}\rightarrow \mathcal{M} Decode:PM,将 μ ∈ P \mu\in\mathcal{P} μP元素映射为 m ∈ M m\in\mathcal{M} mM消息。解码用于解密之后。

如若输入消息取值范围为 0 ∽ 15 0\backsim 15 015,相应的编解码为:

// let msg = thread_rng().gen_range(0..16); // msg范围为0~15.
pub fn encode(msg: u8) -> u64 {
    (msg as u64) << 60
}
//decode中有round操作,所以不是直接mu>>60,而是((mu >> 59) + 1) >> 1
pub fn decode(mu: u64) -> u8 {
    ((((mu >> 59) + 1) >> 1) % 16) as u8
}

注意,在代码实现中:

  • TLWE和TGLWE均为对编码后的明文进行加密,解密后再解码才是原始明文。
  • TGGSW为直接对明文进行加密,解密后的即为明文。无需编解码操作。

3. TLWE加解密及其密文运算

代码见lwe.rs文件。
LWE代码实现中取 n = 630 n=630 n=630

3.1 TLWE加解密

在这里插入图片描述
注意,由于 B ∈ { 0 , 1 } \mathbb{B}\in\{0,1\} B{0,1},实际加密密钥中 s i s_i si值要么为0,要么为1。

// 即经TLWE加密后的密文结构,为(a_1, ..., a_n, b)
pub struct LweCiphertext {
    pub mask: Vec<u64>, //即为:(a_1, ..., a_n)
    pub body: u64, //即为:b=\sum_{j=1}^n s_j\cdot a_j + \mu + e
}
// 为加密密钥(s_1, ..., s_n)
pub type LweSecretKey = Vec<u64>;
// 实际加密密钥中s_i值要么为0,要么为1。
pub fn lwe_keygen() -> LweSecretKey {
    let mut sk = Vec::<u64>::with_capacity(LWE_DIM);
    for _ in 0..LWE_DIM {
        sk.push(thread_rng().gen_range(0..=1));
    }

    sk
}

TLWE加密为:【LWE_DIM,即为 n n n值,根据table 2,取 n = 630 n=630 n=630 μ \mu μ值为encode(msg)

	pub fn encrypt(mu: u64, sk: &LweSecretKey) -> LweCiphertext {
        let sigma = f64::powf(2.0, 49.0);
        let normal = Normal::new(0.0, sigma).unwrap();

        let e = normal.sample(&mut rand::thread_rng()).round() as i64; // small noise,为i64,有符号位整数
        let mu_star = mu.wrapping_add_signed(e); // (mu + e) mod 2^{64}
		//取随机值(a_1, ..., a_n)
        let mask: Vec<u64> = (0..LWE_DIM).map(|_| rand::random::<u64>()).collect();

        let mut body = 0u64;
        for i in 0..LWE_DIM {
            if sk[i] == 1 {
                body = body.wrapping_add(mask[i]);
            }
        }
		//即为:b=\sum_{j=1}^n s_j\cdot a_j + \mu + e
        body = body.wrapping_add(mu_star);

        LweCiphertext { mask, body }
    }

TLWE解密为:【实际返回的是 μ ∗ \mu^* μ,而不是round后的 μ \mu μ值。】

	pub fn decrypt(self, sk: &LweSecretKey) -> u64 {
        let mut body: u64 = 0u64;
        for i in 0..sk.len() { //计算\sum_{j=1}^{n}s_j \cdot a_j
            if sk[i] == 1 {
                body = body.wrapping_add(self.mask[i]);
            }
        }
		// 计算 \mu^* = b-\sum_{j=1}^{n}s_j \cdot a_j
        self.body.wrapping_sub(body) // mu_star
    }

decrypt()函数是 μ ∗ \mu^* μ,需调用decode()函数round后,才获得解密的msg值。
对应的测试用例为:

	#[test]
    fn test_keygen_enc_dec() {
        let sk = lwe_keygen();
        for _ in 0..100 {
            let msg = thread_rng().gen_range(0..16);
            let ct = LweCiphertext::encrypt(encode(msg), &sk);
            let pt = decode(ct.decrypt(&sk));
            assert_eq!(pt, msg);
        }
    }

3.2 TLWE密文加减法运算

在这里插入图片描述

	pub fn add(self, rhs: Self) -> Self {
        let mask = self
            .mask
            .iter()
            .zip(rhs.mask)
            .map(|(a, b)| a.wrapping_add(b))
            .collect();

        let body = self.body.wrapping_add(rhs.body);

        LweCiphertext { mask, body }
    }
	pub fn sub(self, rhs: &Self) -> Self {
        let mask = self
            .mask
            .iter()
            .zip(&rhs.mask)
            .map(|(a, b)| a.wrapping_sub(*b))
            .collect();

        let body = self.body.wrapping_sub(rhs.body);

        LweCiphertext { mask, body }
    }

测试用例为:

	#[test]
    fn test_add() {
        let sk = lwe_keygen();
        for _ in 0..100 {
            let msg1 = thread_rng().gen_range(0..16);
            let msg2 = thread_rng().gen_range(0..16);
            let ct1 = LweCiphertext::encrypt(encode(msg1), &sk);
            let ct2 = LweCiphertext::encrypt(encode(msg2), &sk);
            let res = ct1.add(ct2);
            let pt = decode(res.decrypt(&sk));
            assert_eq!(pt, (msg1 + msg2) % 16);
        }
    }

    #[test]
    fn test_sub() {
        let sk = lwe_keygen();
        for _ in 0..100 {
            let msg1 = thread_rng().gen_range(0..16);
            let msg2 = thread_rng().gen_range(0..16);
            let ct1 = LweCiphertext::encrypt(encode(msg1), &sk);
            let ct2 = LweCiphertext::encrypt(encode(msg2), &sk);
            let res = ct1.sub(&ct2);
            let pt = decode(res.decrypt(&sk));
            assert_eq!(pt, (msg1.wrapping_sub(msg2)) % 16);
        }
    }

3.3 TLWE密文与某已知常量值的乘法

在这里插入图片描述

	pub fn multiply_constant_assign(&mut self, constant: u64) -> &mut Self {
        self.mask = self.mask.iter().map(|a| a.wrapping_mul(constant)).collect();

        self.body = self.body.wrapping_mul(constant);

        self
    }

3.4 TLWE密文之间的乘法运算

3.4.1 gadget decomposition

关键的“gadget decomposition”技术示例为:
在这里插入图片描述
不过TLWE实际代码中,取 ℓ = 4 , B = 2 4 = 16 , q = 2 64 \ell=4,B=2^4=16,q=2^{64} =4B=24=16q=264:【表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ⁡ ( B ) ∗ ℓ = 4 ∗ 4 = 16 \log(B)*\ell=4*4=16 log(B)=44=16个最高有效位(MSB),decompose为 ℓ \ell 个整数 u i , j u_{i,j} ui,j u i , j u_{i,j} ui,j的取值范围为 [ − ⌊ B / 2 ⌋ , ⌈ B / 2 ⌉ ) = [ − 8 , 8 ) = [ − 8 , 7 ) [-\lfloor B/2\rfloor, \lceil B/2\rceil)=[-8,8)=[-8,7) [B/2,B/2⌉)=[8,8)=[8,7)。】

/// Approximate decomposition with lg(B) = 4 and ell = 4.
/// Takes a polynomial coefficient in Z_{2^64} and decomposes its 16 MSBs in 4 integers in `[-8, 7] as u64`.
pub fn decomposition_4_4(val: u64) -> [u64; 4] { //输入val,对应为v_i
    let mut ret = [0u64; 4]; //此处的4,与ell值对应。
    let rounded_val = round_value(val); //对应为\bar{v}_i

    let mut carry = 0u64;
    for i in 0..4 { // 实际ret中计算的为$G^{-1}(v_i)$,即相应的u_{i,j}
        let mut res = ((rounded_val >> (4 * i)) & 0x0F) + carry;

        let carry_bit = res & 8;

        res = res.wrapping_sub(carry_bit << 1);
        ret[i] = res;

        carry = carry_bit >> 3;
    }

    ret
}

注意:
v ˉ i ≡ ∑ j = 1 ℓ u i , j B ℓ − j ( m o d    B ℓ ) \bar{v}_i\equiv \sum_{j=1}^{\ell}u_{i,j}B^{\ell-j}(\mod B^{\ell}) vˉij=1ui,jBj(modB),其中 u i , j ∈ [ − ⌊ B / 2 ⌋ , ⌈ B / 2 ⌉ ) u_{i,j}\in[-\lfloor B/2\rfloor,\lceil B/2\rceil) ui,j[B/2,B/2⌉)
其中对 B ℓ B^{\ell} B做模运算,代码中对应 ℓ = 4 , B = 2 4 = 16 , q = 2 64 \ell=4,B=2^4=16,q=2^{64} =4B=24=16q=264,对应的round函数计算为:

//实际即为对$B^{\ell}=16^4=2^{16}$做round计算
pub fn round_value(val: u64) -> u64 {
    let mut rounded_val = val >> 47;
    rounded_val += rounded_val & 1;
    rounded_val >>= 1;
    rounded_val
}

4. TGLWE加解密及其密文运算

代码见glwe.rs文件。
GLWE代码实现中,取 N = 1024 , k = 1 N=1024,k=1 N=1024,k=1

4.1 多项式表示

多项式表示见poly.rs

TGLWE将TLWE加密扩展到 T N , q [ X ] \mathbb{T}_{N,q}[X] TN,q[X] torus多项式。其中 T N , q [ X ] = T q [ X ] / ( X N + 1 ) \mathbb{T}_{N,q}[X]=\mathbb{T}_q[X]/(X^N+1) TN,q[X]=Tq[X]/(XN+1)多项式以ResiduePoly表示为:

/// Represents an element of Z_{q}\[X\]/(X^N + 1) with implicit q = 2^64.
#[derive(Clone, Serialize, Deserialize)]
pub struct ResiduePoly {
    pub coefs: Vec<u64>,
}

相应的多项式运算,对应为其系数的运算。

get_random用于生成TGLWE加密算法中mask a i \mathfrak{a}_i ai需用到随机多项式:

	/// Generates a residue polynomial with random coefficients in \[0..2^64)
    pub fn get_random() -> Self {
        let coefs = (0..N).map(|_| rand::random::<u64>()).collect();

        Self { coefs }
    }

get_random_bin用于生成TGLWE加密算法中加密私钥 s i \mathfrak{s}_i si为多项式 B N [ X ] = B [ X ] / ( X N + 1 ) = { 0 , 1 } [ X ] / ( X N + 1 ) \mathbb{B}_N[X]=\mathbb{B}[X]/(X^N+1)=\{0, 1\}[X]/(X^N + 1) BN[X]=B[X]/(XN+1)={0,1}[X]/(XN+1)

	/// Generates a residue polynomial with random coefficients in \[0..1\]
    pub fn get_random_bin() -> Self {
        let coefs = (0..N).map(|_| thread_rng().gen_range(0..=1)).collect();

        Self { coefs }
    }

在这里插入图片描述

X j ⋅ p ( X ) X^j\cdot \mathfrak{p}(X) Xjp(X)单项式乘法运算multiply_by_monomial为:【有 X N + i ≡ − X i ( m o d    X N + 1 ) X^{N+i}\equiv -X^i(\mod X^N+1) XN+iXi(modXN+1) X 2 N + i ≡ X i ( m o d    X N + 1 ) X^{2N+i}\equiv X^i(\mod X^N+1) X2N+iXi(modXN+1)

	/// Tests that the monomial multiplication is coherent with monomial multiplication.
    fn test_monomial_mult() {
        for _ in 0..1000 {
            let mut monomial_coefs = vec![0u64; N];
            let monomial_non_null_term = thread_rng().gen_range(0..2 * N);

            if monomial_non_null_term < 1024 {
                monomial_coefs[monomial_non_null_term] = 1;
            } else {
                monomial_coefs[monomial_non_null_term % 1024] = 1u64.wrapping_neg();
            }

            let monomial = ResiduePoly {
                coefs: monomial_coefs,
            };

            let polynomial = ResiduePoly::get_random();

            let res_mul = polynomial.mul(&monomial);
            let res_monomial_mul = polynomial.multiply_by_monomial(monomial_non_null_term);

            assert_eq!(res_mul.coefs, res_monomial_mul.coefs);
        }
    }

	/// Multiplies the residue polynomial by X^{exponent} = X^{2N + exponent}.
    /// `exponent` is assumed to be reduced modulo 2N.
    pub fn multiply_by_monomial(&self, exponent: usize) -> Self {
        let mut rotated_coefs = Vec::<u64>::with_capacity(N);

        let reverse = exponent >= N;
        let exponent = exponent % N;

        for i in 0..N {
            rotated_coefs.push({
                if i < exponent {
                    if reverse {
                        self.coefs[i + N - exponent]
                    } else {
                        self.coefs[i + N - exponent].wrapping_neg()
                    }
                } else if reverse {
                    self.coefs[i - exponent].wrapping_neg()
                } else {
                    self.coefs[i - exponent]
                }
            })
        }

        ResiduePoly {
            coefs: rotated_coefs,
        }
    }

	// TODO: use FFT for better performances
    pub fn mul(&self, rhs: &ResiduePoly) -> Self {
        let mut coefs = Vec::<u64>::with_capacity(N);
        for i in 0..N {
            let mut coef = 0u64;
            for j in 0..i + 1 {
                coef = coef.wrapping_add(self.coefs[j].wrapping_mul(rhs.coefs[i - j]));
            }
            for j in i + 1..N {
                coef = coef.wrapping_sub(self.coefs[j].wrapping_mul(rhs.coefs[N - j + i]));
            }
            coefs.push(coef);
        }
        ResiduePoly { coefs }
    }

4.2 TGLWE加解密及加减法运算

在这里插入图片描述

实际TGLWE加密密钥中 s i \mathfrak{s}_i si值为多项式 B N [ X ] = B [ X ] / ( X N + 1 ) = { 0 , 1 } [ X ] / ( X N + 1 ) \mathbb{B}_N[X]=\mathbb{B}[X]/(X^N+1)=\{0, 1\}[X]/(X^N + 1) BN[X]=B[X]/(XN+1)={0,1}[X]/(XN+1)

	/// Generates a residue polynomial with random coefficients in \[0..1\]
    pub fn get_random_bin() -> Self {
        let coefs = (0..N).map(|_| thread_rng().gen_range(0..=1)).collect();

        Self { coefs }
    }
pub struct GlweCiphertext { //密文
    pub mask: Vec<ResiduePoly>, //为多项式列表
    pub body: ResiduePoly, //为多项式
}

/// Set of `k` polynomials in {0, 1}\[X\]/(X^N + 1).
#[derive(Clone)]
pub struct SecretKey {
    pub polys: Vec<ResiduePoly>, //加密密钥,为多项式列表
}

注意实际代码实现中,明文 μ \mu μ取的是u64整数,而不是多项式,对应解密时也是取的常量项值:

	// 注意输入明文mu为u64整数
	pub fn encrypt(mu: u64, sk: &SecretKey) -> GlweCiphertext {
        let sigma = f64::powf(2.0, 39.0);
        let normal = Normal::new(0.0, sigma).unwrap();

        let e = normal.sample(&mut rand::thread_rng()).round() as i64;
        let mu_star = mu.wrapping_add_signed(e);

        let mask: Vec<ResiduePoly> = (0..k).map(|_| ResiduePoly::get_random()).collect();

        let mut body = ResiduePoly::default();
        for i in 0..k {
            body.add_assign(&mask[i].mul(&sk.polys[i]));
        }

        body.add_constant_assign(mu_star as u64);

        GlweCiphertext { mask, body }
    }

	pub fn decrypt(&self, sk: &SecretKey) -> u64 {
        let mut body = ResiduePoly::default();
        for i in 0..k {
            body.add_assign(&self.mask[i].mul(&sk.polys[i]));
        }

        let mu_star = self.body.sub(&body);
        mu_star.coefs[0] //解密时,取的是常量项值
    }

同时TGLWE密文具有加法同态性:

	#[test]
    fn test_keygen_enc_dec() {
        let sk = keygen();
        for _ in 0..100 {
            let msg = thread_rng().gen_range(0..16);
            let ct = GlweCiphertext::encrypt(encode(msg), &sk);
            let pt = decode(ct.decrypt(&sk));
            assert_eq!(pt, msg);
        }
    }

    #[test]
    fn test_add() {
        let sk = keygen();
        for _ in 0..100 {
            let msg1 = thread_rng().gen_range(0..16);
            let msg2 = thread_rng().gen_range(0..16);
            let ct1 = GlweCiphertext::encrypt(encode(msg1), &sk);
            let ct2 = GlweCiphertext::encrypt(encode(msg2), &sk);
            let res = ct1.add(&ct2);
            let pt = decode(res.decrypt(&sk));
            assert_eq!(pt, (msg1 + msg2) % 16);
        }
    }

    #[test]
    fn test_sub() {
        let sk = keygen();
        for _ in 0..100 {
            let msg1 = thread_rng().gen_range(0..16);
            let msg2 = thread_rng().gen_range(0..16);
            let ct1 = GlweCiphertext::encrypt(encode(msg1), &sk);
            let ct2 = GlweCiphertext::encrypt(encode(msg2), &sk);
            let res = ct1.sub(&ct2);
            let pt = decode(res.decrypt(&sk));
            assert_eq!(pt, (msg1.wrapping_sub(msg2)) % 16);
        }
    }

5. TGGSW加解密及其密文运算

代码见ggsw.rs文件。`

5.1 TGGSW加解密

TGGSW加密为:
在这里插入图片描述
TGGSW密文,有一组TGLWE密文组成:【共 ( k + 1 ) ℓ (k+1)\ell (k+1)个TGLWE密文】

  • 1) k ℓ k\ell k个对应为mask。
  • 2) ℓ \ell 个对应为body。
  • 3)最后一个TGLWE密文,对应为对msg * q/B^l的加密。
  • 4)TGLWE密文,要远短于TGGSW密文。
pub struct GgswCiphertext {
    z_m_gt: Vec<GlweCiphertext>, //共$(k+1)\ell$个
}

其中gadget matrix G ∈ T N , q [ X ] ( k + 1 ) × ( k + 1 ) ℓ \mathbf{G}\in\mathbb{T}_{N,q}[X]^{(k+1)\times (k+1)\ell} GTN,q[X](k+1)×(k+1) 的转置矩阵 G T \mathbf{G}^T GT为:【共 ( k + 1 ) ℓ (k+1)\ell (k+1)行, ( k + 1 ) (k+1) (k+1)列】
在这里插入图片描述
TGGSW实际代码中,取 ℓ = 2 , B = 2 8 = 256 , q = 2 64 \ell=2,B=2^8=256,q=2^{64} =2B=28=256q=264,并因代码中固定 k = 1 k=1 k=1,从而TGGSW加密实现为:【不过需注意,此处的TGGSW加密是为后续的bootstrapping中的blind rotation服务,实际是额外再乘以了 q q q,对应的"gadget vector" g \mathbf{g} g 不再是 ( 1 / B , ⋯   , 1 / B ℓ ) ∈ T q l (1/B,\cdots,1/B^{\ell})\in\mathbb{T}_q^l (1/B,,1/B)Tql,而是 ( q / B , ⋯   , q / B ℓ ) ∈ T q l (q/B,\cdots,q/B^{\ell})\in\mathbb{T}_q^l (q/B,,q/B)Tql

	pub fn encrypt(msg: u8, sk: &SecretKey) -> Self {
        // initialize Z
        let mut z_m_gt: Vec<GlweCiphertext> = (0..(k + 1) * ELL)
            .map(|_| GlweCiphertext::encrypt(0, sk))
            .collect();

        // m * g, g being [q/B, ..., q/B^l]
        let mut mg = [0u64; ELL];
        //此处处理不够灵活,相当于固定了k=1值。
        mg[0] = (msg as u64) << 56; //注意此处msg为u8类型,取值范围为0~15
        mg[1] = (msg as u64) << 48;

        // add m * G^t to Z
        for i in 0..z_m_gt.len() {
            if i < k * ELL { //处理mask部分
                for j in 0..z_m_gt[i].mask.len() {
                    z_m_gt[i].mask[j].add_constant_assign(mg[i % ELL]);
                }
            } else { //处理body部分
                z_m_gt[i].body.add_constant_assign(mg[i % ELL]);
            }
        }

        GgswCiphertext { z_m_gt }
    }

TGGSW密文中的最后一个TGLWE密文,对应为对msg * q/B^l的加密。所以,TGGSW的解密,可表示为对最后一个TGLWE的解密:

	// The last `GlweCiphertext` of `z_m_gt` is an encryption of msg * q/B^l
    pub fn decrypt(self, sk: &SecretKey) -> u8 {
        ((((&self.z_m_gt[self.z_m_gt.len() - 1].decrypt(sk) >> 47) + 1) >> 1) % 16) as u8
    }

其中:

  • 对解密结果 μ ∗ \mu^* μ>> 47) + 1) >> 1) % 16) as u8,表示的是 m s g = ⌊ μ ∗ ∗ B ℓ / q ⌉ m o d    p msg=\lfloor \mu^**B^{\ell}/q\rceil\mod p msg=μB/qmodp

5.3 TGGSW和TGLWE密文乘法运算

在这里插入图片描述

TGGSW和TGLWE密文乘法运算,对应为ggsw.rs中的external_product函数:

	/// Performs a product (GGSW x GLWE) -> GLWE.
    pub fn external_product(&self, ct: &GlweCiphertext) -> GlweCiphertext {
        let g_inverse_ct = apply_g_inverse(ct);

        let mut res = GlweCiphertext::default();
        for i in 0..(k + 1) * ELL {
            for j in 0..k {
                res.mask[j].add_assign(&g_inverse_ct[i].mul(&self.z_m_gt[i].mask[j]));
            }
            res.body
                .add_assign(&g_inverse_ct[i].mul(&self.z_m_gt[i].body));
        }
        res
    }

在这里插入图片描述

此时的decomposition matrix G \mathbf{G} G的逆变换 G − 1 \mathbf{G}^{-1} G1为:【TGGSW实际代码中,取 ℓ = 2 , B = 2 8 = 256 , q = 2 64 \ell=2,B=2^8=256,q=2^{64} =2B=28=256q=264,并因代码中固定 k = 1 k=1 k=1。】【表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ⁡ ( B ) ∗ ℓ = 8 ∗ 2 = 16 \log(B)*\ell=8*2=16 log(B)=82=16个最高有效位(MSB),decompose为 ℓ = 2 \ell=2 =2个整数 u i , j u_{i,j} ui,j u i , j u_{i,j} ui,j的取值范围为 [ − ⌊ B / 2 ⌋ , ⌈ B / 2 ⌉ ) = [ − 128 , 128 ) = [ − 128 , 127 ] [-\lfloor B/2\rfloor, \lceil B/2\rceil)=[-128,128)=[-128,127] [B/2,B/2⌉)=[128,128)=[128,127]。】

/// Approximate decomposition with lg(B) = 8 and ell = 2.
/// Takes a polynomial coefficient in Z_{2^64} and decomposes its 16 MSBs in two signed 8-bit integers.
pub fn decomposition_8_2(val: u64) -> (i8, i8) {
    let rounded_val = round_value(val);
    if rounded_val & 128 == 128 {
        (rounded_val as i8, ((rounded_val >> 8) + 1) as i8)
    } else {
        (rounded_val as i8, (rounded_val >> 8) as i8)
    }
}

对应的 G − 1 ( c 2 ) \mathbf{G}^{-1}(\mathbf{c}_2) G1(c2)运算为:【其中 c 2 \mathbf{c}_2 c2为TGLWE密文。分别对mask和body进行decomposition。】

/// Decomposition of a GLWE ciphertext.
fn apply_g_inverse(ct: &GlweCiphertext) -> Vec<ResiduePoly> {
    let mut res: [ResiduePoly; (k + 1) * ELL] = Default::default();

    for i in 0..N {
        // mask decomposition
        for j in 0..k {
            let (nu_2, nu_1) = decomposition_8_2(ct.mask[j].coefs[i]);
            res[j * ELL].coefs[i] = nu_1 as u64;
            res[j * ELL + 1].coefs[i] = nu_2 as u64;
        }

        // body decomposition
        let (nu_2, nu_1) = decomposition_8_2(ct.body.coefs[i]);
        res[(k + 1) * ELL - 2].coefs[i] = nu_1 as u64;
        res[(k + 1) * ELL - 1].coefs[i] = nu_2 as u64;
    }
    res.to_vec()
}

5.4 CMux

TFHE中,TGGSW密文与TGLWE密文的external product运算,的主要应用场景为:

  • “controlled” multiplexer,或简称为CMUX。
    在这里插入图片描述
    在这里插入图片描述

由于:

  • TGLWE密文具有加法同态属性
  • TGGSW密文与TGLWE密文具有乘法同态属性

相应的CMux的输入参数均为密文:

/// Ciphertext multiplexer. If `ctb` is an encryption of `1`, return `ct2`. Else, return `ct1`.
pub fn cmux(ctb: &GgswCiphertext, ct1: &GlweCiphertext, ct2: &GlweCiphertext) -> GlweCiphertext {
    let mut res = ct2.sub(ct1);
    res = ctb.external_product(&res);
    res = res.add(ct1);
    res
}

6. bootstrapping

在这里插入图片描述

在这里插入图片描述

6.1 bootstrapping key自举密钥

在这里插入图片描述
在代码实现中, E n c r y p t 和 D \mathfrak{E}ncrypt和\mathfrak{D} EncryptD加解密算法对应为TLWE,而 E n c r y p t 和 D e c r y p t Encrypt和Decrypt EncryptDecrypt加解密算法对应为TGGSW——底层本质为TGLWE。因此,对应的自举密钥为,以TGGSW加密算法,逐个对TLWE的私钥 s s s进行加密。【TLWE私钥 s s s的长度为LWE_DIM(630),且每个 s i s_i si值为 0 0 0或者 1 1 1。】

/// Encrypts the bits of `s` under `sk`
pub fn compute_bsk(s: &LweSecretKey, sk: &SecretKey) -> BootstrappingKey {
    let bsk: Vec<GgswCiphertext> = (0..LWE_DIM)
        .map(|i| GgswCiphertext::encrypt(s[i].try_into().unwrap(), sk))
        .collect();

    bsk
}

6.2 recode

在这里插入图片描述
recode的目的,是将TGLWE私钥,转换为TLWE私钥:

	/// Converts a GLWE secret key into a LWE secret key.
    // TODO: generalize for k > 1
    pub fn recode(&self) -> LweSecretKey {
        self.polys[0]
            .coefs
            .iter()
            .copied()
            .map(|coef| coef)
            .collect()
    }

6.3 blind rotate

在这里插入图片描述
其中,modswitch函数,用于将TLWE密文中的各值由模 q q q调整为模 2 N 2N 2N:【此代码实现中,有 q = 2 64 , 2 N = 2 ∗ 1024 = 2 11 q=2^{64},2N=2*1024=2^{11} q=2642N=21024=211。】

	/// Switch from ciphertext modulus `2^64` to `2N` (implicit `N = 1024`).
    pub fn modswitch(&self) -> Self {
        let mask = self.mask.iter().map(|a| ((a >> 52) + 1) >> 1).collect();

        let body = ((self.body >> 52) + 1) >> 1;

        LweCiphertext { mask, body }
    }

在这里插入图片描述

在这里插入图片描述
其中:

  • c ~ \tilde{\mathbf{c}} c~即为modswitch之后的TLWE密文。
  • c = ( 0 , ⋯   , 0 , v ) \mathfrak{c}=(0,\cdots,0,v) c=(0,,0,v),为TGLWE密文。
	/// Performs the blind rotation of `self`.
    // `self` is assumed to be a trivial encryption
    // `c` is a modswitched LWE ciphertext (modulus = 2N)
    pub fn blind_rotate(&self, c: LweCiphertext, bsk: &BootstrappingKey) -> Self {
    	//self为TGLWE密文
        let mut c_prime = self.clone();
		//利用了$X^{2N-i}\equiv X^{-i} \mod (X^N+1)$
        c_prime.rotate_trivial((2 * N as u64) - c.body);//计算c'=X^{-\tidle{b}}\cdot c
        for i in 0..LWE_DIM { //LWE_DIM,为TLWE中的n值
            c_prime = cmux(&bsk[i], &c_prime, &c_prime.rotate(c.mask[i]));
        }

        c_prime
    }

    /// Multiplies by the monomial `X^exponent` the body of `self`.
    /// `self` is assumed to be a trivial encryption.
    fn rotate_trivial(&mut self, exponent: u64) { //因TGLWE密文$\mathfrak{c}=(0,\cdots,0,v)$,所以仅需要关注body乘以单项式。
    	//不过TGLWE密文的body本身也是多项式
        self.body = self.body.multiply_by_monomial(exponent as usize);
    }
    
    //考虑TGLWE密文中的mask为非零的情况。
	/// Multiplies by the monomial `X^exponent` every component of `self`.
    pub fn rotate(&self, exponent: u64) -> Self {
        let mut res = Self::default();
        for i in 0..k {
            res.mask[i] = self.mask[i].multiply_by_monomial(exponent as usize);
        }

        res.body = self.body.multiply_by_monomial(exponent as usize);

        res
    }

6.4 sample extraction

经BlindRotate之后,可将明文 μ ∈ P \mu\in\mathcal{P} μP的TLWE密文,转换为,常量项为 μ \mu μ的多项式明文 μ ( X ) : = X − μ ~ ∗ ⋅ v ∈ P N [ X ] \mu(X):=X^{-\tilde{\mu}^*}\cdot \mathfrak{v}\in\mathcal{P}_N[X] μ(X):=Xμ~vPN[X] 的TGLWE密文。基于不同的key,通过 μ \mu μ的refreshed TLWE加密,可提取该常量项。该过程称为sample extraction。

需注意,尽管其用于常量项,该技术也可调整为用于提取 μ \mu μ的其它元素。
在这里插入图片描述

在这里插入图片描述

	/// Converts a GLWE ciphertext into a LWE ciphertext of dimension `N`.
    // TODO: generalize for k > 1
    pub fn sample_extract(&self) -> LweCiphertext {
    	//self为TGLWE密文
        let mut mask = [0u64; N];
        mask[0] = self.mask[0].coefs[0];
        for i in 1..N { //固定为k=1的情况
            mask[i] = self.mask[0].coefs[N - i].wrapping_neg();
        }

        let body = self.body.coefs[0]; //仅提取常量项值

        LweCiphertext {
            mask: mask.to_vec(),
            body,
        }
    }

6.5 key switching

在这里插入图片描述

	/// This test fails from time to time.
    /// It might be the case that B = 16, ell = 4 isn't suitable for keyswitching.
    #[test]
    fn test_keyswitching() {
        let sk1 = lwe_keygen();
        let sk2 = keygen();
        let ksk = compute_ksk(&sk2.recode(), &sk1); // list of encryptions under `sk1` of the bits of `sk2`.

        for _ in 0..100 {
            let msg = thread_rng().gen_range(0..8);
            let ct = GlweCiphertext::encrypt(encode(msg), &sk2).sample_extract();
            let ks = ct.keyswitch(&mut ksk.clone());
            let res = ks.decrypt(&sk1);
            let pt = decode(res);

            assert_eq!(msg, pt)
        }
    }

6.5.1 key switching密钥

在这里插入图片描述
key switching密钥中, s s s s i ′ s_i' si均为TLWE私钥:【注意TLWE实际代码中,取 ℓ = 4 , log ⁡ ( B ) = 4 , B = 2 4 = 16 , q = 2 64 \ell=4,\log(B)=4, B=2^4=16,q=2^{64} =4log(B)=4,B=24=16q=264(表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ⁡ ( B ) ∗ ℓ = 4 ∗ 4 = 16 \log(B)*\ell=4*4=16 log(B)=44=16个最高有效位(MSB),decompose为 ℓ \ell 个整数 u i , j u_{i,j} ui,j u i , j u_{i,j} ui,j的取值范围为 [ − ⌊ B / 2 ⌋ , ⌈ B / 2 ⌉ ) = [ − 8 , 8 ) = [ − 8 , 7 ) [-\lfloor B/2\rfloor, \lceil B/2\rceil)=[-8,8)=[-8,7) [B/2,B/2⌉)=[8,8)=[8,7)。)】

/// Encrypts `sk1` under `sk2`.
// TODO: generalize for k > 1
pub fn compute_ksk(sk1: &LweSecretKey, sk2: &LweSecretKey) -> KeySwitchingKey {
    let mut ksk = Vec::<LweCiphertext>::with_capacity(4 * N);

    for bit in sk1.iter().take(N) {
        // 4 layers in the decomposition for the KSK
        for j in 0..4 {
            let mu = bit << (48 + (4 * j)); // lg(B) = 4
            ksk.push(LweCiphertext::encrypt(mu, sk2));
        }
    }
    ksk
}

6.5.2 key switch

在这里插入图片描述
其中TLWE中decomposition matrix G \mathbf{G} G的逆变换 G − 1 \mathbf{G}^{-1} G1对应decomposition_4_4函数。【注意TLWE实际代码中,取 ℓ = 4 , log ⁡ ( B ) = 4 , B = 2 4 = 16 , q = 2 64 \ell=4,\log(B)=4, B=2^4=16,q=2^{64} =4log(B)=4,B=24=16q=264(表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ⁡ ( B ) ∗ ℓ = 4 ∗ 4 = 16 \log(B)*\ell=4*4=16 log(B)=44=16个最高有效位(MSB),decompose为 ℓ \ell 个整数 u i , j u_{i,j} ui,j u i , j u_{i,j} ui,j的取值范围为 [ − ⌊ B / 2 ⌋ , ⌈ B / 2 ⌉ ) = [ − 8 , 8 ) = [ − 8 , 7 ) [-\lfloor B/2\rfloor, \lceil B/2\rceil)=[-8,8)=[-8,7) [B/2,B/2⌉)=[8,8)=[8,7)。)】

	/// Switch to the key encrypted by `ksk`.
    /// This reduces the dimension of the ciphertext.
    // TODO: generalize for k > 1
    pub fn keyswitch(&self, ksk: &mut KeySwitchingKey) -> Self {
        let mut keyswitched = LweCiphertext {
            body: self.body,
            ..Default::default()
        }; //这实际上是c'=(0, ..., 0, b)

        for i in (0..4 * N).step_by(4) { //TLWE中取\ell为4,即每4个对应一个a_i
            let decomp = decomposition_4_4(self.mask[i / 4]); //这实际即为g^{-1}(a_i)
            keyswitched = keyswitched //计算c'=c'-d'
                .sub(ksk[i].multiply_constant_assign(decomp[0]))
                .sub(ksk[i + 1].multiply_constant_assign(decomp[1]))
                .sub(ksk[i + 2].multiply_constant_assign(decomp[2]))
                .sub(ksk[i + 3].multiply_constant_assign(decomp[3]));
        }

        keyswitched
    }

6.6 bootstrapping

整个bootstrapping流程为:
在这里插入图片描述
目前实现还有问题,待改进:【有时成功有时失败,应该还是有bug,问题可能在keyswitch?】【为何此处范围限制为了[0,7],而不是[0,15]?】

	#[test]
  	//  #[ignore]
    fn test_bootstrapping() {
        let sk1 = lwe_keygen();
        let sk2 = keygen();
        let bsk = compute_bsk(&sk1, &sk2); // list of encryptions under `sk2` of the bits of `sk1`.
        let ksk = compute_ksk(&sk2.recode(), &sk1); // list of encryptions under `sk1` of the bits of `sk2`.

        let lut = GlweCiphertext::trivial_encrypt_lut_poly();

        for _ in 0..1 {
            let msg = thread_rng().gen_range(0..8); //为何此处范围限制为了[0,7],而不是[0,15]?

            let c = LweCiphertext::encrypt(encode(msg), &sk1).modswitch(); // "noisy" ciphertext that will be bootstrapped

            let blind_rotated_lut = lut.blind_rotate(c, &bsk); // should return a GLWE encryption of X^{- \tilde{\mu}^*} * v(X) which should be equal to a polynomial with constant term \mu.

            let res = blind_rotated_lut //为TGLWE密文
                .sample_extract() //为TLWE密文
                .keyswitch(&mut ksk.clone()) //为TLWE密文
                .decrypt(&sk1); //TLWE解密

            let pt = decode_bootstrapped(res); //解码
            assert_eq!(msg, pt)
        }
    }

其中:

  • trivial_encrypt_lut_poly函数返回的为 c = ( 0 , ⋯   , 0 , v ) \mathfrak{c}=(0,\cdots,0,v) c=(0,,0,v),其中 v : = v ( X ) = ∑ i = 0 N − 1 ⌊ p ∗ i / ( 2 N ) ⌉ m o d    p p X i ∈ P N [ X ] ⊂ T N , q [ X ] v:=v(X)=\sum_{i=0}^{N-1}\frac{\lfloor p*i/(2N)\rceil\mod p}{p}X^i\in\mathcal{P}_N[X]\sub\mathbb{T}_{N,q}[X] v:=v(X)=i=0N1ppi/(2N)⌉modpXiPN[X]TN,q[X]
/// Trivially encrypts the LUT polynomial.
    pub fn trivial_encrypt_lut_poly() -> Self { //此处self是指TGLWE密文
        // TODO: use iterator
        let mut lut_coefs = [0u64; N];

        for i in 0..N {
            lut_coefs[(i.wrapping_sub(64)) % N] = encode(((P * i) / (2 * N)).try_into().unwrap());
        }

        Self {
            body: ResiduePoly {
                coefs: lut_coefs.to_vec(),
            },
            ..Default::default()
        }
    }
  • decode_bootstrapped解码:
//decode中有round操作,所以不是直接mu>>60,而是((mu >> 59) + 1) >> 1
pub fn decode(mu: u64) -> u8 {
    ((((mu >> 59) + 1) >> 1) % 16) as u8
}
pub fn decode_bootstrapped(mu: u64) -> u8 {
    if (mu >> 63) == 1 {
        decode(!mu) % 8
    } else {
        decode(mu) % 8
    }
}

参考资料

[1] Zama团队的Marc Joye 2021年论文 Guide to Fully Homomorphic Encryption over the [Discretized] Torus

FHE系列博客

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐