1. 程式人生 > 實用技巧 >去中心化數字身份DID簡介——四、使用者屬性的零知識證明

去中心化數字身份DID簡介——四、使用者屬性的零知識證明

上一篇文章中,我們介紹了使用者具有多個身份屬性時,選擇性的把其中的一個屬性暴露出來,而不會造成其他資訊的暴露。更進一步的情況,某些時候我們只需要驗證使用者的年齡達到多少歲,或者小於多少歲,但是並不關心使用者的具體年齡和出生日期,比如在購買菸酒時,商家需要驗證使用者的年齡大於18歲。除了年齡,住址、民族等都可能會有對某個斷言進行驗證的情況。比如某旅遊景點,對本市所有居民免費,所以居民只需要證明自己身份證上的住址在某市,而不需要暴露具體的居住地址。這些只給出證明的答案,而不暴露其他任何身份資訊的情況,都是零知識證明的範疇。

傳統的零知識證明演算法很複雜,難以理解,也難以實現,而且就算實現了,也不能很好的滿足我們數字身份中關於身份屬性驗證的問題,因為這裡涉及到三方:發證方、持證方、驗證方。在發證方將證件(可驗證宣告VC)發給持證方時,發證方並不知道持證方以後會遇到驗證大於18歲,還是驗證大於65歲,另外持證方在生成亮證(可驗證表達VP)時,只需要持證方和驗證方進行互動,不應該在此時引入發證方。所以我們可以認為:

1.生成VC和生成VP是兩個獨立的事件,生成VP時不應該有發證方參與。

2.驗證方信任的是發證方,而不是持證方,所以持證方只是簡單的證明自己大於18歲,但是這個證明沒有發證方的背書,是不可信的。

基於以上條件,本文提出了一種身份證明的零知識證明方法,該方法將VC和VP獨立,生成VP時發證方不需要參與,而且VP中給出的證明具有零知識性,而且有發證方背書。

0x0. Issuer:根據取樣粒度與取樣範圍進行資料的斷言構建

發證方Issuer要對某個屬性做出證明,首先需要在該資料所在的作用域進行取樣,取樣包括取樣的粒度和範圍。以使用者的生日屬性為例,其作用範圍雖然可以是歷史上任意一天,但是我們考慮實際情況,可以將取樣範圍定義在1900-1-1到2020-1-1,然後是取樣粒度的問題,如果我們以年為粒度,也就是說我們只關心使用者出生的那個年份來確定年齡,如果我們以年月粒度,那麼就可以根據具體出生在幾月來確定年齡,最細的粒度就是到天。這裡簡單起見,我們就以年為採用粒度,這樣我們就建立了一個從1900到2020的陣列:

[1900,1901,1902,…,2019,2020]

然後我們還可以增加下區間”<1900”和上區間”>2020”兩個元素,從而覆蓋年份的所有取值範圍。

我們有了年份取樣的陣列,接下來是根據使用者的實際情況,為每個元素增加斷言,這裡我以當年使用者是否已經出生為斷言,所以我們的陣列變為,以小明1985年出生為例:

[<1900未出生, 1900未出生,1901未出生,1902未出生,…1984未出生,1985已出生,1986已出生,…,2019已出生,2020已出生,>2020已出生]

有了這麼一個斷言陣列,我們可以簡化斷言為更簡潔一些的形式:

Min=1900,Max=2020,Step=1,OtherRange=Both,Assert=[0,0,0,0,……,0,1,1,1,…,1,1],Format=“{0}:{1}"

這裡定義了陣列的範圍,取樣粒度,邊界處理,以及斷言的結果。Format是定義了斷言字串的格式。

0x1. Issuer:斷言默克爾樹的構建

現在我們已經構建好了斷言陣列,接下來就只需要將斷言陣列作為默克爾樹的葉子節點,並採用上一篇文章中說的加鹽方法,防止雜湊碰撞,從而構建一個加鹽斷言默克爾樹。

0x2. Issuer:默克爾根簽名,VC生成

