王沛然 李加福 雷志偉 張桂剛 張 勇 邢春曉
(1.清華大學行業可信區塊鏈應用技術聯合研究中心,計算機系,信息技術研究院 北京 100084)(2.中國科學院自動化研究所 北京 100190)(3.清華大學北京信息科學與技術國家研究中心,計算機系,信息技術研究院,互聯網產業研究院 北京 100084)
區塊鏈技術從Bitcoin發展到Ethereum之后,支持圖靈完備的編程語言的智能合約的引入[1],使得Ethereum成為可以支持開發和運行DApp的系統。而Ethereum的智能合約被調用并形成交易后,需要網絡上其他節點對其正確性進行驗證[2]。智能合約調用及驗證的過程如圖1所示。

圖1 Ethereum智能合約調用及驗證過程
在圖1中,智能合約調用者即交易發起者,其調用合約時使用的參數全集,必須是明文且公開的,如此,網絡上的其他節點即智能合約驗證者才能根據這個參數全集來驗證該智能合約執行結果是否正確。
顯然,這樣的驗證方式,對于智能合約調用者節點來說,沒有任何隱私保護可言[3]。對此,Ethereum技術團隊也曾坦言,由于同態加密技術遠不能夠支持圖靈完備的智能合約語言,所以只好放棄了智能合約調用過程中的加密功能。
與技術上的障礙相對應的,是一些應用場景對于智能合約加密的迫切需求。事實上,有很多可以運行在Ethereum或類似區塊鏈上的應用場景,都是需要保護隱私的。例如:需要多方參與的競拍競價、競選投票之類的應用[4],各方調用智能合約的輸入都是需要被保密的。再如:醫療健康數據會涉及到個人隱私,因此信息持有者在通過調用智能合約來分享相關信息的時候也會有顧慮。
由于全同態加密技術距離支持圖靈完備的智能合約語言還有很長的路要走,所以業界目前普遍采用一些折衷的辦法,例如Commit-Reveal辦法[5]:通常是將智能合約的輸入參數中加入鹽,從而在保護原參數的前提下,讓其他節點可以驗證合約的調用情況,并在未來通過提供具體的鹽的方式,來證明調用者知曉相關的調用參數。
同態加密是基于數學難題的計算復雜性理論的密碼學技術,其概念可以簡單理解為:對經過同態加密的密文數據進行運算所得到的結果,與對數據原文進行同樣運算后得到的結果再進行同態加密的結果,恰好是一樣的[6]。正如如下公式所示:

其中,f()為數據處理方法,E()為同態加密算法。
同態加密概念來自于代數學,通常分為加法同態和乘法同態。而如果同時滿足加法同態和乘法同態,則稱該同態加密算法滿足全同態。自1978年同態加密概念被提出以來,尋找滿足全同態的加密算法成為了開放性的密碼學難題。直到2009年,IBM的Craig Gentry才突破了這一難題[7~8]。
同態機密技術首先被應用于云計算中,而對于區塊鏈技術,同態加密同樣是很好的支撐技術。在實際的行業應用里,同態加密更多被用作普通交易數據的保護,特別是對交易金額和賬戶余額等信息進行保密。例如:華為區塊鏈提供了同態加密庫,可以將用戶賬戶和賬本數據以密文形式保護,并且交易也采用密文運算[9];趣鏈(Hyperchain)同樣采用同態加密的辦法來保護賬戶及交易的隱私性,同時保留其在網絡中的可驗證性[10]。
值得一提的是,行業中選擇同態加密的區塊鏈,更傾向于使用滿足加法同態的Paillier算法[11]。該算法于1999年被Paillier發明,基于復合剩余類的困難問題,其原理此處不再贅述。由于該算法必然會產生密鑰,所以其在工程領域應用的時候,需要有配套的密鑰管理機制,包括創建、保存、頒發、撤銷等。除了Paillier算法外,滿足乘法同態的El-Gamal算法[12]也常被業界使用。它是除RSA外最具代表性的公開密鑰密碼之一,其安全性建立在離散對數問題的困難性之上,其原理此處亦不再贅述。
值得注意的是,業界尚未出現直接將全同態加密方案直接運用于智能合約加密的相關思路及具體實現。究其原因在于:行業對于智能合約的一個默認的基本要求是具備圖靈完備性,盡管業界也承認無法提供無限存儲能力供圖靈完備的智能合約使用[13]。而圖靈完備意味著,合約語言必須支持跳轉。而跳轉則意味著,合約語言必須能夠對計算的中間結果進行判斷或比較。換言之,同態加密方案要保證計算過程中的單調性,而這是不可能做到的,因為單調性是對破解非常有幫助的特性。
為了解決圖靈完備特性對全同態加密方案的不切實際的要求,我們寄希望于通過將智能合約的執行過程分段,從而在不涉及比較和判斷的地方使用全同態加密技術。
為了對智能合約分段進行同態加密,我們首先要了解智能合約的編譯和執行過程。從Ethereum引入智能合約開始,到Fabric中基于Golang語言編譯器及docker模擬器的強大的智能合約[14],智能合約的編寫語言從定制走向通用,相應的模擬器也從定制走向通用,但智能合約的編譯和執行過程一直都是相似的。如圖2所示。

