隱私加密系列|資料完整性演算法_Schnorr數字簽名介紹

買賣虛擬貨幣

概述

私鑰-公鑰對是許多加密安全性的基礎,從安全的網路瀏覽到銀行到加密貨幣。私鑰-公鑰對是不對稱的。這意味著給定一個數字(私鑰),就有可能推匯出另一個數字(公鑰)。但是進行逆向操作是不可行的。正是這種不對稱性使人們可以公開地共享公共金鑰,並且一致沒有人能計算出我們的專有金鑰。

非對稱金鑰對主要用於兩種應用:

1.在身份驗證中,證明您知道私鑰;

2.在加密中,可以對訊息進行編碼,只有擁有私鑰的人才能解密和讀取訊息;

在數字簽名介紹中,我們將討論一類特殊的金鑰:從橢圓曲線派生的金鑰。還有其他非對稱方案,其中包括基於RSA金鑰的質數乘積的方案。

這是對數字簽名的間接介紹。我們使用Rust程式碼來演示此處提出的一些想法。本段程式碼是使用libsecp256k-rs庫。

這很有意思,secp256k1是橢圓曲線的名稱,它為許多加密貨幣交易(包括比特幣)中提供非常重要的加密保護工作。

這個特殊的庫有一些很好的特性。我們重寫了+(加法)和*(乘法)運算子,剎車鏽跡看起來更像數學公式。這使得我們更容易運用我們將要探索的想法。

Schnorr簽名的基礎

公鑰與私鑰

我們要做的第一件事是從橢圓曲線創造一個公鑰和私鑰。

在secp256k1上,私鑰只是介於0和〜2256之間的標量整體值。我們在secp256k1曲線上有一個特殊的點,稱為G,它稱為“原點”。透過將曲線上的G與自身相加ka次來計算公鑰。這是乘以標量的定義,寫為:

讓我們以這篇文章中的一個例子為例,其中1的公鑰在以未壓縮格式編寫時是0479BE667…C47D08FFB10D4B8。下面的程式碼片段演示了這一點:

1externcratelibsecp256k1_rs; 2 3uselibsecp256k1_rs::{SecretKey,PublicKey}; 4 5#[allow(non_snake_case)] 6fnmain(){ 7//Createthesecretkey"1" 8letk=SecretKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); 9//Generatethepublickey,P=k.G10letpub_from_k=PublicKey::from_secret_key(&k);11letknown_pub=PublicKey::from_hex("0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8").unwrap();12//Compareittotheknownvalue13assert_eq!(pub_from_k,known_pub);14println!("Ok")15}

建立簽名

採取的方法

當對標量使用正確選擇的隨機值時,反向ECC數學乘法(即除法)幾乎是不可行的。這種特性被稱為離散日誌問題(Discrete Log Problem),並被使用了很多密碼學和數字簽名背後的原理。有效的數字簽名是提供簽名的人知道與訊息關聯的公共金鑰相對應的私有秘密的證據,或者表明他們已經解決了離散日誌問題(Discrete Log Problem)。

建立簽名的方法始終遵循以下方法:

1.生成一個機密的一次性數字(稱為nonce),r。

2.從r建立一個公鑰R(其中R = rG)。

3.將以下內容傳送給您的收件人Bob-您的訊息(m),R和您的公共金鑰(P = kG)。

實際簽名是透過對以上所有公開資訊的組合進行雜湊運算來建立的,以建立一個質詢,例如:

選擇雜湊函式,以便e與您的私鑰具有相同的範圍。

在我們的情況下,我們需要返回256位數字的物件,因此SHA256是一個不錯的選擇。

現在使用您的私人資訊構建簽名:

Bob現在還可以計算e,因為他已經知道m,R,P。但是他不知道您的私鑰或隨機數。注意:像這樣構造簽名時,它稱為Schnorr簽名,下一節將對此進行討論。還有其他構造s的方法,例如比特幣中使用的ECDSA 。但是看這個:

乘以右側:

替換R = rG和P = kG,我們有:

