來源:https://www.8btc.com/author/3708 灑脫喜 2019-02-13 17:41釋出在 鏈頭條 技術指南 92796
前言:在前一篇文章《區塊鏈替代簽名方案優劣勢對比》中,譯者簡單介紹了幾種替代簽名方案,其中Schnorr簽名方案和BLS簽名方案最受關注,前者也是目前最大可能應用於比特幣的技術方案,因此我們有必要更深入地瞭解它們。
Schnorr簽名演算法最初是由德國密碼學家Claus Schnorr於2008年提出的,而來自區塊鏈協議公司Blockstream的密碼學家Gregory Maxwell、Pieter Wuille等人,則在2018年提出了一種名為MuSig的Schnorr簽名方案,這也是我們即將探索的簽名方案。而BLS簽名方案,最初是由斯坦福大學教授Dan Boneh等人於2001年便提出的一種方案,目前則在Boneh教授等人的完善下,變得更適用於區塊鏈。
總的來說,兩大簽名方案各有千秋,它們在不同的場景下都有各自的優勢。
以下內容譯自量子物理學家Stepan的兩篇科普文章(part1和part2),總結部分略有改動:
目錄:
1、橢圓曲線數字簽名演算法(ECDSA)
2、什麼是Schnorr簽名?
3、Schnorr簽名的批量驗證
4、Schnorr簽名的金鑰聚合
5、MuSig:由Blockstream提出的Schnorr簽名方案
6、默克爾多重簽名(Merkle Multisig)
7、什麼是BLS簽名方案?
8、BLS簽名的魔法
9、BLS簽名方案的具體原理
10、BLS簽名的簽名聚合
11、BLS簽名的金鑰聚合和n-of-n多重簽名
12、子群多重簽名方案(m-of-n multisig)
13、BLS簽名可能的應用場景
14、BLS簽名的弊端:配對效率低下
15、結論
(圖1:來自pxhere.com)
Blockstream曾在2018年初的時候釋出了一份關於MuSig的論文,這是一種Schnorr簽名方案,總的來說,這種簽名方案的一些特徵是非常好的,但其也存在著一些讓人討厭的其他特徵。
首先,我們需要明白,比特幣目前使用的是ECDSA橢圓曲線數字簽名演算法,要對訊息m進行簽名,我們需對其進行雜湊操作,並將此雜湊視為一個數字: z = hash(m)
。我們還需要一個隨機或隨機查詢的數字k。我們不喜歡信任隨機數生成器(存在太多的故障,很多漏洞與糟糕的RGN有關),因此,我們通常會使用 RFC6979,並根據我們的祕密和我們要簽名的訊息,計算確定性K值。
使用私鑰pk,我們可以為包含兩個數字的訊息m生成簽名:r(隨機點R的x座標 = k×G) 和s = (z+r⋅pk)/k。然後,使用我們的公鑰 P = pk×G
,任何人都可通過檢查點 (z/s)×G+(r/s)×P
的x座標等於r,來驗證我們的簽名。
(圖2: ECDSA演算法的可檢視)
這個演算法很常見,也很棒,但它也是可改進的。首先,簽名驗證包括反轉(1/s)和兩點乘法,這些操作的計算量非常大。在比特幣中,每個節點都必須驗證所有交易。這意味著當你廣播一筆交易時,數千臺計算機將不得不驗證你的簽名。而簡化驗證過程將是非常有益的,即使簽名過程會更加困難。
第二,每個節點必須分別驗證每個簽名。如果是m-of-n多重簽名交易節點,則可能需多次驗證相同的簽名。例如,具有7-of-11多重簽名輸入的交易將包含7個簽名,並且需要對網路中的每個節點進行7-11次的簽名驗證。同樣,這樣的交易會佔用大量的空間,你必須為此支付大量的費用。
Schnorr簽名的生成則略有不同,我們使用了一個點R和一個標量s來代替兩個標量(r,s)。與ECDSA相似的是,R是橢圓曲線(R=K×G)上的一個隨機點。簽名的第二部分計算略有不同: s = k + hash(P,R,m) ⋅ pk
.這裡的pk是你的私鑰,而 P = pk×G
則是你的公鑰, m
是訊息。然後可通過檢查 s×G = R + hash(P,R,m)×P
來驗證這個簽名。
(圖3: Schnorr簽名演算法的可檢視)
這個方程是線性的,所以方程可互相加減,並且仍然有效,這就給我們帶來了幾大關於Schnorr簽名的好處。
要驗證比特幣區塊鏈中的區塊,我們需確保區塊中的所有簽名都有效。如果其中一個是無效的,我們不會在乎是哪個無效的,我們只會拒絕掉整個區塊。
對於ECDSA簽名演算法,每個簽名都必須單獨驗證。也就是說,如果區塊中有1000個簽名,我們就需要計算1000次倒置和2000次點乘運算,總共約3000次繁重的計算任務。
而通過使用Schnorr簽名,我們可以將所有簽名驗證方程相加,從而節省一些計算能力。對於有1000個簽名的區塊而言,我們需驗證:
(s1+s2+…+s1000)×G=(R1+…+R1000)+(hash(P1,R1,m1)×P1+ hash(P2,R2,m2)×P2+…+hash(P1000,R1000,m1000)×P1000) 這裡我們有一堆加法(在計算能力上幾乎是免費的)以及1001次點乘法。我們需要為每個簽名計算大約一次繁重的計算。
(圖4:兩個簽名的批驗證圖,由於驗證方程是線性的,只要所有簽名都有效,幾個方程的和就有效。我們節省了一些計算能力,因為標量和點加法比點乘法容易得多)
我們希望讓自己的比特幣保持安全,所以我們可能希望使用至少2個不同的私鑰來控制我們的比特幣。比如說一個放在膝上型電腦或手機,另一個則放在硬體錢包/冷錢包。因此,當其中一個受到威脅時,我們仍然可以控制我們的比特幣。
目前,它是通過2-of-2多重簽名指令碼來實現的,這需要在交易中包含兩個單獨的簽名。
而使用schnorr簽名,我們可以使用一對私鑰 (pk1,pk2),並生成一個與共享公鑰 P=P1+P2=pk1×G+pk2×G
對應的共享簽名。要生成這個簽名,我們需要在每個裝置上選擇一個隨機數(k1,k2),生成一個隨機點 Ri=ki×G
,將它們相加以計算一個公共雜湊 (P,R1+R2,m)
,然後從每個裝置 (si = ki + hash(P,R,m) ⋅ pki)
中獲取s1和s2。然後我們可以將這些簽名相加,並使用一對 (R, s) = (R1+R2, s1+s2)
作為我們對共享公鑰p的簽名。其他所有人都無法說出它是否是聚合簽名,它看起來與普通schnorr簽名完全相同。
這種構造有3個問題,第一,從使用者介面的角度來看,要進行交易,我們需要進行幾輪通訊,計算公共R,然後-簽名。有了兩個私鑰,只需一次訪問冷錢包就可以完成:我們在線上錢包上準備一個未簽名的交易,選擇k1,將 R1=K1×G
與未簽名的交易一起記下。然後我們將這些資料傳送到冷錢包並簽名。因為我們已經有了R1,所以我們可以一次性在冷錢包上籤署交易。從冷錢包中,我們得到了R2和s2,並將其傳輸回線上錢包。線上錢包使用之前選擇的 (k1, R1)
簽署交易,結合簽名並廣播簽名交易。這與我們現在的情況非常相似,但一旦新增第三個私鑰,一切就會變得更加複雜。比方說,你有一筆財富,它是由10個私鑰控制的,它們儲存在世界各地不同的安全位置,然後,你需要進行一筆交易。目前,你只需要瀏覽所有這些位置一次,但如果你使用的是金鑰聚合,則需要執行兩次,以組裝所有RI,然後簽名。在這種情況下,最好在不進行聚合的情況下保留單獨的簽名,然後我們就需要3輪通訊:
- 選擇一個隨機數
ki
和相應的Ri=ki×G
,然後只告訴每個人其雜湊ti=hash(Ri)
,這樣每個人都可以確定在學習了其他隨機數之後你不會改變主意; - 把所有的數字彙總起來,計算出共同的R;
- 簽名;
第二個問題是已知的惡意金鑰攻擊。無論是在論文還是這篇文章中,都有很好的描述,所以我不想詳細討論。其想法是,如果你的某個裝置被黑客攻擊(比如說,你的線上錢包),並假裝其公鑰是 (p1-p2)
,那麼它可以用它的私鑰 pk1
控制共享資金。一個簡單的解決方案是,在設定裝置時,需要使用相應的私鑰對公鑰進行簽名。
還有第三個重要問題。不能使用確定性k進行簽名。有一種簡單的攻擊方式,如果你使用確定性K,它允許黑客獲得我們的私鑰。攻擊看起來會是這樣的:有人入侵了我們的膝上型電腦,並完全控制了兩個私鑰中的一個(例如pk1)。我們可能覺得是安全的,因為我們的比特幣需要來自 pk1
和 pk2
的聚合簽名。因此,我們嘗試像往常一樣進行交易,準備一個未簽名的交易和 R1
值,將它們轉移到我們的硬體錢包並在那裡簽名。然後返回 (r2,s2)
和……我們的線上錢包發生了一些事情,它無法簽名和廣播。我們再次嘗試,但我們被黑的電腦這次使用了另一個隨機值 R1'
。我們再次與我們的硬體錢包簽署相同的交易,並將值 (r2,s2)
帶回我們被黑的電腦。然後,糟糕的事就發生了,我們的比特幣就丟失了。
在這個攻擊中,黑客為同一筆交易獲得一對有效的簽名: (R1, s1, R2, s2)
和 (R1', s1', R2, s2')
,這裡R2是相同的,但 R = R1+R2
和 R'=R1'+R2
是不同的,這意味著黑客可以計算我們的第二個私鑰: s2-s2'=(hash(P,R1+R2,m)-hash(P,R1'+R2,m))⋅pk2 and pk2=(s2-s2')/(hash(P,R1+R2,m)-hash(P,R1'+R2,m))
。我發現這是金鑰聚合最不方便的特性:我們需要在任何地方使用好的隨機數生成器來使用金鑰聚合。
MuSig方案解決了其中一個問題,它使得金鑰盜竊攻擊成為了不可能 。目標是將多個參與方/裝置的簽名和公鑰聚合到一個參與方/裝置,但不能證明你擁有與公鑰對應的私鑰。
聚合簽名對應於聚合公鑰。但是,我們不只是將所有聯合簽名者的公鑰相加,而是將它們乘以某個因子。聚合的公鑰將是 P=hash(L,P1)×P1+…+hash(L,Pn)×Pn
。這裡 L=hash(P1,…,Pn)
是一個取決於所有公鑰的公用數字。這種非線性特性可以防止攻擊者構造一個不好的公鑰,例如在惡意金鑰攻擊當中。儘管攻擊者知道自己的雜湊 (L,Patk)×Patk
應是什麼,但他不能從中派生 Patk
,這和從公鑰派生私鑰是相同的問題。
剩餘部分和前面的情況非常相似,為了生成簽名,每個聯合簽名者選擇一個隨機數 ki
,並與其他人共享 Ri=ki×G
。然後將所有這些隨機點相加到一個 R=R1+…+Rn
,生成簽名 si = ki + hash(P,R,m) ⋅ hash(L,Pi) ⋅ pki
。這個聚合簽名是 (R, s)=(R1+…+Rn, s1+…+sn)
,驗證方程與之前相同: s×G = R + hash(P,R,m)×P
。
你可能會注意到,MuSig和金鑰聚合都需要所有簽名者簽署一筆交易。但是,如果你要的是一個2-of-3的多重簽名呢?在這種情況下,是否可以使用簽名聚合,或者我們必須使用我們通常使用的 OP_CHECKMULTISIG
和單獨的簽名?
嗯,這是可能的,但是協議有一個小的改變。我們可以開發一個類似 OP_CHECKMULTISIG
的新op碼,它檢查聚合簽名是否與公鑰的默克爾樹中的特定項對應。
例如,如果我們使用帶有公鑰p1、p2和p3的2-of-3多重簽名,那麼我們需要為所有可使用的組合構造一個聚合公鑰的默克爾樹: (P1, P2), (P2, P3), (P1, P3)
,並將根放到鎖定指令碼中。為了使用比特幣,我們提供了一個簽名以及一個證明我們的公鑰在默克爾樹上的證據。對於2-of-3多重簽名,默克爾樹中只有三個元素,證明將由兩個雜湊組成,其中一個是我們想要使用的雜湊,另一個則是其鄰雜湊。而對於7-of-11的多重簽名,已經有11!/7!/4!=330種可能的金鑰組合,而證明需要8個元素。一般來說,證明中的元素數量幾乎與多重簽名種的金鑰數成線性關係 ( log2(n!/m!/(n-m)!)
。
但是,有了公共金鑰的默克爾樹,我們不侷限於m-of-n的多重簽名。我們可以用任何我們想要的公共金鑰製作一棵默克爾樹。例如,如果我們有一臺膝上型電腦、一部電話、一個硬體錢包和一個恢復種子,我們可以構建一個結構,允許我們將比特幣與一臺膝上型電腦、一個硬體錢包、一部電話和一個硬體錢包或僅與一個恢復種子一起使用。只有當你用分支和其他東西構造更復雜的指令碼時,僅使用 OP_CHECKMULTISIG
會是不可能的。
(圖5 : 聚合公鑰的默克爾樹,不僅僅是m-of-n多重簽名)
BLS簽名方案最初是由斯坦福大學教授Dan Boneh等人於2001年就提出的一種簽名方案(原論文地址:https://www.iacr.org/archive/asiacrypt2001/22480516.pdf),而在2018年,Boneh教授還與IBM研究機構的Manu Drijvers等人更新了這種簽名方案(https://eprint.iacr.org/2018/483.pdf)。
Schnorr簽名方案是非常了不起的,如果我們做得對,我們可以將交易中的所有簽名和公鑰組合為一個金鑰和一個簽名,沒有人會發現它們對應於多個金鑰。區塊驗證也可以變得更快,因為我們可一次性驗證所有簽名。但它也存在著一些問題:
- 多重簽名方案需要多輪通訊,這會使冷儲存變得非常煩人;
- 對於簽名聚合,我們必須依賴隨機數生成器,我們不能像在ECDSA中那樣確定地選擇隨機點R;
- m-of-n多重簽名方案是棘手的,我們需要製作一個公共金鑰的默克爾樹(merkle tree),對於大數的m和n來說,這顆默克爾樹可以變得相當大;
- 我們不能將區塊中的所有簽名組合為單個簽名;
BLS簽名可修復上述所有問題,我們不需要隨機數,區塊中的所有簽名都可以組合成單個簽名,m-of-n類型的多重簽名非常簡單,我們不需要簽名者之間進行多輪通訊。此外,BLS簽名相比Schnorr簽名或ECDSA簽名要小2倍,其簽名不是一對,而是一個單曲線點。聽起來似乎很棒,對吧,讓我們看看它是如何工作的。
在開始之前,我們需要兩個新的結構:雜湊到曲線(hashing to the curve)以及曲線配對(curves pairing)。
雜湊到曲線(hashing to the curve)
通常對於ECDSA簽名以及Schnorr簽名,我們會雜湊一則訊息,並在簽名演算法中使用此雜湊作為一個數字。而對於BLS簽名,我們需要一個稍修改過的雜湊演算法,它直接雜湊到橢圓曲線。最簡單的方法是像往常一樣雜湊一則訊息,並將結果視為點的x座標。橢圓曲線(就像我們在比特幣中使用的曲線)通常有2²⁵⁶個點,而SHA-256雜湊演算法也給出了一個256位的結果。但是對於每個有效的x座標,有兩個點具有正和負的y座標(因為如果(x,y)在曲線 y²=x³+ax+b
上,則 (x,-y)
也在曲線上)。這意味著我們的雜湊有大約50%的概率為一些x找到兩個點,另有50%的概率無法找到。
(圖6 : 在有限域模23上定義的橢圓曲線y²=x³+7的玩具雜湊例子。只有一半的X座標有點,這裡只有第三次嘗試是成功的)
要為任何訊息找到一個點,我們可嘗試雜湊幾次,方法是在訊息中追加一個數字,並在失敗時遞增。如果 hash(m||0)
找不到點,我們嘗試 hash(m||1)
、 hash(m||2)
等等,直到最後得到一個匹配的點。然後我們選擇兩個對應點中的一個,比如y較小的那個,然後我們就完成了。
曲線配對(curves pairing)
我們需要的另一件事是一個非常特殊的函數,它在一條曲線(或兩條不同的曲線)上取兩點P和Q,並將它們對映至一個數字:
e(P, Q) → n. 我們還需要這個函數的一個重要屬性。如果我們有一些祕密數字x和兩點P和Q,不管我們用哪個點乘以這個數字,我們都應得到相同的結果: 即:e(x×P, Q) = e(P, x×Q). 基本上,我們需要能夠在不改變結果的情況下交換兩個參數之間的點乘數。更普遍地說,所有這些互換都應產生相同的結果: e(a×P, b×Q) = e(P, ab×Q) = e(ab×P, Q) = e(P, Q)^(ab) 我不想描述這個函數是如何精確工作的。基礎數學相當複雜,如果你想知道所有令人討厭的細節,我建議你閱讀這篇文章和其中的參考資料。如果你想更深入一些,那這篇論文(https://crypto.stanford.edu/pbc/thesis.pdf)會比較適合你。目前,我們只需要接受這種函數的存在,它們不會洩露關於我們的祕密數字X的任何資訊。
一個重要的注意項是,我們不能在這裡使用任何橢圓曲線(特別是標準比特幣曲線secp256k1不起作用)。為了使這個函數高效和安全,我們必須使用來自“友好配對”家族非常特殊的曲線。
現在我們有了構建BLS簽名所需的一切。和往常一樣,我們的私鑰是一些祕密數字 pk
,我們的公鑰是 P = pk×G
,我們要簽名的訊息則是 m
。
為了計算簽名,我們將訊息雜湊到曲線H(m) ,並將結果點乘以私鑰: S = pk×H(m)
.就是這樣了!沒有別的東西,沒有隨機數,沒有額外的操作,只有雜湊乘以私鑰!我們的簽名只是曲線上的一個單點,在壓縮序列化格式中只需要 33個位元組
!
(圖7 : BLS簽名生成,為了獲得簽名,我們將訊息的雜湊值乘以私鑰)
要驗證此簽名,可以使用我們的公鑰P並檢查:
e(P, H(m)) = e(G, S) 這為真,因為上面描述的配對函數的優良特性: e(P, H(m)) = e(pk×G, H(m)) = e(G, pk×H(m)) = e(G, S)
(圖8 : BLS簽名驗證,我們只需要檢查公鑰和訊息雜湊是否對映到與曲線生成器點和簽名相同的數字)
這個簽名方案既美觀又簡單,此外也有幾個非常好的功能,特別是對比特幣而言。
現在,讓我們組合區塊中的所有簽名!假設我們有一個包含1000筆交易的區塊,每筆交易都包含一個簽名 Si
、一個公鑰 Pi
以及一個簽名為 mi
的訊息。如果我們可以合併所有簽名,為什麼要儲存所有的簽名?畢竟,我們只關心區塊中的所有簽名是否有效。聚合簽名將只是區塊中所有簽名的總和:
S = S1+S2+…+S1000 要驗證區塊,我們需要檢查以下等式是否成立: e(G, S) = e(P1, H(m1))⋅e(P2, H(m2))⋅…⋅e(P1000, H(m1000)) 如果你仔細看,你會發現這是真的: e(G, S) = e(G, S1+S2+…+S1000) = e(G, S1)⋅e(G, S2)⋅…⋅e(G, S1000) = e(G, pk1×H(m1))⋅…⋅e(G, pk1000×H(m1000)) = e(pk1×G, H(m1))⋅…⋅e(pk1000×G, H(m1000)) = e(P1, H(m1))⋅e(P2, H(m2))⋅…⋅e(P1000, H(m1000)) 我們仍然需要知道所有的公鑰並計算1001個配對函數,但是至少區塊中的所有簽名只佔用33個位元組。簽名聚合可以由礦工完成,並節省區塊大量的空間。
如果我們使用多重簽名地址,我們將使用不同的金鑰簽署相同的交易。在這種情況下,我們可以像在Schnorr簽名方案中那樣進行金鑰聚合,在Schnorr中,我們將所有簽名和所有金鑰組合到一對金鑰和一個簽名上。以常見的3-of-3多重簽名方案為例(對於任何數量的簽名者都可以這樣做)。
組合它們的一個簡單方法,是將所有簽名和所有金鑰新增到一起。結果將是簽名 S=S1+S2+S3
和金鑰 P=P1+P2+P3
。很容易看出,同樣的驗證方程仍然有效:
e(G, S) = e(P, H(m))
e(G, S) = e(G, S1+S2+S3) = e(G, (pk1+pk2+pk3)×H(m)) = e((pk1+pk2+pk3)×G, H(m)) = e(P1+P2+P3, H(m)) = e(P, H(m)) 和Schnorr簽名方案一樣,運用BLS簽名需要保護自己免受流氓金鑰攻擊。我們可通過要求每個聯合簽名者證明他們具有的公鑰的私鑰(通過簽名他們的公鑰),或者我們可以在方案中新增一些非線性元素,使惡意金鑰攻擊成為不可能。我們不是簡單地將所有的金鑰和簽名相加,而是將它們乘以一個特定的數字,然後再相加: S = a1×S1+a2×S2+a3×S3
P = a1×P1+a2×P2+a3×P3 在這裡,簽名和金鑰的係數是根據簽名者的公鑰和所有其他公鑰來確定計算的: ai = hash(Pi, {P1,P2,P3}) 例如,它可以只是簽名者公鑰和用於簽名的整個公鑰集的級聯:
ai = hash(Pi||P1||P2||P3)
.
同樣的驗證方程仍然有效。在證明中會有額外的係數 ai
,但邏輯仍然存在。
這個方案的好處在於,你不需要在裝置之間進行多輪通訊。你只需要知道誰是其他簽名者。這比Schnorr簽名的3輪多重簽名方案要簡單得多。它也不依賴任何隨機性,它是一種完全確定的簽名演算法。
通常,我們不會想用n-of-n的多重簽名方案,我們更喜歡使用m-of-n(比如說2-of-3)的多重簽名方案。我們不想因為丟了一把私鑰就把錢給弄丟。在這種情況下,最好使用金鑰聚合。有了Schnorr簽名方案,我們就可以通過使用公共金鑰的默克爾樹來做到這一點,它不是最有效的辦法,但很管用,壞處在於一旦m和n值很大,默克爾樹(merkle tree)的大小就會成倍放大。
而對於BLS簽名方案,還有另一種方法。不過也沒那麼簡單,我們需要一個普通的雜湊函數,它輸出一個數字hash(x),以及一個到曲線的雜湊 H(x)。當我們決定使用多重簽名時,我們還需要一個“設定”階段,但在此之後,我們不再需要通訊,我們只需要簽名來簽署任何數量的交易。
再次,我將使用一個簡單的例子,我們希望構建三個不同裝置上儲存金鑰的2-of-3多重簽名方案,但它可以擴充套件到任何值的m和n。
設定階段
我們的每個裝置都有一個簽名者編號i=1,2,3,代表其在集合中的位置,一個私鑰 pki
以及一個對應的公鑰 Pi = pki×G
。這裡計算聚合公鑰的方式與之前完全相同:
P = a1×P1+a2×P2+a3×P3, ai = hash(Pi, {P1,P2,P3}) 現在,每個裝置都需要簽名,而編號i是我們的聚合公鑰的成員(對於每個i),聚合這些簽名並將結果儲存到相應的裝置上: MKi = (a1⋅pk1)×H(P, i)+(a2⋅pk2)×H(P, i)+(a3⋅pk3)×H(P, i) 這個簽名我們稱為“成員金鑰”,稍後我們將使用它進行簽名。每個成員金鑰都是訊息H(P,i)的有效n-of-n簽名,這意味著: e(G, MKi)=e(P, H(P,i)) 記住這個方程,我們以後會用到的。它將用來證明我們是多重簽名方案的有效參與者。
簽名
現在假設我們只想用金鑰 pk1
和 pk3
簽署一筆交易。我們生成兩個簽名 S1
和 S3
:
S1 = pk1×H(P, m)+MK1, S3=pk3×H(P, m)+MK3 並將它們相加以獲得單個簽名和金鑰: (S’, P’) = (S1+S3, P1+P3) 我在這裡寫為
P’
和S’
,來強調這個金鑰和簽名只由簽名者的一個子集簽名,它與P
不同,P
是所有簽名者的聚合金鑰。要驗證這3個簽名中的2個,我們需要檢查: e(G, S’) = e(P’, H(P, m))⋅e(P, H(P, 1)+H(P, 3)) 我們記得成員金鑰MK1
和MK3
是由聚合金鑰P
簽名的訊息H(P, 1)
及H(P, 3)
的有效簽名,因此: e(G, S’) = e(G, S1+S3)=e(G, pk1×H(P, m)+pk3×H(P, m)+MK1+MK3) =e(G, pk1×H(P, m)+pk3×H(P, m))⋅e(G, MK1+MK3)=e(pk1×G+pk3×G, H(P, m))⋅e(P, H(P, 1)+H(P, 3))=e(P’, H(P, m))⋅e(P, H(P, 1)+H(P, 3)) 就是這樣,要比n-of-n複雜一點,但仍然是可被理解的。
要實現這個多重簽名方案,我們需要將資金髮送到與聚合公鑰 P
對應的地址,並表示我們至少需兩個簽名。在比特幣指令碼語言中,鎖定指令碼可能如下所示:
OP_2
OP_CHECK_BLS_MULTISIG 這裡,
OP_2
告訴我們需要兩個金鑰來簽署訊息。我們不會說任何地方總共就只有3個簽名者,所以不能說它是2-of-3還是2-of-100多重簽名地址。
為了使用這個輸出,我們需要在我們的案例1和3中提供參與簽名者的金鑰 P’
、簽名 S’
以及索引。解鎖指令碼可能如下所示:
OP_1 OP_3 <P’> <S’> 結合這些指令碼,可以給我們提供所有必要的資訊。從
OP_1
和OP_3
,我們知道我們需要計算雜湊H(P, 1)
和H(P, 3)
,然後我們就擁有了驗證交易所需的一切。
這意味著對於任何 m
和 n
,我們只需要:
- 鎖定指令碼中的一個聚合公鑰
P
; - 參與簽名者的一個聚合公鑰
P’
- 一個聚合簽名;
- 帶有簽名者索引的
m
數字;
它非常緊湊,也很美!
但它也並非完美… 通常我們只使用一次地址(我們使用像bip32這樣的金鑰派生來生成新的私鑰和地址),但是對於每一組新的私鑰,我們也需要一組新的成員金鑰。一種不用每次都執行設定階段的方法是生成一組金鑰,比如1000個金鑰,並獲得相應的成員金鑰。畢竟,它們只有32位元組大小,然後,只有在使用了所有1000個地址時,我們才需要再次執行設定階段。
正如本文以及很多技術大佬在Twitter上所指出的,上面的討論沒有談到一個重要的部分,而它可能是BLS簽名方案最大的缺陷。
BLS簽名的配對是很難的 ,我們認為神奇的函數 e(P, Q)是有效和安全的,但事實並非如此。
BLS簽名驗證要比ECDSA簽名驗證的難度大上幾個數量級。對於具有1000筆交易的整個區塊的簽名聚合,仍然需要計算1000次配對,因此驗證一個區塊中的一個小簽名,可能比驗證1000個單獨的ECDSA簽名需要更長的時間。這裡BLS簽名能夠實現的唯一好處,在於我們可以在區塊中容納更多的交易,因為聚合簽名只需要大約32個位元組。
與BLS簽名不同,Schnorr簽名是非常有效的,它們可以一起驗證,而且這個過程比ECDSA效率高3倍。
另外,配對函數是複雜的,如果我們不夠小心,它會成為我們的敵人。一方面,我們希望配對能夠有效地、更快地驗證簽名,另一方面,我們不希望透露任何關於我們金鑰的資訊。這兩大需求相互競爭,我們需要非常小心地選擇對配對友好的曲線。
實際上有一種針對橢圓曲線加密系統的攻擊方法稱為MOV攻擊(以Menezes, Okamoto和Vanstone命名),它允許使用我們的魔法配對函數來降低系統的安全性。所以我們真的需要小心。
然後問題就出現了: 什麼對我們而言會更重要 ?
Schnorr簽名和BLS簽名方案都有自己的獨特之處,前者的主要缺點在於需要額外的通訊回合,以及不適合較大值m和n的多重簽名方案,後者的驗證時間則是最大的弊端,兩者共同存在的好處都是可聚合簽名,節省區塊空間。
就目前來看,Schnorr簽名應用於比特幣的可能性會更大,當然,方案都是可完善的,最終哪種協議能夠被採用,依然是一個未知數。