基於哈希函數的簽名,Part-1

回首近幾年,我有幸經歷了兩個相互衝突、卻又使人着迷的時代潮流變遷。第一個潮流變遷是:專家學者們耗費四十年設計的密碼學,終於派上用場;從信息加密、電話安全、到加密數字貨幣,咱們能夠在生活的方方面面發現使用密碼學的例子。算法

第二個潮流變遷是:全部密碼學家已經作好準備,迎接以上美好的幻滅。數組

正文開始以前我得重申一下,本文所講的不是所謂量子計算啓示錄(末日預言),也不是要講 21 世紀密碼學的成功。咱們要談論的是另外一件未成定局的事情——密碼學有史以來最簡單的(也是最酷炫的)技術之一:基於散列函數的簽名。安全

在 20 世紀 70 年代末,Leslie Lamport 發明了基於哈希函數(Hash Function,又稱散列函數)的簽名 ,並通過 Ralph Merkle 等人進一步改進。然後的不少年,這被視爲密碼學領域一灘有趣的「死水」,由於除了相應地產生冗長的(對比其餘複雜方案)簽名,基於哈希函數的簽名好像沒有什麼做用。然而近幾年來,這項技術彷佛有了復甦的跡象。這很大程度歸因於它的特性——不一樣於其餘基於RSA或離散對數假設的簽名,哈希函數簽名被視爲能夠抵抗量子計算攻擊(如 Shor’s 算法)。函數

首先,咱們進行一些背景介紹。工具

背景:哈希函數和簽名方法

在正式介紹哈希函數簽名以前,首先你得知道密碼學中的哈希函數是什麼。哈希函數能夠接受一串字符(任意長度)做爲輸入,通過「消化」後,產生固定長度的輸出。常見的密碼學哈希運算,像是 SHA二、SHA3 或 Blake2 等,經運算會產生長度介於 256 ~ 512 位的輸出。優化

一個函數 H(.) 要被稱做「密碼學」哈希函數,必須知足一些安全性的要求。這些要求有不少,不過咱們主要聚焦在如下三個方面:加密

  1. 抗-原像攻擊 Pre-image resistance (或俗稱「單向性」):給定輸出 Y=H(X),想要找到對應的輸入 X 使得 H(X)=Y 是一件「極度費時」的工做。(這裏固然存在許多例外,但最棒的部分在於,不論 X 屬於什麼分佈,找到 X 的時間成本和暴力搜尋相同。)
  2. 抗-次原像攻擊:這和前者有些微的差異。給定輸入 X,對於攻擊者來講,要找到另外一個 X’ 使得 H(X)=H(X’) 是很是困難的。
  3. 抗-碰撞:很難找到兩個輸入 X1, X2,使得 H(X1)=H(X2)。要注意的是,這個假設的條件比 抗-次原像攻擊還要嚴苛。由於攻擊者能夠從無垠的選擇中尋找任意兩個輸入。

咱們相信全部本文提到的哈希函數示例都能提供上述的全部特性。換言之,沒有任何可行的(甚至是概念上的)方法能破解它。固然這種狀況也是會變的,若是破解的方法被找到,咱們固然會當即停用哈希函數(稍後會討論關於量子計算攻擊的特例)。翻譯

咱們的目標是使用哈希函數構造數字簽名方案,所以簡要回顧數字簽名這個詞能帶來很大的幫助。設計

數字簽名方法源於公鑰的使用,使用者(簽署人)生成一對密鑰:公鑰和私鑰。使用者自行保管私鑰,並可以用私鑰「簽署」任何消息,從而產生相應的數字簽名。任何一個持有公鑰的人都能驗證該消息正確性和相關簽名。cdn

從安全的角度來講,咱們但願簽名是不可僞造的,或是說「存在不可僞造性」。這意味着攻擊者(沒有私鑰控制權的人)沒法在某段消息上僞造你的簽名。有關數字簽名安全的更多定義請參閱這裏。

Lamport 一次性簽名

在 1979 年,一位名叫 Leslie Lamport 的數學家發明了世界上第一個基於哈希函數的簽名。Lamport 發現只要使用簡單的哈希函數,或是單向函數,就能夠構建出很是強大的數字簽名方法。