這棵樹構建好了,得到了默克爾根,發證方接下來使用自己的私鑰對這個默克爾根進行簽名,並將簽名、默克爾樹生成辦法、隨機種子等資訊放到VC中,以供使用者認證。以下是公安部門針對使用者生成的身份證VC,並在其中包含了出生年份的斷言。

{
  // VC內容所遵循的JSON-LD標準
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://studyzyexamples.com/identity/v1"
  ],
  // 本VC的唯一標識,也就是證書ID
  "id": "vc511112200001010015",
  // VC內容的格式
  "type": ["VerifiableCredential", "Identity"],
  // 本VC的發行人
  "issuer": "did:公安部門ID",
  // 本VC的發行時間
  "issuanceDate": "2010-07-01T19:73:24Z",
  // VC宣告的具體內容
  "credentialSubject": {
    // 被宣告的人的DID
    "id": "did:cid:511112200001010015",
    // 宣告內容:姓名、性別、生日、民族、住址等
    "name":"小明",
    "gender":"男",
    "birthdate":"2000-01-01",
    "nation":"漢",
    "address":"A省B市C區D街道xxx號",
    //接下來是種子數、默克爾根、公安的簽名
    "seed":"23523865082340324",
    "merkleRoot":"ea59a369466be42d1a4783f09ae0721a5a157d6dba9c4b053d407b5a4b9af145",
    "rootSignature":"3066022051757c2de7032a0c887c3fcef02ca3812fede7ca748254771b9513d8e266",
    "signer":"did:公安部門ID#keys-1"
    //接下來是對出生年份斷言默克爾樹的構建和簽名
    "birthYearAssert":{
      "min":1900,
      "max":2020,
      "step":1,
      "otherRange":"Both",
      "assert":[0,0,0,0,0,0,......0,1,1,1,1,1.......1],
      "format":"{0}:{1}",
      "seed":"9013492332268116070",
      "merkleRoot":"806de4a868682738bb328a4801c09d88a93ce1301e3dbff08f5b0881be01fddb",
      "rootSignature":"3077022051757c2de7032a0c887c3fcef02ca3812fede7ca748254771b9513d8e255",
      "signer":"did:公安部門ID#keys-1"
    }
  },
  // 對本VC的證明
  "proof": {
    "creator": "did:公安部門ID#keys-1",
    "type": "Secp256k1",
    "signatureValue": "3044022051757c2de7032a0c887c3fcef02ca3812fede7ca748254771b9513d8e2bb"
  }
}

0x3. Verifier:生成斷言請求

現在賣菸酒的商家Verifier要求顧客證明自己大於18歲,換句話說,以當前2020年來說,就是要證明18年前 2002年已經出生。所以商家需要顧客證明的斷言是:

2002:1

商家將這個斷言生成斷言請求,併發送給顧客的數字身份APP中,比如商家可以把斷言請求生成二維碼,讓所有顧客都掃碼,生成VP。

0x4. Holder:生成零知識證明VP