因此,Bob必須只計算與簽名(s.G)相對應的公鑰,並檢查它是否等於Bob已經知道的最後一個等式(R + P.e)的右側。我們為什麼需要Nonce?為什麼我們需要在標準簽名中使用nonce?我們使用下面公式簽署訊息m

然後簽名是s=ek。現在像之前一樣,我們可以檢查簽名是否有效:

到現在為止還挺好。但是現在任何人都可以讀取您的私鑰,因為s是一個標量,因此k = s/e並不難。對於Nonce,您必須求解k =(sr)/ e,但是r未知,因此只要r是隨機選擇的,這是不可行的計算。我們可以證明,放棄隨機數確實是非常不安全的:

1externcratelibsecp256k1_rsassecp256k1; 2 3usesecp256k1::{SecretKey,PublicKey,thread_rng,Message}; 4usesecp256k1::schnorr::{Challenge}; 5 6#[allow(non_snake_case)] 7fnmain(){ 8//Createarandomprivatekey 9letmutrng=thread_rng();10letk=SecretKey::random(&mutrng);11println!("Myprivatekey:{}",k);12letP=PublicKey::from_secret_key(&k);13letm=Message::hash(b"Meetmeat12").unwrap();14//Challenge,e=H(P||m)15lete=Challenge::new(&[&P,&m]).as_scalar().unwrap();1617//Signature18lets=e*k;1920//Verifythesignature21assert_eq!(PublicKey::from_secret_key(&s),e*P);22println!("Signatureisvalid!");23//Butlet'strycalculatetheprivatekeyfromknowninformation24lethacked=s*e.inv();25assert_eq!(k,hacked);26println!("Hackedkey:{}",k)27}

ECDH那麼在安全通訊的各方如何生成用於加密訊息的共享機密?一種方法稱為橢圓曲線Diffie-Hellman交換(ECDH),這是一種簡單的方法。ECDH被用於許多地方,包括在通道協商期間的閃電網路。運作方式如下。Alice和Bob想要安全溝通。一種簡單的方法是使用彼此的公鑰並計算

1externcratelibsecp256k1_rsassecp256k1; 2 3usesecp256k1::{SecretKey,PublicKey,thread_rng,Message}; 4 5#[allow(non_snake_case)] 6fnmain(){ 7letmutrng=thread_rng(); 8//Alicecreatesapublic-privatekeypair 9letk_a=SecretKey::random(&mutrng);10letP_a=PublicKey::from_secret_key(&k_a);11//Bobcreatesapublic-privatekeypair12letk_b=SecretKey::random(&mutrng);13letP_b=PublicKey::from_secret_key(&k_b);14//Theyeachcalculatethesharedsecretbasedonlyontheotherparty'spublicinformation15//Alice'sversion:16letS_a=k_a*P_b;17//Bob'sversion:18letS_b=k_b*P_a;1920assert_eq!(S_a,S_b,"Thesharedsecretisnotthesame!");21println!("Thesharedsecretisidentical")22}

出於安全原因,通常在每個會話中隨機選擇私鑰(您將看到使用的術語是臨時金鑰),但是我們有一個問題,就是不能確定對方是誰(可能是由於中間人攻擊)。可以採用各種其他身份驗證步驟來解決此問題,在此不再贅述。

Schnorr簽名Schnorr簽名被認為是在隨機預言模型中可證明安全的最簡單的數字簽名方案。那為什麼要大驚小怪呢?Schnorr簽名如此有趣和而潛在危險的原因在於它們的簡單性。Schnorr簽名是線性的,所以有一些很好的特性。橢圓曲線具有乘法特性。因此如果您有兩個標量x,y和對應的點X,Y,則滿足以下條件:

Schnorr簽名的形式是s=r+e.k。這種構造也是線性的,因此它很好地符合橢圓曲線數學的線性。在我們驗證簽名時,您在上一節中看到了此屬性。Schnorr簽名的線性使得它非常吸引人,尤其是:1. 聚合簽名;2. 原子交換;3. “無指令碼”指令碼(Scriptless Scripts);聚合簽名讓我們看看如何使用Schnorr簽名的線性特性來構造兩個多重簽名。Alice和Bob希望在不需要相互信任的情況下共同簽名(比如Tari事務);也就是說,他們需要能夠證明各自金鑰的所有權,並且聚合簽名只有在Alice和Bob都提供其部分簽名時才有效。假設私鑰表示為ki,公鑰表示為Pi。如果我們要求Alice和Bob各自提供一個隨機數,我們可以嘗試:、