強大的前提是,用戶只須要作一次簽名的動做就能保證安全性!後續會作更詳細的闡述。

爲了更好的討論,咱們假設如下條件:一個哈希函數,它能接受 256 位的輸入併產生 256 位的輸出; SHA256 哈希函數就是個絕佳的示範工具;咱們也須要能產生隨機輸入的方法。

假設咱們的目標是對 256 位的消息進行簽名。要獲得咱們須要的密鑰,首先須要生成隨機的 512 個位字符串,每一個位字符串長度爲 256 位。爲了便於理解,咱們將這些字串列爲兩個獨立的表,並以符號代指:

sk0= sk10, sk20, …,sk2560

sk1= sk11, sk21, …,sk2561

咱們以列表 (sk~0~, sk~1~) 表示用來簽名的

密鑰
。接下來爲了生成公鑰,咱們將隨機的位字符串經過 H(.) 進行哈希運算,獲得公鑰以下表:

pk0= H(sk10), H(sk20), …,H(sk2560)

pk1= H(sk11), H(sk21), …,H(sk2561)

如今咱們能夠將公鑰 (pk~0~,pk~1~) 公佈給全部人知道。好比說,咱們能夠把公鑰發給朋友,嵌入證書中,或是發佈在 Keybase 上。

接着咱們使用密鑰對 256 位消息 M 進行簽名。首先咱們得將消息 M 重現爲獨立的 256 位元(Bit,又稱「比特」):

M1, M2, …, M256 ∈ {0, 1}

簽名算法的其他部分很是簡單。咱們從消息 M 的第 1 位至第 256 位,逐一相應在密鑰列表中的其中一個密鑰上取出字符串。而所選密鑰取決於咱們要簽名的消息每一位(bit)的值。

具體一點地說,對於 i = [1,256],若是第 i 位的消息位元 Mi = 0,咱們會從 sk0 表中選擇第 i 個字符 (ski0) ,做爲咱們簽名的一部分;若是第 i 位的消息位元 Mi = 1,咱們則從 sk1 表進行前述過程(即,若是咱們要對消息 M 中的第 3 位進行簽名,而該位值爲 0,則使用 sk0 中的第三位,sk03,做爲咱們簽名的一部分)。對每一個消息位元完成此操做後,咱們將選中的字符串鏈接,獲得簽名。

過程如圖示說明,由於部分過程化簡,密鑰和消息長度只有 8 個 bit(位元)。要注意的是,每一個色塊表明的都是不一樣的隨機 256 位字符串。

當某個用戶(已經知道公鑰 (pk0, pk1))收到消息 M 和簽名,她可以輕易地驗證這個簽名。咱們以 si 表示簽名中第 i 個組成部分,用戶可以檢查相應的消息 Mi 並計算哈希值 H(si) 。若是 Mi = 0 ,則哈希值必須匹配公鑰 pk0 中的元素;若是 Mi = 1 ,則哈希值必須匹配公鑰 pk1 中的元素。

若是簽名中的每一個元素通過哈希運算後,都能找到對應的正確部分的公鑰,咱們就會說這個簽名是有效的。如下是驗證過程圖示,簽名中至少有一個簽名元素:

若是你開始以爲 Lamport 的計劃有些瘋狂,你既是對的,也是錯的。

首先探討下這個數字簽名方法的弊端。咱們會發現, Lamport 方法的簽名和密鑰實在太大了,大約有數千 bits。並且更要命的是,這個方法存在嚴重的安全侷限:每一個密鑰只能被用來簽名一個消息,因此 Lamport 方法做爲「一次性簽名」 在這裏被拿來舉例。

這種安全侷限爲何存在呢?回想一下, Lamport 簽名代表了在各個消息位元上可能的兩個密鑰之一。假如只須要簽署一條信息,這個簽名方法徹底沒問題。然而,若是我簽署了兩條在每個對應位置 i 的 bit 值都不一樣的消息,而後連同密鑰一塊兒發送出去,這可能致使大問題!

假設攻擊者從不一樣的消息獲得兩個有效的簽名,她便可以發起 「混合搭配(mix and match)」攻擊,成功僞造簽署第三條我從未簽名過的信息。如下圖示說明這個攻擊過程:

