零知識證明實踐教程,第二部分

本文是零知識證明簡單實踐教程的第二部分,
第一部分見:零知識證明第一部分
第三部分見:零知識證明第三部分

現在一個問題是,prover(證明者)可能撒謊,比如原本它應該向verifier(驗證者)揭露p[1]的值,但prover出於某種原因或者純粹惡作劇,把這個值替換成其他的數值。因此,我們要解決的問題是,如何做到如果prover撒謊,verifier能夠知道呢?於是,“承諾commitment”被提出來了。

承諾,單單從字面來理解,指的是當前對未來某一刻的保證,當前一方拿到它,到了未來能夠驗證這個承諾是成立還是失敗。本文中的承諾的含義跟這個基本一致。

prover的承諾 commitment

在密碼學中,承諾的表示有很多種,這裏使用Merkle樹來說明。

在這裏插入圖片描述

上圖是一個merkle樹,葉子節點是明文信息,其他節點是兩個子節點的hash的值。merkle樹的一個特點是隻要子節點有一點點改動,會導致該節點的父節點以及以上節點都變動。換一種表述是隻要merkle樹的根節點不變,那麼任何一個節點都沒變動。下面的圖片表示,如果我把葉子節點Boogie改動一點,導致其他節點的變動。
在這裏插入圖片描述
Merkle樹的認證路徑

一個驗證過程:
假如上圖所示的merkle樹,您早先拿到了根節點的值,現在,除了第二個葉子節點的信息“Sir”,不讓你知道其他葉子節點的情況下,如何向你證明Sir是這棵樹的一個葉子節點呢?

做法:
我只需要讓你知道4號節點的hash值和3號節點的hash值,以及Sir節點,就可以了。

原因:
你可以使用上面這幾個值計算出merkle樹的根節點的值,跟原來的對比,如果相等,表面驗證通過。否則驗證失敗。上面這幾個值組成了一個完整的從子節點到根節點的驗證路徑,下文我們稱它們爲驗證路徑

現在回到我們的例子,如果prover將數組p中的每一個元素作爲一顆merkle樹的葉子節點,並將根節點作爲承諾發給verifier。然後,verifier發送一個隨機數 i 給prover,prover向verifier發送p[i]和p[i+1]的值和merkle樹的驗證路徑。verifier通過這些信息能夠判斷這兩個元素p[i]和p[i+1]是不是在數組p中。這樣解決了verifier撒謊的問題。

下面的代碼是上面這一段描述的實現,注意的是下面這個代碼只是爲了演示功能的作用,不是優化的。

import hashlib
from math import log2, ceil

def hash_string(s):
    return hashlib.sha256(s.encode()).hexdigest()

class MerkleTree:
    """
    A naive Merkle tree implementation using SHA256
    """
    def __init__(self, data):
        self.data = data
        next_pow_of_2 = int(2**ceil(log2(len(data))))
        self.data.extend([0] * (next_pow_of_2 - len(data)))
        self.tree = ["" for x in self.data] + \
                    [hash_string(str(x)) for x in self.data]
        for i in range(len(self.data) - 1, 0, -1):
            self.tree[i] = hash_string(self.tree[i * 2] + self.tree[i * 2 + 1])

    def get_root(self):
        return self.tree[1]

    def get_val_and_path(self, id):
        val = self.data[id]
        auth_path = []
        id = id + len(self.data)
        while id > 1:
            auth_path += [self.tree[id ^ 1]]
            id = id // 2
        return val, auth_path

def verify_merkle_path(root, data_size, value_id, value, path):
    cur = hash_string(str(value))
    tree_node_id = value_id + int(2**ceil(log2(data_size)))
    for sibling in path:
        assert tree_node_id > 1
        if tree_node_id % 2 == 0:
            cur = hash_string(cur + sibling)
        else:
            cur = hash_string(sibling + cur)
        tree_node_id = tree_node_id // 2
    assert tree_node_id == 1
    return root == cur

上面的merkle樹存在的缺陷是,verifier可能通過暴力的方式找到其他葉子節點的明文信息。如果verifier知道數組p的所有值,很可能以此來推測出數組m的值,這違背了“零知識”原則。在下一部分內容中,我們將講解如何通過一個簡單的方法來混淆merkle樹,讓這種暴力破解的方式變得不可能或者基本不可能。
在下一部分內容中,我們還將繼續進一步說明零知識證明的多次驗證過程等內容。

第一部分見:零知識證明第一部分
第三部分見:零知識證明第三部分

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章