多方安全計算(3)MPC萬能鑰匙:混淆電路

學習&轉載文章:多方安全計算(3)MPC萬能鑰匙:混淆電路

前言

我們在講解不經意傳輸(Oblivious Transfer,OT)的文章(安全多方計算(1):不經意傳輸協議)中提到,利用n選1的不經意傳輸可以解決百萬富翁問題(兩位富翁Alice和Bob在不泄露自己真實財富的情況下比對出誰更有錢),過程如圖1所示,具體過程不再展開描述。

圖片

圖1 基於n選1的OT協議實現百萬富翁問題

圖1中的例子雖然解決了兩位富翁在不泄露財富時的比對問題,但是如果遇到其他計算問題(如財富求和)時怎麼解決?是否有一種通用的方法,可以在不泄露Alice和Bob原始數據的前提下,實現各種計算問題?本篇文章將向您揭曉答案,即基於混淆電路的MPC通用場景計算

  • 重點在計算

混淆電路簡介

我們在安全多方計算系列的首篇文章(安全多方計算之前世今生)中提到,基於混淆電路(Garbled Circuit,GC)可以實現MPC通用場景計算。

什麼是混淆電路

混淆電路是雙方進行安全計算的布爾電路。混淆電路將計算電路中的每個門都加密並打亂,確保加密計算的過程中不會對外泄露計算的原始數據和中間數據。雙方根據各自的輸入依次進行計算,解密方可得到最終的正確結果,但無法得到除了結果以外的其他信息,從而實現雙方的安全計算。

混淆電路執行過程簡述

以兩方安全計算爲例,假設參與方爲Alice和Bob,則混淆電路執行過程分爲四個步驟:

  • Step 1: Alice 將計算算法轉爲混淆電路

  • Step 2: Alice 和 Bob 進行通信

  • Step 3: Bob 解密收到的混淆電路

  • Step 4: 分享結果

每一步的執行細節,將在第三節中以經典的百萬富翁問題爲例進行詳細介紹。

基於混淆電路實現安全兩方數值比較

兩方比較混淆電路

百萬富翁問題可看作是安全兩方的數值比較問題,本節將以數值比較計算方法爲例,詳述混淆電路執行過程中的四個步驟。

將數值比較算法轉化爲混淆電路

畫出數值比較算法的邏輯電路

將可計算問題轉化爲混淆電路的前提,是得到數值比較算法的邏輯電路圖

假設有兩個正整數\(x,y\)。轉換爲二進制後,\(x,y\)可分別表示爲:

圖片

其中\(x_i,y_i\in (0,1)\),當\(x>y\)時,\(x,y\)的比較過程可簡化爲:如果最高位\(x_n>y_n\),直接認定\(x>y\);否則,如果\(x_{n-1}…x_1>y_{n-1}…y_1\),認定\(x>y\),循環此過程直到最低位。

整個過程中,我們定義一個變量\(c_{i+1}\)

圖片

  • 即根據\(c_{i+1}\)就可以判斷出\(x,y\)的大小。

不失一般性,可總結爲\(c_{i+1}=1\)意味着:\((x_i>y_i)\)或($x_i=y_i $並且 \(c_i=1\))。這個總結等價於圖2中的邏輯電路圖:

同或(XNOR):相同爲真,不同爲假。

圖片

圖2 \(c_{i+1}\)表達式的等價邏輯電路

則對於正整數\(x,y\)來說,\(n\)個圖中的邏輯電路進行串聯,即可組成完整的數值比較邏輯電路,其中\(c_1=0\)。完整電路如圖3所示。

圖片

圖3 完整的正整數比較邏輯電路圖

以最底層模塊爲例生成混淆電路

(1)寫出邏輯電路的真值表

Alice畫出可計算問題的邏輯電路後,接下來需要生成整個電路的真值表。我們以整個電路中最下面的模塊爲例,爲了方便理解,我們爲每個門電路的輸出做上標記,如圖4所示。