圖2 智能合約的編譯和執行過程
首先,智能合約的編寫者需要使用圖靈完備的高級語言編寫合約/鏈碼,然后使用相應的編譯器將其編譯為字節碼,并存儲于區塊鏈中[15]。而智能合約調用者則需要提供相應的輸入參數,將區塊鏈上的智能合約運行于模擬器上,并將結果以交易的形式提交到區塊鏈,供其他節點驗證。驗證節點同樣是根據交易中提供的輸入參數,將智能合約再次放到模擬器上運行,以驗證交易結果的正確性[16]。
所以,我們可以將智能合約理解為一個程序的源代碼,是用高級語言編寫的,可讀的文本。而當智能合約被提交到區塊鏈上的時候,它首先需要被編譯,形成類似匯編語言的指令序列,并被存儲到區塊鏈上。此時,該智能合約就類似于通用計算機上的“可執行程序”,可以被區塊鏈網絡中的其他節點調用,并且在調用的時候可以接受不同的輸入參數,從而得到不盡相同的結果。也就是說,當某個節點調用智能合約時,就類似于一臺個人電腦調用了網絡上的一個程序來進行計算,并將計算結果存儲到網絡中。所不同的是,所有被存儲到網絡上的智能合約計算結果,都需要被網絡上的其他節點進行驗證。而驗證的過程,就是各個節點利用該合約被調用時所傳入的參數,在節點本地利用模擬器再次進行計算,來驗證網絡上的結果與自己本地是否一致。
這一過程中,最值得我們注意的是:可被調用的智能合約,是以一種可被模擬器執行的指令流的方式存儲到區塊鏈中的,這樣才好被不同的節點反復執行并驗證。而這個指令流中的指令,一定屬于一個指令集,而且該指令集最好是圖靈完備的[17],才有可能被業界接受。
顯然,我們需要一個相對精簡的,圖靈完備指令集。我們知道,如果巧妙地選取一條指令來構成一個指令集(One Instruction Set Computer,OISC)[18],并給予它無限的資源,是可以構成一個圖靈機的。這樣的OISC的大致可以分為三類:位操縱機、傳輸觸發架構(Transport Triggered Architecture)機器[19]和基于算數運算的圖靈完備機器[20]。
最簡單的圖靈完備的單一指令集的思路是:“SBNZ abcd”指令,其中a、b、c、d都是地址指針。該指令的功能是:如果a中的內容與b中的內容不相等,則將b中的內容減去a中的內容,并將結果存儲到c地址中,然后將控制轉移到地址d;否則按順序執行下一條指令。
顯然,滿足圖靈完備的充要條件是:該指令集支持算數運算和“判斷+跳轉”。算數運算可以由加法和乘法指令組合完成,并且加法與乘法是可以被全同態加密方案支持的?!芭袛?跳轉”的關鍵是判斷,即判斷兩個數字的大小。如果全同態加密方案在對明文和密文運算的時候,可以保證單調性,就可以認為能夠支持判斷。例如:明文的x1小于x2,那么密文的x1也要小于密文的x2。但這對于同態加密技術來說不切實際。試想,如果我們現在得到了E(x)的值,想反求x的值,此時如果同態加密能夠保證單調性的話,那么我們很容易找到x1,x2,使得E(x1) 所以,我們只有讓同態加密繞過全部判斷指令,這也正是本文所要著重說明的“分段加密”的原因。所謂分段,指的不是對指令流字節碼分段,而是在該指令流字節碼被虛擬機執行過程中的指令執行序列進行分段。指令流字節碼是編譯結果,而指令執行序列是運行時才能確定的序列。圖3可以說明指令流字節碼和指令執行序列之間的區別。 如圖3所示,指令流字節碼,類似于一個可執行程序;而指令執行序列,則類似于程序運行時,調度到CPU上運行的指令的順序列表。一個智能合約,可以一一映射到一個指令流字節碼,且后者一定要先被存儲到區塊鏈上,才能被調用。而智能合約的每次調用,都可以一一映射到一個指令執行序列。需要指出的是,節點在對智能合約調用進行驗證的時候,其所對應的指令執行序列,必然與原調用者調用智能合約時所對應的指令執行序列完全一致。所以我們可以說,同一個智能合約,在輸入參數相同的情況下,其執行過程中的指令執行序列也一定相同,所以合約執行結果也一定相同。 圖3 指令流字節碼與指令執行序列 顯然,由于智能合約調用者掌握著輸入參數的明文,其合約執行過程中也是進行明文計算,所以合約調用者可以在現有的虛擬機上運行現有的指令流字節碼,而且可以在執行的過程中獲得每次遇到判斷指令時的判斷結果。 而智能合約驗證者由于只掌握了輸入參數的密文,其合約執行過程中也只能進行密文計算,而同態加密是不能保證單調性的,所以合約執行者在現有虛擬機上運行現有指令流字節碼的時候,勢必會在判斷指令上于明文運算存在一定概率的不一致,而這個概率可能會逼近0.5,即密文的大小關系是隨機的,與明文大小關系無關。 既然我們現在缺少且僅缺少針對判斷的同態加密方案,那么我們可以考慮通過圖3中的判斷結果序列來繞開判斷指令的同態加密:這就是本文的核心思路:利用判斷結果序列來輔助同態加密方案,構成針對圖靈完備的智能合約的同態加密方案。 為了解決合約驗證者在執行判斷指令的時候存在不一致的問題,我們可以要求智能合約調用者在用明文參數執行完合約的同時,將這一過程中遇到的所有判斷指令的結果(即一個Y/N序列,或二進制串)保存下來,并以判斷序列的方式作為輔助參數,一并發布到區塊鏈上。與此同時,智能合約驗證者在得到輸入參數密文,以及判斷序列后,同樣可以在虛擬機上再次執行該合約,并在遇到判斷指令的時候,不對其進行判斷,而是直接使用判斷序列里的值,從而避開同態加密所不支持的判斷。流程參考圖4。 這一方案的實現,需要考慮智能合約的調用者和驗證者在執行智能合約時所使用的不同的參數即不同的執行過程,即我們需要對模擬器進行一次升級,才能驗證這一方案。 首先,智能合約調用者節點,在其本地使用明文參數執行智能合約時,模擬器需要知道這是明文參數,可以按照智能合約指令流字節碼來執行,并且每次遇到判斷指令的時候,記錄下判斷的結果(Y/N或1/0)。當該合約執行完成后,模擬器不僅需要記錄下合約執行結果,同時還要將執行過程中所生成的判斷結果序列保留下來,因為這將是其他節點對該次合約執行結果進行驗證的必須的參數。 圖4 分段加密的執行過程 在合約執行完畢后,支持同態加密的區塊鏈節點需要對輸入參數和執行結果進行同態加密,并且連同判斷結果序列的明文,一并提交,存儲到區塊鏈上。 與此同時,其他節點需要對合約執行結果進行驗證,而它們所得到的參數將是密文形式,以及一串明文的判斷結果序列。此時,不僅需要模擬器進行密文計算,并且在遇到判斷指令的時候,不去執行指令,而是順序使用判斷結果序列中的值。如此,模擬器的整個執行過程,等價于一連串的針對密文的算數運算。由于判斷結果序列是唯一的,所以這個一連串的算數運算,每條指令都與明文計算時的算數運算指令保持一致,其結果作為密文計算的結果,應該與明文計算的結果加密后是一致的。 綜上,我們需要對模擬器進行升級,使其既可以生成判斷結果序列,也可以根據判斷結果序列對智能合約進行密文計算。 Ethereum是第一個承載智能合約的區塊鏈,其模擬器(EVM)是最經典也最簡單的能夠運行智能合約的虛擬機。EVM是一個準圖靈機,可以運行包含復雜的隨機的算法的智能合約字節碼,其運行原理入圖5。 我們構造一個非常簡單的智能合約,并通過如圖6的合約編譯界面來生成字節碼,從而在EVM上運行該智能合約。 從圖6中可以看到,簡單的智能合約中所出現的判斷語句“if(b==1)”,在編譯結果中有“ISZERO”指令與之相對應,而這樣的判斷指令正是智能合約分段加密工作的研究對象。 圖5 EVM結構 圖6 Remix-Ethereum IDE 最終在EVM上執行的智能合約字節碼,其執行邏輯便是將圖6右側的類匯編指令逐條地利用圖5中的EVM的內存和堆棧來執行,并將結果寫入圖5中的狀態數據庫。 基于上述實驗,我們可以更加明確以Ethereum智能合約為代表的區塊鏈智能合約的執行原理和細節。而我們接下來的工作,則集中在針對模擬器的重構,使之能夠分別以明文和密文的模式來運行智能合約,其部署原理及流程如圖7所示。 圖7 EVM的執行模式與驗證模式 智能合約所涉及到的算數運算通常具有一定的規模,例如Ethereum上標準發幣合約會被官方編譯器編譯為約1500條類匯編指令。而目前不能確定同態加密方案對于復雜運算的支持程度,所以這方面需要進一步的調研和試驗。 由于判斷序列是由合約調用者通過明文運算后給出的,所以存在該調用者惡意造假的可能性。因為參數明文是該調用者唯一持有的,所以其他節點無法再次參數明文來執行智能合約,也就無法直接驗證該判斷序列的正確性。 由于智能合約本身是公開的,而判斷序列同樣是公開的,所以不能排除有人可能會根據判斷序列來反推出調用者明文參數的一些特征。不過判斷序列所帶有的隱私信息是非常有限的,并且可以在合約頭部代碼中加入一些干擾代碼來增加這種“破譯”難度。 本文通過對智能合約執行過程的介紹、對全同態加密領域的調研以及對“圖靈完備”指令集的分析,論證了難以尋找到適用于圖靈完備的智能合約的全同態加密方案的原因,進而提出了利用判斷結果序列的方法來講智能合約簡化為算數運算,從而使用全同態加密方案進行加密。接下來的驗證工作,需要升級模擬器,為全同態加密在智能合約中的應用提供基礎設施。

4 分段加密實踐



5 尚需解決的問題
5.1 算術運算的規模
5.2 判斷序列的真實性
5.3 判斷序列的隱私性
6 結語