所以看起來Alice和Bob可以提供自己的R,任何人都可以從Rs和公鑰的和構造兩個多重簽名。這確實有效:

1externcratelibsecp256k1_rsassecp256k1; 2 3usesecp256k1::{SecretKey,PublicKey,thread_rng,Message}; 4usesecp256k1::schnorr::{Schnorr,Challenge}; 5 6#[allow(non_snake_case)] 7fnmain(){ 8//Alicegeneratessomekeys 9let(ka,Pa,ra,Ra)=get_keyset();10//Bobgeneratessomekeys11let(kb,Pb,rb,Rb)=get_keyset();12letm=Message::hash(b"amultisigtransaction").unwrap();13//Thechallengeusesbothnoncepublickeysandprivatekeys14//e=H(Ra||Rb||Pa||Pb||H(m))15lete=Challenge::new(&[&Ra,&Rb,&Pa,&Pb,&m]).as_scalar().unwrap();16//Alicecalculateshersignature17letsa=ra+ka*e;18//Bobcalculateshissignature19letsb=rb+kb*e;20//Calculatetheaggregatesignature21lets_agg=sa+sb;22//S=s_agg.G23letS=PublicKey::from_secret_key(&s_agg);24//ThisshouldequalRa+Rb+e(Pa+Pb)25assert_eq!(S,Ra+Rb+e*(Pa+Pb));26println!("Theaggregatesignatureisvalid!")27}2829#[allow(non_snake_case)]30fnget_keyset()->(SecretKey,PublicKey,SecretKey,PublicKey){31letmutrng=thread_rng();32letk=SecretKey::random(&mutrng);33letP=PublicKey::from_secret_key(&k);34letr=SecretKey::random(&mutrng);35letR=PublicKey::from_secret_key(&r);36(k,P,r,R)37}但是這種方案並不安全!

Key Cancellation(金鑰取消攻擊)讓我們再來看前面的場景,但是這次,Bob提前知道了Alice的公鑰和nonce,直到她公開它們。現在Bob說謊,說他的公鑰是P'b = Pb-Pa,公有隨機數是R'b = Rb-Ra。注意,Bob不知道這些偽造值的私鑰,但這無關緊要。每個人都根據聚合方案假定sagg = Ra + R'b + e(Pa + P'b)。但是Bob可以自己建立此簽名:

1externcratelibsecp256k1_rsassecp256k1; 2 3usesecp256k1::{SecretKey,PublicKey,thread_rng,Message}; 4usesecp256k1::schnorr::{Schnorr,Challenge}; 5 6#[allow(non_snake_case)] 7fnmain(){ 8//Alicegeneratessomekeys 9let(ka,Pa,ra,Ra)=get_keyset();10//Bobgeneratessomekeysasbefore11let(kb,Pb,rb,Rb)=get_keyset();12//..andthenpublisheshisforgedkeys13letPf=Pb-Pa;14letRf=Rb-Ra;1516letm=Message::hash(b"amultisigtransaction").unwrap();17//Thechallengeusesbothnoncepublickeysandprivatekeys18//e=H(Ra||Rb'||Pa||Pb'||H(m))19lete=Challenge::new(&[&Ra,&Rf,&Pa,&Pf,&m]).as_scalar().unwrap();2021//Bobcreatesaforgedsignature22lets_f=rb+e*kb;23//Checkifit'svalid24letsG=Ra+Rf+e*(Pa+Pf);25assert_eq!(sG,PublicKey::from_secret_key(&s_f));26println!("Bobsuccessfullyforgedtheaggregatesignature!")27}2829#[allow(non_snake_case)]30fnget_keyset()->(SecretKey,PublicKey,SecretKey,PublicKey){31letmutrng=thread_rng();32letk=SecretKey::random(&mutrng);33letP=PublicKey::from_secret_key(&k);34letr=SecretKey::random(&mutrng);35letR=PublicKey::from_secret_key(&r);36(k,P,r,R)37}