圖片

圖4 爲模塊中的所有門電路輸出做標記

圖4中標記可理解爲:輸入爲\(x_1\)\(y_1\)的“同或門”的輸出爲\(f\)\(f\)\(c_1\)是“與門”的輸入,該門的輸出爲\(g\)等。圖4中模塊的門電路真值表如圖5所示。

圖片

圖5 將模塊中每一個門電路表示爲真值表

(2)以“同或門”爲例將真值表轉爲混淆表

以圖5中的“同或門”的真值表爲例,講解如何將真值表轉爲混淆表。真值表轉爲混淆表的過程可概括爲3個步驟:

第1步:用隨機字符串替換真值表中的0和1。

圖中的“同或門”一共有3個標籤(\(x_1、y_1、f\)),每一個標籤有0或1這2個可能的值,因此我們需要用6個隨機字符串,來代替3個標籤的0\1,這一過程如圖6所示。

  • 這裏可以看到,每個值都是不一樣的(隨機值)

圖片

圖6 用隨機字符串替換真值表中的0和1

第2步:對替換字符串後的表做加密處理。

加密處理過程如圖7所示,加密後的表有4行,每行僅有1個密文數據。圖中每一個密文數據均經過兩次對稱加密而來,其中\(E_y(f)\)表示以\(y\)爲密鑰,對明文數據\(f\)進行對稱加密

  • ⚠️留個疑問:只能用對稱加密麼?

圖片

圖7 對替換字符串後的表做加密處理

第3步:將加密表的數據排序按行隨機置亂

加密後的表有4行密文數據,如圖8所示,將4行密文數據在加密表中所處的行隨機置亂。

圖片

圖8 對替換字符串後的表做加密處理

“將真值表轉爲混淆表”這一小節中的“字符串替換->加密->置亂”的過程,就是混淆電路的核心思想。

(3)以“同或門”爲例對混淆電路進行解密

爲了便於讀者理解混淆電路的整個執行過程,先以前一小節的“同或門”混淆表爲例,講解另一參與方Bob如何對混淆電路進行解密。前一小節第3步中,Alice已完成了“同或門”混淆表的轉換工作,\(x_1\)表示Alice的輸入,\(y_1\)表示Bob的輸入。混淆電路接下來的步驟如圖9所示。

圖片

圖9 Bob以“同或門”爲例對混淆電路進行解密

此過程中,第4~6步展示的是Alice和Bob的通信過程。

第4步:Alice將“同或門”混淆表【加密表】發送給Bob。

第5步:Alice將自己真實輸入所代表的字符串明文發送給Bob,Bob雖然拿到的是明文【與真值表對應的混淆值】,但是無法知道該明文字符串表示1還是0。

第6步:Alice與Bob利用不經意傳輸(OT)協議,將Bob真實輸入所表示的兩個字符串以密文【OT加密的】形式發送,Bob根據實際輸入,正確解密【OT解密的】獲得其中一條明文字符串。

Alice這邊有\(k_{y_1}^1\)\(k_{y_1}^0\)的加密值(OT加密的),Bob通過OT協議想獲得\(k_{y_1}^1\)

第7步:此時Bob已掌握兩條明文字符串【即,\(k_{x_1}^0\)\(k_{y_1}^1\)】。利用這兩個明文字符串做密鑰,分別嘗試解密收到的“同或門”混淆表中的密文,其中僅有一條結果是正確的。

Bob如何知道哪條結果是正確的?

Alice對於“同或門”中\(f\)標籤所表示的0\1明文字符串做加密處理時,可在明文前加128位的0。Bob強行解密完混淆表中的密文後,查看解密結果。如果解密出來的某個消息前面有128個0,就知道此條消息解密是正確的

最後一步:分享結果。Bob將混淆表中獲得的正確消息【混淆的字符串】拿出,Alice拿出\(f\)標籤所表示的0\1明文字符串映射關係。Alice和Bob即可共同知道“同或門”電路的執行結果。

