前序博客见:
Zama团队的Marc Joye 2021年论文 Guide to Fully Homomorphic Encryption over the [Discretized] Torus,开源代码实现见:
具体各相关常量值定义为:
/// 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;
加密算法以(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=Tp⊂T,且 q = 2 Ω q=2^{\Omega} q=2Ω。
如若输入消息取值范围为 0 ∽ 15 0\backsim 15 0∽15,相应的编解码为:
// 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
}
注意,在代码实现中:
代码见lwe.rs
文件。
LWE代码实现中取 n = 630 n=630 n=630。
注意,由于 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);
}
}
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);
}
}
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
}
关键的“gadget decomposition”技术示例为:
不过TLWE实际代码中,取 ℓ = 4 , B = 2 4 = 16 , q = 2 64 \ell=4,B=2^4=16,q=2^{64} ℓ=4,B=24=16,q=264:【表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ( B ) ∗ ℓ = 4 ∗ 4 = 16 \log(B)*\ell=4*4=16 log(B)∗ℓ=4∗4=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ˉi≡∑j=1ℓui,jBℓ−j(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} ℓ=4,B=24=16,q=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
}
代码见glwe.rs
文件。
GLWE代码实现中,取 N = 1024 , k = 1 N=1024,k=1 N=1024,k=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) Xj⋅p(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+i≡−Xi(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+i≡Xi(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 }
}
实际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);
}
}
代码见ggsw.rs
文件。`
TGGSW加密为:
TGGSW密文,有一组TGLWE密文组成:【共 ( k + 1 ) ℓ (k+1)\ell (k+1)ℓ个TGLWE密文】
msg * q/B^l
的加密。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} G∈TN,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} ℓ=2,B=28=256,q=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
}
其中:
>> 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ℓ/q⌉modp。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} G−1为:【TGGSW实际代码中,取 ℓ = 2 , B = 2 8 = 256 , q = 2 64 \ell=2,B=2^8=256,q=2^{64} ℓ=2,B=28=256,q=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)∗ℓ=8∗2=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) G−1(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()
}
TFHE中,TGGSW密文与TGLWE密文的external product运算,的主要应用场景为:
由于:
相应的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
}
在代码实现中, E n c r y p t 和 D \mathfrak{E}ncrypt和\mathfrak{D} Encrypt和D加解密算法对应为TLWE,而 E n c r y p t 和 D e c r y p t Encrypt和Decrypt Encrypt和Decrypt加解密算法对应为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
}
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()
}
其中,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=264,2N=2∗1024=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 }
}
modswitch
之后的TLWE密文。 /// 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
}
经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−μ~∗⋅v∈PN[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,
}
}
/// 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)
}
}
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} ℓ=4,log(B)=4,B=24=16,q=264(表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ( B ) ∗ ℓ = 4 ∗ 4 = 16 \log(B)*\ell=4*4=16 log(B)∗ℓ=4∗4=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
}
其中TLWE中decomposition matrix G \mathbf{G} G的逆变换 G − 1 \mathbf{G}^{-1} G−1对应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} ℓ=4,log(B)=4,B=24=16,q=264(表示将(范围在 Z 2 64 Z_{2^{64}} Z264内的)多项式系数的 log ( B ) ∗ ℓ = 4 ∗ 4 = 16 \log(B)*\ell=4*4=16 log(B)∗ℓ=4∗4=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
}
整个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=0N−1p⌊p∗i/(2N)⌉modpXi∈PN[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