本文作者:郭宇
zkPoD 原理解讀系列(一):本文面向有一定密碼學基礎,或者對密碼學感興趣的讀者。文中雖有大量數學公式出現,但都比較簡單不難理解。
導言:zkPoD 是什麼?
zkPoD 實現了去中心化的「零知識有條件支付」,支援上 GB 資料的零信任公平交易。關於「零知識有條件支付」的概念請看這篇概述文章 『zkPoD:區塊鏈,零知識證明與形式化驗證,實現無中介、零信任的公平交易』。 zkPoD 是一個全新的實現 ZKCP 目標的方案。目前 zkPoD 已經支援上 GB 的資料,支援低 TPS 公鏈,也支援高 TPS 聯盟鏈;既支援二進位制資料,也支援帶有內部豐富型別與結構的表格資料。與傳統的「受信第三方」相比,zkPoD利用區塊鏈來作為一個「Trustless 第三方」,實現「零信任公平交易」。
zkPoD 也是一個實現資料與價值雙向流通的底層基礎協議
zkPoD 開原始碼與更多文件請見:github.com/sec-bit/zkP…
Proof-of Delivery (PoD) 協議
PoD 是實現 zkPoD系統的核心協議。PoD 協議實現了借用區塊鏈智慧合約來進行「資料」和 「Token」的原子交換,並且保證買賣雙方的公平性。PoD 並沒有像 ZKCP[1] 那樣採用單一的 zkSNARK 方案來實現原子交換,而是利用了 Pedersen Commitment,Schnorr Protocol 等密碼學經典方案。這樣 PoD 可以做得更高效,同時易擴充套件。PoD 協議還將利用形式化的證明來構建堅實的「信任根基」。
本文介紹一個極簡的 PoD 協議——PoD-Tiny,這個協議簡化了很多細節,並不實用,但是可以幫助讀者快速理解 PoD 的原理和麵臨的挑戰。
假設我是賣家,而你需要從我手裡買一個資料檔案,這個協議的一個大致流程是:
- 步驟一:我把「資料」加密後,傳給你
- 步驟二:你把「Token」交給區塊鏈「智慧合約」
- 步驟三:我用「金鑰」交換「智慧合約」手裡的「Token」,然後你緊接著可以從智慧合約中讀取「金鑰」進行「資料」解密
是不是很簡單?聰明的你此刻正在高度懷疑這個過程是不是哪有問題。
「公平交易」中的關鍵問題
- 關鍵問題(1):你收到的加密資料確實是你想要的資料
- 關鍵問題(2):你收到加密資料之後,不付錢就跑路怎麼辦
- 關鍵問題(3):而我出示給智慧合約的金鑰必須是真金鑰,否則拿不到 Token
- 關鍵問題(4):我出示真金鑰之後,必須要能拿到 Token
我們接下來就抽絲剝繭,討論下 PoD-Tiny 是怎麼巧妙解決這些問題的。
鎖定資料的特徵:Authenticator
對於關鍵問題(1),我們需要一個錨點,什麼是你想要的資料。這裡簡單起見,假設我們事先約定了一個資料檔案的唯一標籤,或者特徵。然後你購買的資料需要能和這個標籤一一對應。
一般來說,大家喜歡用 Hash 來標記對一個字串的特徵,比如計算
h = MD5("hello,zkPoD!")
複製程式碼
我們看下這個字串 "hello,zkPoD!"
總共有 12 個位元組大小,也就是 96 個 bit。於是我們可以將這 12 個位元組轉換成一個有限域上的整數(這裡我們假設有限域的大小接近 256 bit )。這樣我們可以把這個字串編碼成一個整數,我們姑且用一個符號表示這個整數, 假設是 m
。
我們通過下面的運算產生這個資料的「承諾」。
A = m*G
承諾也叫 Commitment,它可以做到和資料的一一對應,同時並且能夠隱藏資料的值。這裡的 A
在 zkPoD 系統中被稱為「認證資訊」 Authenticator。而這裡的G
是一個橢圓曲線迴圈群的生成元。
「認證資訊」Authenticator 可以向所有人公開,我們不用擔心會洩露原始資料資訊。這是因為,通過 A
難以反算出來 m
。這個逆運算是一個有限域的「求對數」運算。假如有限域比較大的話,這個對數運算是非常非常困難的,這就是常說的「離散對數難題」假設。拋開這些理論細節,我們只要知道,Authenticator 可以放心交給任何人,而不用擔心 m
被逆向破解。
「認證資訊」為什麼要採用這種「承諾」形式,而不是採用大家所熟知的 Hash 運算。這是因為「承諾」具有加法運算同態性。所謂同態性質,大家可以這麼理解:明文資料具有的某種運算,可以同態對映到密文的運算中。假設有三個資料明文,m1
,m2
還有 m3
,其中 m1 = m2 + m3
。
他們的 Authenticator 分別計算如下:
A1 = m1*G
, A2 = m2*G
, A3 = m3*G
,
我們可以計算 A1 ?= A2 + A3
來驗證 m1 ?= m2 + m3
。大家可以發現,雖然一個吃瓜群眾知道了 A1
,他也不能反算出m1
。但是他仍然知道 m1
等於另外兩個數的和,雖然他完全不知道這三個數具體的值是多少。
注:這裡的加法是模加,a + b
是 a + b mod p
的簡寫。為了易讀性,後續的加減乘除一律約定是有限域上的運算。
剩下的事情就簡單了,在 PoD協議中,我們可以任選一個隨機數 k
作為一次性金鑰,來加密m
,計算 E(m) = k + m
。 E(m)
就是加密資料。我可以把 K = k*G
也發給你,這樣你手裡有三樣東西,A
,K
,還有 E(m)
。你就可以用下面的公式來「同態地」校驗加了密的 E(m)
確實是資料 m
的密文:
E(m)*G ?= K + A
並且,通過上面的公式,你還能知道一個關鍵資訊:金鑰是一個關聯到 K
的數值。儘管這時候你完全不知道 m
和 金鑰 k
。這個資訊也是解決**關鍵問題(3)**的關鍵所在。
總結一下:
通過同態性,買家可以在資料加密的情況驗證資料是否滿足一些條件
回憶一下關鍵問題(2):
- 關鍵問題(2):你收到加密資料之後,不付錢就跑路怎麼辦
解決這個問題是核心方法是「零知識證明」。如果買家拿到加密資料之後,從中分析不出來任何多餘的資訊,那麼就不會損害賣家利益,也就能解決關鍵問題(2)。簡單講,如果加密資料是零知識的,就不怕買家拿了加密資料不給錢就跑路。所謂的「零知識」,大家可以這麼通俗理解:買家拿到的加密資料後,就像拿到一堆隨機數一樣,沒有任何資訊量。怎麼做到零知識呢?PoD-Tiny 採用了經典 Schnorr 協議的思想。
插播科普:Schnorr 協議 與 「零知識」
Schnorr 協議是非常經典的教課書例子,我這裡快速帶大家過一遍。Schnorr 協議的用途之一是用來做身份認證,它是一個兩方安全協議,一方「證明者」Alice ,向另一方「驗證者」Bob證明她擁有一個公鑰對應的私鑰。
首先 Alice 產生一對「公私鑰」,(PK, sk)
。然後 Bob 持有 Alice 的公鑰 PK
。當 Alice 要向 Bob 證明身份時,他們會通過一個「三步互動協議」來完成證明:證明 Alice 擁有私鑰 sk
。如果 Bob 接受了這個證明,那麼 Bob 會認為 對面證明有私鑰的人就是 Alice。下面簡單描述下這個協議:
sk = a, pk = a*G
公開輸入:PK = a*G
Alice私有輸入:sk = a
第一步:Alice 選擇一個隨機數 r
,並且傳送 r
的「承諾」R = r*G
給 Bob
第二步:Bob 發回一個隨機數 c
,作為挑戰數
第三步:Alice 計算 z = r + c * a
,然後將 z
傳送給 Bob,然後 Bob 通過如下的公式來驗證:
驗證公式:z*G ?= R + c*PK = r*G + c*a*G
這個 Schnorr 協議具有三個性質:
- 完備性 Completeness
- 可靠性 Special Soundness
- 對誠實驗證者零知 Special Honest Verifier Zero-Knowledge
其中第三個性質就是「零知識」,這個性質保證了:在協議互動過程中,Bob 無論如何都不會得到關於私鑰的任何資訊。
注:嚴格地講, Schnorr 協議並不是「Full-ZK」,只能保證「HVZK」,這是一個相對弱一些的零知識性質。不過大家暫時不用糾結這一點,Schnorr 協議可以通過一些技巧升級為「Full-ZK」。
PoD-Tiny:一個簡單的 PoD 協議
如果大家已經大概記住了 Schnorr 協議的細節,那麼我來展示一個協議叫做 PoD-Tiny。
協議描述:假設 Alice 擁有一個資料明文 m
,然後 Bob 擁有這個資料的 Authenticator(A
),這裡還有一個 「Trustless 第三方」,我們暫且叫她 Julia。請大家記住:她是一個智慧合約。
協議:
開場前的道具:m
,a
,G
, 一個隨機數產生器 rand()
角色:
- Alice:擁有資料
m
,一次性金鑰k <-rand()
- Bob:擁有
A
- Julia: N/A
步驟:
第一步:Alice 產生一個隨機數,r <-rand()
,然後發給 Bob 兩個數 K=k*G
和 R=r*G
第二步:Bob 產生一個隨機數 c <-rand()
,傳送給 Alice
第三步:Alice 計算兩個數字 E(m) = k + m
,z = r + c * k
,並且傳送給 Bob。這兩個數,第一個 E(m)
是用一次性金鑰 k
加密後的「資料密文」,而 z
是「金鑰的密文」。
注:什麼?金鑰的密文?沒錯,這裡 Alice 用第一步生成的隨機數 r
,加上第二步 Bob 提供的挑戰數 c
對 k
做了加密,得到了 z
。
第四步:Bob 對收到的資料密文 E(m)
進行驗證(公式(1) ),並且對金鑰的密文進行驗證(公式(2) ):
- 驗證公式(1):
E(m) * G ?= A + K
- 驗證公式(2):
z*G ?= R + c*K
注:在第四步中,Bob 需要搞明白兩件事:首先傳給他的密文資料(E(m)
)能不能對應到資料錨點(A
);然後密文資料(E(m)
)是不是由某一個未知金鑰 X
加密的,並且這個「未知金鑰」的密文應該等於第三步中 Alice 發過來的「金鑰密文」(z
)。倘若如此,在未來的某個時刻,若 Bob 得到「金鑰密文的金鑰」(r
) 之後,就可以做兩次解密動作來成功拿到資料明文(m
)。兩次解密動作為:首先 Bob 用「金鑰密文的金鑰」r
還有挑戰數 c
解密 金鑰密文 z
,得到資料金鑰 k
,然後再用資料金鑰來解密 資料密文 E(m)
,從而得到資料明文 m
。
再注:上面的協議第一步到第四步,其實大家可以發現和 Schnorr 協議非常類似。只不過把 a
替換成了一個一次性金鑰 k
。然後另一個不同點是,K = k*G
相當於原 Schnorr 協議中的公鑰,並不是一開始發給了Bob,而是在協議的第一步和 R
一起傳送給 Bob。不管如何,從整體上,這四步協議正是一個 Schnorr 協議的擴充套件。當然到這裡還沒完,接下來區塊鏈要登場了,Bob,Alice 要和 Julia 開始進行互動。
第五步:如果 Bob 在第四步中驗證公式(1)和公式(2)通過,那麼說明 Alice 發的加密資料都是正確的。這時候 Bob 要發給 Julia 一個「資料交付收據」(Delivery-Receipt),R
。Bob 在這一步需要將 「Token」一併儲存給 Julia。
注:這個收據是為了告訴 Julia,Bob 他已經收到了加過密的資料了,但是金鑰還沒收到。金鑰需要 Julia 幫他接收並驗證。那麼驗證的憑據是什麼呢?正是「金鑰密文的金鑰」對應的「承諾」,是不是有點繞,這個收據就是協議第一步 Alice 發給 Bob 的 R
。
第六步:Alice 向 Julia 出示「金鑰密文的金鑰」,也就是 r
。Julia 檢查下面這個關鍵公式。如果驗證通過,Julia 可以將 Bob 儲存的 Token 轉給 Alice。
- 驗證公式(3):
R ?= r*G
我們看看關鍵問題(3)是如何解決的。
- 關鍵問題(3):Alice 出示給智慧合約的金鑰必須是真金鑰,否則拿不到 Token
在協議的第一步,Alice 給 Bob 傳送了一個「金鑰的金鑰」的「承諾」R
;然後在協議的第五步,Bob 把 R
轉交給了 Julia;第六步,Alice 兌現承諾,揭示對應的 r
。如果 Alice 出示一個錯誤的值,Julia 立即就會發現公示(3)不成立。
還有一個:
- 關鍵問題(4):Alice 出示真金鑰之後,必須要能拿到 Token
在協議的第六步,Julia 要檢驗公式(3)。在 Alice 出示正確的 r
的情況下,如果等式不成立,那麼只有兩種情況:(1)Julia 故意搗亂, (2)R
的值不正確。對於前一種情況,需要保證 Julia 的合約程式碼確實沒有漏洞,功能正常,這個需要額外採用「形式化驗證」的方法來解決。對於後一種情況,這裡需要 Alice 在第六步先檢查一下 Julia 的內部狀態,在第五步中 Bob 提交的 R
是不是一個正確的值。這裡請注意:Julia 是一個公開的智慧合約,她持有的任何資料都是公開可見的,她的任何內部狀態與計算過程都是公開可見的。
協議的安全性與公平性分析
如果我們不考慮多次交易,PoD-Tiny 是一個「公平」的交易協議。我們接下來依次分析下為何這個協議是公平的。
我們首先考慮 Alice 有哪些作弊手段:
- A1. 將假的資料
m'
加密後傳給 Bob - A2. 加密資料時用的金鑰
k
,但是在加密金鑰的時候卻用的是另一個k'
,並且k <> k'
- A3. 向 Julia 出示一個假的密文金鑰
r'
分析:如果 Alice 採用作弊手法 A1,那麼 Bob 在校驗公式(1)時能夠發現;如果 Alice 採用作弊手法 A2,那麼 Bob可以通過計算公式(2)發現;如果 Alice 採用作弊手法 A3,那麼 Julia 通過公式(3)能發現。
我們再考慮 Bob 都有哪些作弊手段:
- B1. Bob 在拿到加密資料
E(m)
之後,就退出協議,然後嘗試破解密文 - B2. Bob 在驗證加密資料之後,向 Julia 出示一個錯誤的「交付收據」
- B3. Bob 賬戶沒有足夠的 Token
分析:如果 Bob 採用作弊手段 B1,那麼 Bob 是無法從加密資料中得到任何資訊的,因為協議的前三步是「零知識的」(準確地說:Honest Verifier Zero-Knowledge)。如果 Bob 採用手段 B2,Alice 可以在第六步檢查下 Julia 手裡的「資料交付收據」R
是不是和她在第一步發給 Bob 的相同,一旦 Bob 提交錯誤的收據,Alice 可以直接退出協議,拒絕出示金鑰。同樣,如果 Bob 採用手段 B3,Alice 可以在第六步的時候檢查 Bob儲存在 Julia 處的 Token 是否足夠,如果不足則直接退出協議。
最後,Julia 有沒有可能作弊呢?Julia 是智慧合約,她的任何行為和內部狀態都能被任何人讀取,那麼通過 Julia 是有可能產生資訊洩露的,從而對 Alice 或者 Bob 不利。但是請大家注意下,Julia 其實並不接觸任何和資料明文 m
相關的資訊,也就從鏈上不會洩露 m
的資訊。Julia 接觸到的資訊只有兩個,R
和 r
。
壓縮到最簡協議
我們數一數上面的協議的互動步驟總共有五步,分別是 Alice 與 Bob互動三次,Bob 與 Julia 互動一次,Alice 與 Julia 互動一次。安全協議裡面有一個叫做 Fiat-Shamir Heuristic 變換的工具,它可以將 PoD-Tiny 協議中的前三次互動,直接「壓縮」成為一次互動。
壓縮前:
Step | Interaction | Info |
---|---|---|
第一步 | Alice => Bob | (K, R) |
第二步 | Bob => Alice | c |
第三步 | Alice => Bob | (E(m), z) |
第四步 | Bob => Julia | R |
第五步 | Alice => Julia | r |
壓縮後:
Step | Interaction | Info |
---|---|---|
第一步 | Alice => Bob | (K, R, E(m), z) ,這裡 c = Hash(A || K || R) |
第二步 | Bob => Julia | R |
第三步 | Alice => Julia | r |
我們發現最主要的不同點是,在壓縮後的 PoD-Tiny 中挑戰數不再由 Bob 產生,而是由 Alice 產生。這裡大家可能會產生疑問,這樣做會不會對 Bob 不公平?這相當於三步的 Schnorr 協議直接壓縮成一步就完成了。這裡先下個結論:壓縮後的協議保持零知識的性質,仍然對雙方公平。原因是,壓縮前的協議可以證明 HVZK(Honest Verifier Zero-Knowledge);壓縮後的協議可以證明出 NIZK (Non-Interactive Zero-Knowledge)。但是安全性在壓縮前後的對比會比較 Subtle,這裡不再展開。
經過壓縮,最後這個協議變得不可思議地簡潔:
邁向實用性的挑戰:安全與效能
最簡協議 PoD-Tiny 只是萬里長征的第一步,當面對紛繁冗雜的現實世界,要將理論變成程式碼時,會面臨許許多多的問題。這些問題會相互糾纏在一起,反過來又會影響著協議在理論層面的設計。
- 如何支援長度超過 1MB 的資料,甚至上 GB
- 如何有效降低鏈上驗證計算的開銷
- 如何支援以太坊,並免疫以太坊上的各式安全問題
- 如何支援資料的複雜同態計算
在安全和效能方面,zkPoD 做了不少的工程改進和創新,感興趣的朋友請關注我們的後續文章。
寫在最後
區塊鏈到底能做什麼?我在最近一年裡看到了許多相當「悲觀」的論調,我想 PoD 協議應該會給這些懷疑論者帶來些許啟發。區塊鏈在 PoD 協議中起到了一個「Trustless 第三方」的關鍵角色,並且讓這個協議變得不可思議的簡潔,這是我們始料未及。
參考文獻
- [1] Maxwell, G. "Zero knowledge contingent payment. 2011." URl: en.bitcoin.it/wiki/Zero_K… (visited on 05/01/2016) (2016).
- [2] Greg Maxwell. “The first successful Zero-Knowledge Contingent Payment.” bitcoincore.org/en/2016/02/…