現在顧客Holder已經得知了Verifier的斷言請求,於是根據斷言請求,找到該斷言在斷言默克爾樹中的位置,並形成:斷言內容、斷言位置、默克爾驗證路徑、Salt、默克爾根、公安機關DID和簽名的驗證判斷所需的全部元素,而這一切都是基於之前公安機關頒發給使用者的VC生成的,並不需要聯網,更不需要公安機關在本次驗證過程中的參與。

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://studyzyexamples.com/identity/v1"
  ],
  "type": "VerifiablePresentation",
  // 本VP包含的VC的內容
  "verifiableCredential": [{
    "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://studyzyexamples.com/identity/v1"
  ],
  "id": "vc511112200001010015",
  "type": ["VerifiableCredential", "Identity"],
  "issuer": "did:公安部門ID",
  "issuanceDate": "2010-07-01T19:73:24Z",
  "credentialSubject": {
    "id": "did:cid:511112200001010015",
    //以下是斷言的內容,意思是2002年已經出生
    "assert":"2002:1",
    //以下是驗證披露欄位有效性的資料
    //資料在默克爾樹中的索引
    "dataIndex":103,
    //本資料加鹽的值
    "salt":"5ff63326ca055a6dca267985c8ca03732b907b4197eb16a60c723bd567883650",
    //默克爾驗證路徑
    "merklesibling":"b6234c998b586914c76ccabd35c97be779074ea2ea7d03e81b25dc80547ee799 02a9c8c6a60eaab5765f526b4a1792f37e3b399cdab2a9492653dac432f2ccd3 5109396c42763abe94aa92acf11e9b2a034cdd1a63a4493ae0cce9fffb632f81 5bd2b306f156bdab383e352dc31c62cb2e18ada75957398fdd0c369ef3850d97 73af5df5f4c36d1377bd976a4b7be2b88878befd21b75962863d22299d3023a6 537ad98582a606ef422881bf766550bf22dbd8f06eb91d6ea59a57643f02c22a 4b590775b2fddd7fa7b6fe428e5ff83256dfbf6a15fa2c30b643a41785c245a0",
    //默克爾根雜湊
    "merkleRoot":"806de4a868682738bb328a4801c09d88a93ce1301e3dbff08f5b0881be01fddb",
    //公安機關對默克爾根的簽名
    "rootSignature":"3077022051757c2de7032a0c887c3fcef02ca3812fede7ca748254771b9513d8e255",
    //用的公安機關哪個Key進行的簽名
    "signer":"did:公安部門ID#keys-1"
  },
  
  }],
  // Holder小明對本VP的簽名信息
  "proof": {
    "type": "Secp256k1",
    "created": "2010-07-02T21:19:10Z",
    "proofPurpose": "authentication",
    "verificationMethod": "did:cid:511112200001010015#keys-1",
    // challenge和domain是為了防止重放攻擊而設計的
    "challenge": "1f44d55f-f161-4938-a659-f8026467f126",
    "domain": "4jt78h47fh47",
    "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5
      XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqs
      LfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh
      4vGHSrQyHUGlcTwLtjPAnKb78"
  }
}

0x5. Verifier:驗證VP

商家在收到顧客生成的VP後,可以直接通過簽名驗證默克爾根是否是公安機關簽名的,然後通過默克爾驗證來證明”2002:1“這個斷言是正確的。具體驗證的細節我在上一篇文章中已經講過,這裡也是一樣的過程,就不再重複了。

0x6. 小結

本文提出的零知識證明方法基於範圍資料構建和加鹽默克爾樹驗證,對於無法定範圍的場景,可能並不是很適用。比如對於姓名欄位,我們可以拆分成姓和名,姓欄位是有一個範圍的,大概率都在百家姓裡面 ,而名欄位就太廣泛了,基本上所有的漢字都可以作為名,而且名有多個字,排列組合的情況是天文數字,所以我們可以對姓欄位建立零知識證明,而對於名欄位無法使用。除了生日外,民族是一個可數的範圍(56個民族+其他),住址的省、市、縣都是一個可數的範圍,我們都可以使用本文中的零知識證明方法。

本零知識證明還有個侷限性,就是隻能證明範圍資料構建時的取值,而無法證明中間的取值。比如我們對家庭年收入欄位建立範圍取值,每10W一個值,所以默克爾樹葉子節點就是:[0,10W,20W,30W …...]

比如小明的實際家庭年收入是38W,我們可以基於這個取值來證明家庭年收入大於30W,但是我們無法證明家庭年收入大於35W。如果想要證明,我們就需要將劃分範圍的粒度變細,比如變成每一個範圍是1W,這樣構建了一個新的長了很多的葉子節點列表[0,1W,2W,3W…….]如果我們將範圍上限定在1000W,那麼就需要1000個葉子節點,但是我們也只能證明年收入大於35W,無法證明更細的粒度,比如年收入是否達到38.5W?

基於前面幾篇文章介紹的數字身份DID技術的基礎知識,下一篇我們將介紹DID的應用場景。