- 同態加密(HE)
HE是一種特殊的加密方法,它允許直接對加密數據執行計算,如加法和乘法,而計算過程不會泄露原文的任何信息。計算的結果仍然是加密的,擁有密鑰的用戶對處理過的密文數據進行解密后,得到的正好是處理后原文的結果。
根據支持的計算類型和支持程度,同態加密可以分為以下三種類型:
- 半同態加密(Partially Homomorphic Encryption, PHE):只支持加法或乘法中的一種運算。其中,只支持加法運算的又叫加法同態加密(Additive Homomorphic Encryption, AHE);
- 部分同態加密(Somewhat Homomorphic Encryption, SWHE):可同時支持加法和乘法運算,但支持的計算次數有限;
- 全同態加密(Fully Homomorphic Encryption, FHE):支持任意次的加法和乘法運算。
- Paillier半同態加密方案
Paillier是一個支持加法同態的公鑰密碼系統 [1],由Paillier在1999年的歐密會(EUROCRYPT)上首次提出。此后,在PKC'01中提出了Paillier方案的簡化版本,是當前Paillier方案的最優方案。在眾多PHE方案中,Paillier方案由於效率較高、安全性證明完備的特點,在各大頂會和實際應用中被廣泛使用,是隱私計算場景中最常用的PHE實例化方案之一。
- Paillier的rust實現
算法原理我閱讀的是這位大佬的文章:Paillier半同態加密:原理、高效實現方法和應用 ,說的非常詳細。
- KeyGen():密鑰生成算法。用於產生加密數據的公鑰PK(Public Key)和私鑰SK(Secret Key),以及一些公開常數PP(Public Parameter):
a、隨機選擇兩個大素數 滿足
,且滿足
長度相等
b、計算 以及
,這里
表示最小公倍數,
為
的比特長度
c、隨機選擇整數 ,下面代碼中用的是優化后的算法,取g = n + 1
d、定義 函數:
,計算
,這地方的μ我在測試中發現就是λ-1,代碼中直接計算的lcm-1 mod n
公鑰: ,私鑰:
1 pub struct PaillierKeypair{ 2 // public key 3 pub n: BigUint, 4 pub n_exp2: BigUint, 5 pub g: BigUint, 6 // private key 7 pub lcm: BigUint, 8 // u = (L(g^lcm mod n^2)) mod n = lcm 9 } 10 impl fmt::Display for PaillierKeypair{ 11 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 write!(f, "(\nn = {}\nn^2 = {}\ng = {}\nlcm = {}\n)", 13 self.n,self.n_exp2, self.g,self.lcm) 14 } 15 } 16 17 pub fn key_gen(bits_n:usize)-> PaillierKeypair{ 18 loop { 19 let p = Generator::new_prime(bits_n / 2); // 這里實際應用中可以多任務加速的,我跑起來比較耗時,可以優化 20 let q = Generator::new_prime(bits_n / 2); // 當bits_n = 1536時,用時甚至達到1分鍾 21 let n = p.checked_mul(&q).unwrap(); // n = p*q 22 23 if n.bits() != bits_n { 24 continue 25 } 26 // check: gcd(pq,(p-1)(q-1)) == 1 27 let mut temp = p.checked_sub(&ONE).unwrap(); // p-1 28 temp = temp.checked_mul(&q.checked_sub(&ONE).unwrap()).unwrap(); // (p-1)(q-1) 29 30 if n.gcd(&temp) != *ONE { 31 continue 32 } 33 34 let n_exp2 = n.checked_mul(&n).unwrap(); // n^2 35 let g = n.checked_add(&ONE).unwrap(); // g = n + 1 36 let lcm = (p.checked_sub(&ONE).unwrap()).lcm(&(q.checked_sub(&ONE).unwrap())); 37 38 return PaillierKeypair { 39 n, // p*q 40 n_exp2, // n^2 這里計算主要是為了后面加解密方便,提速 41 g, 42 lcm, // 最小公倍數lcm((p-1)(q-1)) = λ 43 }; 44 } 45 }
2. Encrypt():加密算法。使用PK對用戶數據Data進行加密,得到密文CT(Ciphertext);
a、輸入明文消息 , 滿足
b、選擇隨機數 滿足
且
c、計算密文
1 pub fn encrypt(message: &BigUint,key: &PaillierKeypair)->BigUint{ // 這個地方message的范圍是[0,n)半開區間,當message大於n時會錯誤,開始沒注意查了很久 2 let r = Generator::new_uint(key.n.bits()).mod_floor(&key.n); // 隨機r的取值同樣是[0,n) 3 // g^m = (n+1)^m = (mn + 1) mod n^2 4 let g_exp_m = message.checked_mul(&key.n).unwrap() 5 .checked_add(&ONE).unwrap() 6 .mod_floor(&key.n_exp2); 7 8 let r_exp_n = r.modpow(&key.n,&key.n_exp2); 9 let c = g_exp_m.checked_mul(&r_exp_n).unwrap(); 10 c.mod_floor(&key.n_exp2) 11 }
3. Decrypt():解密算法。用於解密得到數據原文PT(Plaintext);
a、輸入密文 ,滿足
b、 計算明文消息
1 pub fn mod_inverse(a: &BigUint, n: &BigUint)->Option<BigUint>{ //這個主要求解模逆運算:a^-1 mod n ,沒找到現成的運算接口,自己擼了一個,用的是擴展歐幾里得算法 2 let signed_a = BigInt::from_biguint(Sign::Plus,a.clone()); // 擴展歐幾里得算法可能會出現負數,所以我轉一下,不確定專業庫是否有更好的辦法 3 let signed_n = BigInt::from_biguint(Sign::Plus,n.clone()); 4 let extend_gcd = signed_a.extended_gcd(&signed_n); 5 if extend_gcd.gcd.ne(&BigInt::one()){ 6 return None 7 } 8 if extend_gcd.x.gt(&&BigInt::zero()) { 9 Some(extend_gcd.x.to_biguint().unwrap()) 10 }else { 11 Some((extend_gcd.x + unsigned_n).to_biguint().unwrap()) 12 } 13 }
1 pub fn decrypt(cypher: &BigUint,key: &PaillierKeypair)->BigUint{ 2 let u = mod_inverse(&key.lcm,&key.n).unwrap(); // 計算μ,為了速度可以放到秘鑰生成的時候,畢竟秘鑰對只要一次就可以,加解密才是運行最多的代碼 3 cypher.modpow(&key.lcm,&key.n_exp2) 4 .checked_sub(&ONE).unwrap() 5 .checked_div(&key.n).unwrap() 6 .checked_mul(&u).unwrap() 7 .mod_floor(&key.n) 8 }
4. 加解密的驗證代碼
#[test] fn encrypt_decrypt_test(){ let keys = key_gen(256); //生成256bits的秘鑰對 // g^m * r^n mod n^2 let message = Generator::new_prime(256).mod_floor(&keys.n_exp2); // 隨機數生成后一定要取n的模,否者解密對不上,因為解密的結果就是有限域[0,n)內 println!("message: {}",message); let cypher = encrypt(&message,&keys); println!("cypher: {}",cypher); let message = decrypt(&cypher,&keys); println!("message: {}",message); }
隨機的一個結果,驗證是正確的,可以看到明文空間message在n個比特位,而密文空間則是2n的比特位:
1 message: 66974503675301339632978118530573227132140576565216955902029048867114353816463 2 cypher: 1580877749905632525039727741115670439885127886872359880625218265544553014636237278105806831783772438286839148487172139171820994824898226125064321022362594 3 message: 66974503675301339632978118530573227132140576565216955902029048867114353816463
將bits_n參數改為規范的1536后,秘鑰的生成比較費時,加解密速度我比較滿意,一個結果:
1 message: 1252684931009798105744886157798912173919623779017951705401972293985120531626650393138647048991249424015358398974415833034240029525367790100454746009445616906108452612800233545937932132033401054195527373989843081853540308637524031020576866862133481586260487701455003009450625730007049098610496206308660661144686380509082731332049148456268629649541550807600476702833752962698861650432874278762915550474003254147043302816313167343001675822871227906876870952697912363 2 cypher: 91458756898925932591572235226490802504721979964759783902619875482271203393604895228531274503840751337641010436621213721263209500520214054452098964782515565617546433200781417032127193574747779244640713495808227786561857236592307932151460176482010583605181022284631545630619923886447574646746128408527052746772364343005705077708559384361740186441515196060956650060939898536900563554727453497752356839724393471346793037607582993245437284075542939121810424690643012231922690506028686567313483583186821778187848768507153177975808084372417406434211778158991629395012236176981582219916652227890387391621931089482849258855512513949600191201756208634324187989201812785248311075030812797456023879791353169268254968201351924548497547736664449534915899847846124453851263632198495757622154634736904295603655826927286374264360586445195358143053862853930711025411764808509563477304903234951133207018886127992385530687897040204369060819878 3 message: 1252684931009798105744886157798912173919623779017951705401972293985120531626650393138647048991249424015358398974415833034240029525367790100454746009445616906108452612800233545937932132033401054195527373989843081853540308637524031020576866862133481586260487701455003009450625730007049098610496206308660661144686380509082731332049148456268629649541550807600476702833752962698861650432874278762915550474003254147043302816313167343001675822871227906876870952697912363
5. 同態加實現於驗證代碼
a、對於明文 m1 和 m2 , 計算 m = m1 + m2 mod n
b、對於密文 和
,計算
1 #[test] 2 fn paillier_add_test(){ 3 let keypair = key_gen(256); 4 5 let message0 = Generator::new_prime(256).mod_floor(&keypair.n); 6 let message1 = Generator::new_prime(256).mod_floor(&keypair.n); 7 println!("message0:{}",message0); 8 println!("message1:{}",message1); 9 10 let cypher0 = encrypt(&message0,&keypair); 11 let cypher1 = encrypt(&message1,&keypair); 12 13 let sum_raw = (message0+message1).mod_floor(&keypair.n); 14 println!("plaintext sum:{}",sum_raw); 15 16 let sum_cypher = cypher0.checked_mul(&cypher1).unwrap() 17 .mod_floor(&keypair.n_exp2); 18 let decrypted = decrypt(&sum_cypher,&keypair); 19 println!("ciphertext decrypted sum :{}",decrypted); 20 assert_eq!(sum_raw,decrypted); 21 22 }
隨機的一次結果顯示(加密中有隨機數r導致每次結果不同),達到預期效果:
message0:80572675196213302274781046440925488076062768477540430842285233194904118761087 message1:75330398095297333423599060505544509578495617068994807014615413620411449663583 plaintext sum:70628361780147599051583823081065583758277794528232401024661717273681824114561 ciphertext decrypted sum :70628361780147599051583823081065583758277794528232401024661717273681824114561
修改到1536比特位key的隨機一次結果(比較耗時),:
message0:981241317054349089148524566105637038505190329391459972594035858135788691817167238665303375476049525265414506655443779767820351043735520937984152385641997998052982292618924926746413884437004669543875165490772310691519057966522411218878188795396637999548368673325051576190634533828541034781803593282427223238986324567695549078345676930567058349372987225302857037548233632447843010243211309725885654100552608434728655678616607255114327566078247430873988316740716047 message1:133823190640697178740024093649235391846831968400682841540758432284623592178685358256341638757715856326961765431735399473172755097164936764955756659077870992547532591005153559403262882062362470608675258805393433089503986924642349272395787714376661407094480094172405918774310887974205993891187590280020348306100413116807635993787344590051608701903096860617799013220309140201458310410635562433282923781858204053114833339784605723320949415632842049847705945741150718 plaintext sum:1115064507695046267888548659754872430352022297792142814134794290420412283995852596921645014233765381592376272087179179240993106140900457702939909044719868990600514883624078486149676766499367140152550424296165743781023044891164760491273976509773299406642848767497457494964945421802747028672991183562447571545086737684503185072133021520618667051276084085920656050768542772649301320653846872159168577882410812487843489018401212978435276981711089480721694262481866765 ciphertext decrypted sum :1115064507695046267888548659754872430352022297792142814134794290420412283995852596921645014233765381592376272087179179240993106140900457702939909044719868990600514883624078486149676766499367140152550424296165743781023044891164760491273976509773299406642848767497457494964945421802747028672991183562447571545086737684503185072133021520618667051276084085920656050768542772649301320653846872159168577882410812487843489018401212978435276981711089480721694262481866765
6. 同態標量乘法實現於驗證代碼
a、對於明文空間 m1 和 a , 計算 m = a * m1 mod n
b、 對於密文 和標量
,計算
1 fn paillier_scala_mul_test(){ 2 let keypair = key_gen(256); 3 4 let a = Generator::new_uint(32); // 生成一個隨機32bits的整數 5 let message = Generator::new_prime(256).mod_floor(&keypair.n); 6 println!("message:{}",message); 7 println!("a:{}",a); 8 let message_mul_a = message.clone().mul(&a).mod_floor(&keypair.n); 9 println!("plaintext ScalaMul: {}",message_mul_a); 10 11 let cypher = encrypt(&message,&keypair); 12 let cypher_exp_a = cypher.modpow(&a,&keypair.n_exp2); 13 let decrypted = decrypt(&cypher_exp_a,&keypair); 14 15 println!("decrypted ScalaMul: {}",decrypted); 16 assert_eq!(message_mul_a,decrypted); 17 18 }
隨機一次測試結果:
message:33881166959344009350892522926807888414391558053525841173372553541039646592819 a:1028573599 plaintext ScalaMul: 84987263896517518588122173040042618261677792733688091835755914749117111689180 decrypted ScalaMul: 84987263896517518588122173040042618261677792733688091835755914749117111689180
bits_n擴展到1536后,隨機一次結果,測試通過:
message:336904875687065699756037862153680043531666842141845237320562288460274184952266107168259942436780215909209542844162788565290287909810097658153237331523473656144170107795859582642314735693068770358936974958539171474606565716685655227151044490268529273541593084865520480721259091847423361873435749512068048242843552222111364971284497424434983509503745200682525086343817260021790648672304225694887295901445746490359354557019188908268713676738986349343948646740413047 a:3928419561 plaintext ScalaMul: 110690937513873058124794849927088700013403464344629003586096074170264668081500005560518618969348961941431184636502187644166497295354595125590832141689212672211267763451829619875245497886055758386362503609652988193111042024926860417019096337831573028629066426476229998805176236209617055473949814930635131358022285842277763974133312077153399755755402668938483187368666734151323133378676575001681723139986999559916624447700566129373278884470388279312362334209964073 decrypted ScalaMul: 110690937513873058124794849927088700013403464344629003586096074170264668081500005560518618969348961941431184636502187644166497295354595125590832141689212672211267763451829619875245497886055758386362503609652988193111042024926860417019096337831573028629066426476229998805176236209617055473949814930635131358022285842277763974133312077153399755755402668938483187368666734151323133378676575001681723139986999559916624447700566129373278884470388279312362334209964073
總結:這次paillier的實現除了秘鑰對生成比較耗時外,其他都還令人滿意!秘鑰對的生成一方面可以多線程並行實現優化,另外大素數的算法實現,實際應用中甚至可以用查表實現,沒必要實時的計算。