這個問題的嚴重程度取決於你簽名的消息的相異程度,以及有多少消息被攻擊者給截獲了。但總的來講,這確定不是件好事。

讓咱們總結一下 Lamport 簽名方法;它很簡單、快速,但它在實際應用上還有不少不足之處。或許咱們能夠作一點優化?

從一次性簽名到屢次簽名:基於默克爾樹 (Merkle’s tree) 的簽名

Lamport 簽名方法是個好的開端,可是沒法用單一密鑰簽名多條信息,是它最大的弊端。Martin Hellman 的學生 Ralph Merkle 由此獲得大量啓發,他很快地想到了一個聰明的解決辦法。

雖然咱們不打算在這裏展開解釋默克爾方法的步驟,咱們仍是來試着理清 Ralph 的想法。

咱們如今的目標是用 Lamport 簽名方法簽署 N 條信息。最直觀的方法是,以最初的 Lamport 方法生成 N 個不一樣的密鑰對,而後將全部公鑰關聯起來,集合成一個超巨大的 mega-key。(mega-key是我現編的術語。)

若是簽名者繼續拿着這麼一把密鑰集合,她就能夠對 N 條不一樣消息進行簽名,嚴格上來說這也只是一把 Lamport 密鑰。看起來,這樣就解決了密鑰重用的問題。驗證者也有對應的公鑰可以驗證全部收到的消息。沒有任何的 Lamport 密鑰被使用兩次。

很明顯的,這種方法很糟糕,由於時間成本過高了。

具體地說,上述這種天真的方法中,爲了達到要求的簽名次數,簽名者必須分發比普通 Lamport 公鑰還要大數倍的公鑰(簽名者還要繼續拿着一樣巨大的私鑰)。人們極可能會對這種結果感到不滿,也會反思有沒有辦法避免這種負做用產生。接下來,讓咱們進入 Merkle 方法。

Merkle 方法但願能找到一個能簽署多條不一樣消息的方法,同時避免公鑰的成本線性激增。Merkle 方法的實現以下:

  1. 首先,生成 N 個獨立的 Lamport 密鑰,咱們以 (PK1, SK1), …, (PKN, SKN) 表示之。
  2. 接下來,將每個公鑰分別放到 Merkle hash tree (見下圖),並計算根節點哈希值。這個根節點就會成爲Merkle簽名方法中的 「主公鑰」。
  3. 簽名者報關所有的 Lamport 密鑰(公鑰和私鑰),用於簽名。

關於 Merkle tree 的更多描述請點擊這裏。概略地說,Merkle 方法提供了一種能收集不一樣的值,並用一個 「根」 哈希(例子中使用的哈希函數,長度爲 256 bits)表明所收集的值的方法。給出這個根哈希,就能簡單「證實」 某個元素存在於這個給出的哈希樹。並且這個證實的大小和葉節點數量成對數關係。

Merkle tree,來自維基百科的解釋。Lamport 公鑰被放進葉節點中,而後根節點成爲主公鑰。

要簽名的時候,簽名者從 Merkle tree 中直接選擇公鑰,並用對應的 Lamport 密鑰簽名。接着她將獲得的簽名結果鏈接 Lamport 公鑰並附上「Merkle 證實」。Merkle root 能夠來佐證該默克爾樹中包含選中的公鑰(即整個方法使用的公鑰)。最後簽名者將整個集合看成消息簽名發送出去。

(驗證者只要直接將這個「簽名」分別解壓爲 Lamport 簽名、 Lamport 公鑰、 Merkle 證實,就能進行驗證。驗證者可以依靠拿到的 Lamport 公鑰驗證 Lamport 簽名,並用 Merkle 證實這把公鑰的確存在於 Merkle tree 中。只要知足這三個條件,驗證者就能確信簽名是有效的。)

這個方法的缺點是會將「簽名」大小增長兩倍以上。不過,如今 Merkle 方法主要的公鑰只是一串簡單的哈希值,使得這個方法比上面提到的原始 Lamport 方法更爲簡潔。