⚠️:請注意,混淆電路實際執行過程中,Bob並不會將中間某個門電路的解密結果告訴Alice,僅將整個混淆電路的最終結果告訴Alice。

生成整個邏輯電路

在3.1.1小節畫出數值比對算法的邏輯電路後,Alice列出整個邏輯電路中所有門電路真值表,對所有門電路真值表均按3.1.2小節中的“同或門”處理流程轉換爲混淆電路,Alice可得到整個數值比對算法的混淆電路。

Alice和Bob進行通信

① Alice將完整的混淆表發送給Bob。

② Alice將每個門電路中,需要用到的\(xi\),每個\(xi\)真實輸入(0\1)對應的字符串發送給Bob。(實現Alice真實輸入值的隱藏)。

③ Alice將每個門電路中,需要用到的\(yi\),每個\(yi\)所有可能輸入(0\1)對應的字符串以OT協議形式發送給Bob。(OT協議實現Bob真實輸入值的隱藏)。

Bob解密收到的混淆電路

① Bob利用獲得的\(xi、yi、c1\),層層強行解密每個相關門電路的輸出結果字符串(每個門只能正確解密一個字符串,不給Alice看,實現中間計算結果隱藏)。

② Bob最終可得到整個混淆電路的最終輸出結果\(c_{n+1}\)所表示的字符串\(result\)

Alice和Bob共享混淆電路處理結果

Bob將\(result\)給Alice看,Alice通過自己掌握的字符串對應表,看真實值0\1。如果爲1,表示\(x>y\);否則,表示\(x≤y\)

  • Alice最先知道比較結果

程序實現

參考:https://github.com/wbernoudy/pygarble

代碼說明

功能:利用姚氏電路實現安全兩方計算,Alice創建電路,Bob計算。

對於電路的構造,需要創建三個門電路數組:

  • 第一個:輸入
  • 第二個:中間值
  • 第三個:輸出

電路表示形式:[unique gate ID, type of gate, [input0, input1]]

  • 對於輸入電路,[input0, input1]表示輸入的序號

  • 對於其他電路,[input0, input1]表示其門的ID

Circuit類對象的創建參數:

  • 第一個參數是輸入電路的個數
  • 其他參數是三個門電路
> from alice import *
> on_input_gates = [[0, "AND", [0, 1]], 
>                [1, "XOR", [2, 3]], 
>                [2, "OR", [0,3]]]
> mid_gates = [[3, "XOR", [0, 1]],
>             [4, "OR", [1, 2]]]
> output_gates = [[5, "OR", [3, 4]]]
> mycirc = Circuit(4, on_input_gates, mid_gates, output_gates)
image-20221218171619451

可以通過mycirc.poss_inputs實時查看電路的信息。

  • 每個密鑰都是一對值,表示1或0的混淆後的字符串。
> mycirc.poss_inputs
[{0: b'4diGEaU1jdM3Pu-BJNSP9g7_QPc4ujAzGxl2BGoVG0M=', 1: b'wIXEEaM1S004rNrGEJDCxjtkLItla98ybjKE70ffGRU='}, {0: b'GfwHUAuJvWac23NGyT8aeoJbUWAB-yBcucuQhUt2jwg=', 1: b'BfBToX-S0Jqxb5wA1nhFHga-QILPsYzRMRbmuVHgNa8='}, {0: b'louVKwBH3BVABygcEjSd_oZboJBUUFVSoS67q_YLu9E=', 1: b'W6Lk0qWa7ly0QqxOMIrkrQQP-EXBIkC2NHgUWPn2qY8='}, {0: b'_acYcuVdFNjgmHUhDsKe7rTJbg_o2-MSuBv1gBE_lp4=', 1: b'H27Eo04R8_6S9xAMYFo8sxzn_VmJITg9-joTa1HNTzI='}, {0: b'2ihLVa48z4b6c-xI7D7dWPVRMO-XyewKcVTegn2xDPY=', 1: b'u50GeBJpyRbmFLQnFETQmbTgan5vQLYSTtCEBhHQu98='}]