更好的聚合方法在“Key Cancellation(金鑰取消攻擊)”中,Bob不知道其已釋出的R和P值的私鑰。我們可以透過要求Bob簽署一條訊息證明他確實知道私鑰來擊敗Bob。這是可行的,但是它需要各方之間進行另一輪訊息傳遞,這不利於良好的使用者體驗。一個更好的方法是將以下一個或多個特性結合起來:在普通的公共金鑰模型中,它必須被證明是安全的,而不必像我們可能曾要求Bob在天真的方法中那樣,必須證明秘密金鑰的知識。它應該滿足正常的Schnorr方程,即結果簽名可以用R+eX形式的表示式來驗證。、它允許互動式聚合簽名(IAS),要求籤名者進行合作。它允許使用非互動式聚合簽名(NAS),任何人都可以進行聚合。它允許每個簽名者簽名同一條訊息m。它允許每個簽名者簽名自己的訊息mi。

多籤MuSigMuSig是最近提出的簡單簽名聚合方案,它滿足了上一節中的所有屬性。MuSig示範我們將在此處演示互動式MuSig方案,其中每個簽名者都簽名相同的訊息。該方案的工作原理如下:1. 和以前一樣,每個簽名者都有一個公鑰-私鑰對。2. 每個簽名者都對自己的公共隨機數承擔義務(在本演示中,我們將跳過此步驟)。此步驟對於防止某些流氓金鑰攻擊是必要的3. 每個簽名者都會發布其隨機數Ri的公鑰。4. 每個人都計算相同的“共享公鑰”,X如下:

請注意,在前面的公共金鑰排序中,應使用一些確定性約定,例如序列化金鑰的字典順序。1. 每個人還計算共享隨機數R = ∑Ri。2. 挑戰e是H(R||X||m)。3. 每個簽名者對簽名的貢獻如下:

注意,這裡與標準Schnorr簽名的唯一不同之處是包含了因子ai。聚合簽名是通常的總和是s=∑si透過確認以下內容進行驗證:

證明方式:

讓我們用三個multisig中的來演示:

1externcratelibsecp256k1_rsassecp256k1; 2 3usesecp256k1::{SecretKey,PublicKey,thread_rng,Message}; 4usesecp256k1::schnorr::{Challenge}; 5 6#[allow(non_snake_case)] 7fnmain(){ 8let(k1,X1,r1,R1)=get_keys(); 9let(k2,X2,r2,R2)=get_keys();10let(k3,X3,r3,R3)=get_keys();1112//I'msettingtheorderhere.Ingeneral,they'llbesorted13letl=Challenge::new(&[&X1,&X2,&X3]);14//ai=H(l||p)15leta1=Challenge::new(&[&l,&X1]).as_scalar().unwrap();16leta2=Challenge::new(&[&l,&X2]).as_scalar().unwrap();17leta3=Challenge::new(&[&l,&X3]).as_scalar().unwrap();18//X=sum(a_iX_i)19letX=a1*X1+a2*X2+a3*X3;2021letm=Message::hash(b"SomeSharedMultiSigTx").unwrap();2223//Calcsharednonce24letR=R1+R2+R3;2526//e=H(R||X||m)27lete=Challenge::new(&[&R,&X,&m]).as_scalar().unwrap();282930//Signatures31lets1=r1+k1*a1*e;32lets2=r2+k2*a2*e;33lets3=r3+k3*a3*e;34lets=s1+s2+s3;3536//Verify37letsg=PublicKey::from_secret_key(&s);38letcheck=R+e*X;39assert_eq!(sg,check,"ThesignatureisINVALID");40println!("Thesignatureiscorrect!")41}4243#[allow(non_snake_case)]44fnget_keys()->(SecretKey,PublicKey,SecretKey,PublicKey){45letmutrng=thread_rng();46letk=SecretKey::random(&mutrng);47letP=PublicKey::from_secret_key(&k);48letr=SecretKey::random(&mutrng);49letR=PublicKey::from_secret_key(&r);50(k,P,r,R)51}

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;