最後還有個優化部分,密碼學強度的僞隨機數發生器可以輸出生成各式各樣的密鑰,同時「壓縮」密鑰數據自己。這使得原先龐大的位元(顯然是隨機的)可以轉換爲簡短的「種子(seed)」。

很贊啦!

讓簽名和密鑰更有效率一點

Merkle 方法使得一次性簽名轉變爲 N 次性簽名。構造這種方法仍然須要基於某些一次性簽名方法,好比 Lamport 方法;但不幸的是,Lamport 方法的(帶寬)成本仍相對高昂。

有兩種主要的方法能夠下降這些成本。第一種也是 Merkle 提出的;爲了更好的解釋許多強大的簽名方法,咱們優先說明這項技術。

回想一下 Lamport 方法,要對一條 256 位的消息進行簽名,咱們須要一個包含 512 個獨立密鑰(和公鑰)位串的向量,簽名自己就是 256 個密鑰位串的集合。(這些數字會被須要簽名的消息位元激活,位元能夠是 「0」 或 「1」 ,所以須要從兩張不一樣的密鑰表中提取適合的密鑰元素。 )

這裏引起了新的思考:若是咱們不對全部的消息位元進行簽名,會怎麼樣呢?

更詳細點說,在 Lamport 方法中,咱們經過輸出密鑰位串對一條消息的每一個位元進行簽名——不管它的值是什麼。若是咱們不要同時簽名一條消息中 0 和 1 的位元,而是隻簽名 1 的位元,那又會如何呢?這麼作可以將公鑰和私鑰的大小減半,由於咱們能夠徹底丟掉整條 sk0 列。

如今咱們只有單一列位串的密鑰 sk1,…,sk256,對消息的每一個位元 Mi = 1咱們都會輸出一個字符串 ski;對於消息的每一個位元 Mi = 0咱們都會輸出……無(由於許多消息都會包含不少的 0 位元,這麼作能縮減簽名大小,這些 0 位元將再也不帶來任何成本)。

這種方法的明顯缺陷是:它

極度
不安全,因此請不要這麼作!

舉例來講,假設有個攻擊者觀察到一條已經被簽名的消息,消息開頭是「1111…」。如今攻擊者想要在不破壞簽名的狀況下,將消息編輯成「0000…」,只須要刪掉這條簽名中的幾個組成部分便可!簡言之,雖然要將 0 位元「翻轉」 成 1 位元很困難,但反之要將 1 換成 0 就很是簡單了。

如今有了個解決辦法,並且它很是巧妙。

讓咱們接着瞧瞧。雖然沒法避免攻擊者將消息中的 1 改爲 0 ,但咱們能發現這些改動。只要將一個簡單的「校驗和(checksum)」附加到消息上,而後將消息和校驗和一塊兒簽名。對於簽名驗證者來講,她必須驗證整份簽名的兩個值,也須要肯定收到的校驗和是正確的。

咱們使用的校驗和很是小:它由簡單的二進制整數組成,表示原始消息中的全部 0 位元數。

若是攻擊者試圖修改消息內容(或是校驗和),使得部分 1 位元變成 0 位元,並無手段能夠阻止她。可是這種攻擊會增長消息中的 0 位元數,這會使得校驗和無效,驗證者從而會拒絕這個簽名。

​固然,機智的攻擊者可能還會試圖混淆校驗和(校驗和也和消息一塊兒被簽名),增長校驗和的整數值來匹配她篡改的位元數。然而最關鍵的是,由於校驗和是二進制整數,若是要增長校驗和的值,攻擊者勢必得將一些 0 位元轉換成 1 位元。又由於校驗和也被簽過名,這種簽名方法從源頭阻止這種轉換(將 0 換成 1),所以攻擊者沒法得逞。

(若是你繼續記錄下去,的確會增長被簽名的「消息」的大小。在咱們的例子中,一條 256 位的消息的校驗和,須要額外的 8 位元及增長相應的簽名成本。不過,若是這條消息包含許多 0 位元,這麼作對於縮減簽名大小仍然很是有效。)

原文連接: https://blog.cryptographyengineering.com/2018/04/07/hash-based-signatures-an-illustrated-primer/

做者: Matthew Green

翻譯&校對: Ian Liu & 阿劍

以太中文網經受權轉載

相關文章
相關標籤/搜索