這時就可以在Alice一端使用這些密鑰計算電路,例如輸入爲:0, 1, 0, 1:

> my_input = [x[y] for x, y in zip(mycirc.poss_inputs, [0, 1, 0, 1])]
> my_input
[b'4diGEaU1jdM3Pu-BJNSP9g7_QPc4ujAzGxl2BGoVG0M=', b'BfBToX-S0Jqxb5wA1nhFHga-QILPsYzRMRbmuVHgNa8=', b'louVKwBH3BVABygcEjSd_oZboJBUUFVSoS67q_YLu9E=', b'H27Eo04R8_6S9xAMYFo8sxzn_VmJITg9-joTa1HNTzI=']
> mycirc.fire(my_input)
{5: b'\x01'}

可以看到,mycirc.fire返回的是一個字典,鍵對應的是輸出門的ID(這裏是5),(輸出)值是編碼後的一字節(這裏爲1)

然後將Circuit對象以字典的形式存儲在json_data文件中。

然後Bob需要先加載json_data文件:

> from bob import *
> mycirc = Circuit(json_data)

這時得到一個Circuit對象,裏面只有計算後的結果,並沒有包含所有情況,Bob只得到了最後計算結果。

> input = [b'4diGEaU1jdM3Pu-BJNSP9g7_QPc4ujAzGxl2BGoVG0M=', b'BfBToX-S0Jqxb5wA1nhFHga-QILPsYzRMRbmuVHgNa8=', b'louVKwBH3BVABygcEjSd_oZboJBUUFVSoS67q_YLu9E=', b'H27Eo04R8_6S9xAMYFo8sxzn_VmJITg9-joTa1HNTzI=']
> mycirc.fire(input)
{5: b'\x01'}
  • OT

因爲Bob並不想讓Alice知道他的輸入信息,所以這裏需要只用OT技術,參考的是t-out-of-n OTNon-Interactive t-out-of-n Oblivious Transfer Based on the RSA Cryptosystem

ot.py提供了兩個類:AliceBob

> from ot import *
> from alice import keypair # we will use this to generate some example keys
> my_keypair = list(keypair().values())
> my_keypair
[b'tWiWGameWDMNOTUDRBM2FUWHkpPg9ZqWPM_e3bsvdqc=', b'5-x4_N0gwM_Hh0AYnSykYn2Ab4sCUw9iUzBVw9ZK8tw=']
> alice = Alice(my_keypair)

Alice對象調用setup,開始OT,通常將結果(公鑰和hash(消息)寫入alice_setup.json文件中:

> alice.setup()
Pubkey and hashes published.

創建一個Bob對象,參數爲:Alice的消息個數,想要哪些消息,消息對應的ID,假設Bob想要獲取Alice的第二個消息:

> from ot import *
> bob = Bob(2, 1, [1])
> bob.setup()
Polynomial published.

默認情況下,Bob.setupalice_setup.json文件中讀取數據,然後寫入信息到 bob_setup.json文件。

> alice.transmit()
G has been published.

待補充

總結

本文講解了如何通過混淆電路解決百萬富翁問題。實際上,計算機所能處理的所有可計算問題都可以轉換爲邏輯電路,這也就意味着,利用混淆電路可以解決所有的安全多方計算問題:即在混淆電路幫助下,凡是能被邏輯電路表示的計算方法,都能在保證參與方數據機密性的前提下得到正確結果。因此本篇文章將混淆電路稱爲解決MPC的萬能鑰匙

參考文獻

[1] https://zhuanlan.zhihu.com/p/138371497

[2] https://zhuanlan.zhihu.com/p/138188677

[3] https://blog.csdn.net/qq_38798147/article/details/110727263

[4] https://blog.csdn.net/Matrix_element/article/details/117481369

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章