Paillier半同態加密rust實現筆記


  • 同態加密(HE)

  HE是一種特殊的加密方法,它允許直接對加密數據執行計算,如加法和乘法,而計算過程不會泄露原文的任何信息。計算的結果仍然是加密的,擁有密鑰的用戶對處理過的密文數據進行解密后,得到的正好是處理后原文的結果。

根據支持的計算類型和支持程度,同態加密可以分為以下三種類型:

  1. 半同態加密(Partially Homomorphic Encryption, PHE):只支持加法或乘法中的一種運算。其中,只支持加法運算的又叫加法同態加密(Additive Homomorphic Encryption, AHE);
  2. 部分同態加密(Somewhat Homomorphic Encryption, SWHE):可同時支持加法和乘法運算,但支持的計算次數有限;
  3. 全同態加密(Fully Homomorphic Encryption, FHE):支持任意次的加法和乘法運算。
  • Paillier半同態加密方案

  Paillier是一個支持加法同態的公鑰密碼系統 [1],由Paillier在1999年的歐密會(EUROCRYPT)上首次提出。此后,在PKC'01中提出了Paillier方案的簡化版本,是當前Paillier方案的最優方案。在眾多PHE方案中,Paillier方案由於效率較高、安全性證明完備的特點,在各大頂會和實際應用中被廣泛使用,是隱私計算場景中最常用的PHE實例化方案之一。

  • Paillier的rust實現

  算法原理我閱讀的是這位大佬的文章:Paillier半同態加密:原理、高效實現方法和應用 ,說的非常詳細。

  1. 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 = m+ m 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的實現除了秘鑰對生成比較耗時外,其他都還令人滿意!秘鑰對的生成一方面可以多線程並行實現優化,另外大素數的算法實現,實際應用中甚至可以用查表實現,沒必要實時的計算。

 

  

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM