文章學習:TPRE:分散式門限代理重加密

PamShao發表於2023-04-29

學習文章:TPRE:分散式門限代理重加密

前言

成方金科新技術實驗室與隱語團隊合作,構建了“基於國密的分散式門限代理重加密演算法TPRE”,為使用者提供了一種安全、高效、自主可控的資料共享和授權管理方案。在資料隱私保護資料安全共享方面具有廣泛的應用前景。

⚠️:該演算法由成方金科密碼學研究員張曙光(知乎:六三)基於隱語的密碼庫yacl實現,其提供了易於開發的密碼工具介面,TPRE演算法目前已經貢獻至yacl中。https://github.com/secretflow/yacl/tree/main/yacl/crypto/primitives/tpre

TPRE演算法具有以下優勢:

  • 資料金鑰隱含在TPRE的密文中,無需對資料金鑰進行管理,降低金鑰管理的複雜度;
  • 實現了資料的動態授權和訪問控制
  • 適應於區塊鏈中的分散式計算和資料安全共享場景,可以適配於未來價值網路Web 3.0;
  • 使用國密演算法SM2 、SM3和SM4對國際密碼演算法進行替換,實現自主可控。
  • 替代了UMBRAL重加密方案

基礎知識

代理重加密

代理重加密是一種公鑰密碼方案,它允許代理將一個公鑰加密成的密文轉換到另一個公鑰加密成的密文,而代理者不能瞭解關於原始訊息的任何資訊,要做到這一點,代理必須擁有一個重加密金鑰

一個代理重加密演算法通常包含三種角色,即資料擁有者Alice、資料接收者Bob和代理計算Proxy

假設,資料\(m\)已經被Alice使用自己公鑰加密為密文\(c\),並儲存在Proxy,具體演算法具體步驟如下:

  • Alice作為資料擁有者,想要將資料授權給Bob使用,則為Proxy生成重加密金鑰\(rk\)

  • Proxy在接收到\(rk\)後,對密文\(c\)重加密,得到新密文\(c^‘\),並將其傳送至Bob。

  • Bob使用自己的私鑰\(c^‘\)對解密,即可得到明文資料\(m\)

這裡想起來了一個基於NTRU的代理重加密方案:NTRUReEncrypt: An Efficient Proxy Re-Encryption Scheme Based on NTRU,下面具體介紹一下方案:

代理重加密方案:NTRUReEncrypt

委託人為:A,被委託人為:B,代理者:proxy

  • 金鑰生成:A的金鑰\((pk_A,sk_A)\)和B的金鑰\((pk_B,sk_B)\),以A的金鑰生成為例:
    • A隨機式生成一對多項式\((f_A,g_A)\in R^2_{NTUR}\),係數為\((-1,0,1)\),其中\(f_A \equiv 1 mod p\)
    • 私鑰\(sk_A=f_A\),公鑰為\(pk_A=h_A=p*g_Af_A^{-1} mod q\)
  • 重加密金鑰生成\((sk_A,sk_B)\)
    • 輸入A和B的私鑰:\(sk_A=f_A\)\(sk_B=f_B\)
    • A、B和代理者proxy之間透過一個簡單的三方協議計算出重加密金鑰\(rk_{A\to B}\)
      • A選擇一個隨機數\(r\in R_{NTRU/q}\)
      • A計算\(r*f_A mod q\)併傳送給B,A傳送\(r\)給代理者proxy;
      • B計算\(r*f_A f_B^{-1} mod q\)給proxy;
      • 這樣就能計算出\(rk_{A \to B}=f_A*f_B^{-1} mod q\)
  • 加密\((pk_A,M)\)
    • 輸入公鑰\(pk_A\)和訊息\(M\in R_{NTRU/q}\)
    • 隨機選擇一個多項式\(s\in R_{NTRU}\)
    • 輸出密文\(C_A=h_As+M\)
  • 重加密\((rk_{A\to B},C_A)\)
    • 輸入重加密金鑰\(rk_{A \to B}\)和密文\(C_A\)
    • 隨機選擇一個多項式\(e\in R_{NTRU}\)
    • 輸出新的密文\(C_B=C_A*rk_{A \to B}+pe\)
  • 解密\((sk_A,C_A)\)
    • 輸入私鑰\(sk_A=f_A\)和密文\(C_A\)
    • 計算\(C_A^‘=(C_A*f_A) mod q\)
    • 輸出\(M=(C_A^‘ mod p)\)
  • 正確性證明:
    • A加密訊息\(M\),得到密文\(C_A\),並生成重加密金鑰\(rk_{A\to B}\)
    • 代理者proxy對密文\(C_A\)進行重加密,得到\(C_B=C_A*rk_{A\to B}+pe=(h_As+M)rk_{A\to B}+pe=pg_Af_B^{-1}s+pe + f_Af_B^{-1}M\)
    • B對\(C_B\)進行解密,計算\((C_B*f_B)mod q=((pg_Af_B^{-1}s+pe + f_Af_B^{-1}M)\cdot f_B)mod q=(pg_As + pe f_B + f_AM)mod q=M\)

分散式門限代理重加密

代理重加密適合在雲端計算場景中使用,即代理節點為計算效能較強的單節點,這與現有隱私計算體系架構不符,因為現在隱私計算架構通常是分散式架構。因此,需要對傳統代理重加密方案進行改造,使之能夠適應分散式計算環境

分散式代理重加密是指將傳統代理重加密中的單Proxy節點,拆分為多個Proxy節點。因而在對資料重加密時,需要多個Proxy節點參與合作計算

考慮到選取參與計算的Proxy節點的靈活性,需要將分散式代理重加密重新設計為基於門限的分散式代理重加密

Shamir Secret Sharing

Shamir Secret Sharing是一種加密技術,可以將秘密資訊分散成多個部分,並分配給不同的人,只有所有部分被彙集在一起才能重構出原來的秘密資訊。它是由以色列密碼學家Adi Shamir在1979年發明的,被廣泛應用於保護機密資訊,例如在密碼學、數字簽名、多方計算等領域。其基本思想是透過多項式插值來實現資訊的分散和重構,具有高度的安全性和可靠性。

Shamir密秘分享:假設有一個秘密資訊,例如一個密碼或者一個私鑰,需要將這個秘密資訊分配給兩個人,可以使用Shamir Secret Sharing演算法來實現。

  • 首先,選擇一個大的質數\(p\),並選擇一個閾值\(k\),使得只有\(k\)個或更多的部分才能恢復原始秘密資訊,這裡\(k=2\)
  • 然後,隨機生成一個多項式\(f(x)\),其中\(f(0)\)等於秘密資訊,其餘係數都是隨機數。每個人得到一個點\((x,f(x))\),其中\(x\)是一個隨機數,並且只有兩個點都被收集到了,才能恢復原始秘密資訊。如果只有一個點,那麼無法恢復原始秘密資訊

例如:假設秘密資訊是密碼“5”,需要將它分配給兩個人。

  • 選擇一個質數\(p=11\),並選擇閾值\(k=2\)
  • 隨機生成一個多項式\(f(x)=4x+5\),其中\(f(0)=5\),將多項式的係數分別分配給兩個人,例如第一個人得到\((1,9)\),第二個人得到\((2,2)\)。如果兩個人都收集到了這兩個點,那麼可以使用拉格朗日插值法恢復原始的多項式\(f(x)\),進而得到秘密資訊“5”。

具體參考:https://www.cnblogs.com/pam-sh/p/17179097.html#shamir秘密分享

image-20230421223457473

橢圓曲線

下圖是我國商用密碼SM2選定的素數域橢圓曲線"sm2p256v1"的引數選擇:

圖片

更多可參考SM2國標:

image-20230421224017136

密碼雜湊函式

使用的雜湊演算法構造如下:

\(h_x= (1 + Bignum(SM3(x)||SM3(SM3(x))) )mod n-1\)

其中\(n\)是橢圓曲線的階,\(x\)是函式的輸入

SM3套wa?

混合加密體制

由於公鑰密碼是執行在代數計算上的密碼演算法,其計算效率遠低於對稱密碼,因此,在待加密資料量較大時,使用公鑰密碼直接對資料加密不是一個良好選擇。在該場景中,可使用混合加密體制KEM/DEM

  • KEM是指金鑰封裝機制(Key-Encapsulation Mechanism)
  • DEM是指資料封裝機制(Data-Encapsulation Mechanism),可以看做是對稱加密方案。

這兩個機制聯合使用,可以提供資料的加解密效率,在密文需要傳輸時,也可降低通訊開銷。具體而言,DEM機制是用作保護原始資料,即使用對稱加密演算法,對原始資料進行加密保護KEM是用作保護加密原始資料時所使用的對稱金鑰,使用公鑰加密演算法對對稱金鑰進行加密保護

關於更多金鑰封裝可參考:金鑰封裝和公鑰加密的聯絡和區別?

UMBRAL代理重加密演算法

方案:https://github.com/nucypher/umbral-doc

UMBRAL是一個代理重加密方案(A THRESHOLD PROXY RE-ENCRYPTION SCHEME),根據一個金鑰封裝演算法改進而來。Alice作為資料產生方,透過\(N\)個代理階段的重加密機制可以將密文的解密許可權委託給Bob,具體來說,Bob得到\((t,N)\)個節點重加密資訊才能解密(恢復)出該明文。

代理重加密的使用場景:公共網路中任意數量的參與者實現私人資料共享,但不向中間實體(代理者)洩漏金鑰資訊。

UMBRAL是閾值代理重加密演算法,是非互動式的、單向的,且在重加密後可以驗證的,在閾值方面採用Shamir 秘密分享技術。

  • 單向:重加密金鑰是單向的
  • 非互動式:在重加密金鑰生成中不使用被委託人Bob的私鑰,例如“NTRUReEncrypt”是互動式的。

演算法介紹

一個代理重加密【Proxy Re-Encryption,PRE】演算法的簡易框架如下:

image-20230426103956682

演算法中使用者角色主要有:

  • Delegator【委託人】:Alice,加密明文訊息,並生成一個重加密金鑰,傳送給代理者
  • Delegatee【受委託人】:Bob,受Alice的委託用自己的私鑰恢復明文,其中收到的密文是代理者重新加密後的
  • Proxy【代理者】:將Alice傳送來的密文利用重加密金鑰重新加密,傳送給Bob

代理重加密(PRE)由KEM(金鑰封裝)和DEM(分組加密)組成,其中DEM不涉及重加密,所以下圖只討論KEM部分。

image-20230426153901643

  • Encapsulation:用Alice的公鑰進行封裝,得到金鑰\(K\)和封裝的密文\(capsule\)(膠囊)
  • Decapsulate:用Alice的私鑰進行解封裝,獲得金鑰\(K\)
  • Re-Encapsulation:Proxy使用重加密金鑰片段對封裝的密文進行再次封裝,得到重封裝的密文片段\(cFrag\)
  • DecapsulateFrags:用Bob的私鑰對重封裝的密文片段進行接封裝,得到金鑰\(K\)

下面對KEM的演算法功能模組分別介紹:

1、金鑰生成演算法

  • 金鑰生成\(KeyGen()\)

Alice得到一對公私鑰對\((pk_A,sk_A)\)

  • 重加密金鑰生成\(ReKeyGen(sk_A,pk_B,N,t)\)

輸入:\(sk_A=a\)\(pk_B=g^b\)、片段\(N\)、閾值\(t\)

輸出:\(N\)個重加密金鑰片段\(kFrag\)

2、封裝和解封裝演算法

  • 封裝\(Encapsulate(pk_A)\)

輸入:\(pk_A\)

輸出:對稱加密金鑰\(K\)和封裝結果\(capsule\)

  • 解封裝\(Decapsulate(sk_A,capsule)\)

輸入:\(sk_A\)\(capsule\)

輸出:對稱加密金鑰\(K\)

3、重封裝和解封裝片段演算法

  • 重新封裝\(ReEncapsulation(kFrag,capsule)\)

輸入:重加密金鑰片段\(kFrag\)和封裝結果\(capsule\)

輸出:重新封裝的片段(部分)\(cFrag\)

  • 解封裝片段\(DecapsulateFrags(sk_B,\left \{cFage_i \right \}_{i=1}^{t},capsule)\)

輸入:Bob的私鑰\(sk_B=b\)\(t\)個重封裝片段\(cFrag\)

輸出:對稱加密金鑰\(K\)

具體演算法

下面詳細介紹KDM演算法流程:

1、引數設定

  • \(Setup(sec)\)
    • 根據安全引數\(sec\),確定一個階數為素數\(q\)的迴圈群\(G\)\(g,U\in G\)是群的生成元;
    • 雜湊函式\(H_2:G^2\to Z_q\)\(H_3:G^3\to Z_q\)\(H_4:G^3*Z_q\to Z_q\)看作是隨機預言機;
    • 金鑰派生函式\(KDF:G\to \left \{0,1 \right \}^l\)也看作是隨機預言機,其中\(l\)依賴於安全引數\(sec\)
    • 最後得到一個公共引數集:\(\text { params }=\left(\mathbb{G}, g, U, H_{2}, H_{3}, H_{4}, \mathrm{KDF}\right)\)

2、金鑰生成演算法

  • \(KeyGen()\)

    • 選擇一個隨機數\(a,b\in Z_q\),計算\(g^a,g^b\)
    • Alice和Bob的公私鑰分別為:\((pk_A,sk_A)=(g^a,a)\)\((pk_B,sk_B)=(g^b,b)\)
  • \(ReKeyGen(sk_A,pk_B,N,t)\)

    • 選擇一個隨機數\(x_A\in Z_q\),計算\(X_A=g^{x_A}\)
    • 計算\(d=H_{3}\left(X_{A}, p k_{B},\left(p k_{B}\right)^{x_{A}}\right)\),其中\(d\)是Bob的金鑰和臨時金鑰對\((x_A,X_A)\)進行非互動DH的結果,使用該共享秘密資訊使得方案的重加密金鑰的生成為非互動式的;
    • 選擇\(t-1\)個隨機值\(f_i\in Z_q,i\in [1,t-1]\),計算\(f_0=a*d^{-1} mod q\)
    • 構建級數為\(t-1\)的多項式\(f(x)\in Z_q[x]\),例如\(f(x)=f_0+f_1x+...+f_{t-1}x^{t-1}\)
    • 計算\(D=H_6(pk_A,pk_B,pk_B^a)\)
    • 設定\(KF=0\),重複執行\(N\)次:
      • 選擇隨機值\(y,id\in Z_q\)\(id\)表示代理節點的編號;
      • 計算\(s_x=H_5(id,D)\)\(Y=g^y\)
      • 計算\(rk=f(s_x)\)
      • 計算\(U_1=U^{rk}\)
      • 計算\(z_1=H_4(Y,id,pk_A,pk_B,U_1,X_A)\)\(z_2=y-a\cdot z_1\)
      • 定義一個重加密金鑰片段為\(kFrag=(id,rk,X_A,U_1,z_1,z_2)\)
      • \(KF=KF\cup\{kFrag\}\)
    • 最後輸出重加密金鑰\(KF\)

3、封裝和解封裝演算法

  • \(Encapsulate(pk_A)\)

    • 選擇隨機值\(r,u\in Z_q\),計算\(E=g^r,V=g^u\),然後計算\(s=u+r*H_2(E,V)\)
    • 計算\(K=\textsf{KDF}((pk_A)^{r+u})\)
    • \(capsule=(E,V,s)\),叫做“膠囊”,可以反推出(解封裝)\(K\)
    • 最終輸出\((K,capsule)\)
  • \(CheckCapsule(capsule)\)

    • 判斷\(g^s\overset{?}{=}V\cdot E^{H_2(E,V)}\)是否成立;
  • \(Decapsulate(sk_A,capsule)\)

    • 呼叫\(CheckCapsule(capsule)\)檢查\(capsule\)的有效性;
    • 計算\(K=KDF((E,V)^a)\),恢復出\(K\)

4、重封裝和解封裝片段演算法

  • \(ReEncapsulation(kFrag,capsule)\)

    • 呼叫\(CheckCapsule(capsule)\)檢查\(capsule\)的有效性;
    • 計算\(E_1=E^{rk},V_1=V^{rk}\)
    • 輸出\(cFrag=(E_1,V_1,id,X_A)\)
  • \(DecapsulateFrags(sk_B,\left \{cFrag_i \right \}_{i=1}^{t},capsule)\)

    • 其中\(cFrag_i=(E_1,V_1,id,X_A)\);
    • 計算\(D=H_6(pk_A,pk_B,pk_A^b)\)
    • 對於\(S=\left \{s_{x,i} \right \}_{i=1}^t\),計算\(s_{x,i}=H_5(id_i,D)\)
    • 計算\(\lambda_{i,S}=\prod\limits_{j=1,j\neq i}^t\dfrac{s_{x,j}}{s_{x,j}-s_{x,i}}\)\(E^{\prime}=\prod\limits_{i=1}^t(E_{1,i})^{\lambda_{i,S}}\)\(V^{\prime}=\prod\limits_{i=1}^t(V_{1,i})^{\lambda_{i,S}}\)
    • 計算\(d=H_3(X_A,pk_B,X_A^b)\),其中使用\(kFrag\)進行重封裝的\(KF\)\(X_A\)是相同的;
    • 輸出對稱金鑰\(K=\textsf{KDF}((E'\cdot V')^d)\)

結合上述KEM演算法,加入\(DEM\)後,封裝和解封裝演算法變為了加密和解密演算法,另外需後續驗證,所以DEM具體為認證加密演算法(AEAD),下面給出完整的KEM/DEM演算法

  • \(Encrypt(pk_A,M)\)

    • 計算\((K,capsule)=Encapsulate(pk_A)\)
    • 加密:\(encData=AEAD(K,M)\),其中\(M\)是明文資訊,使用\(K\)為金鑰進行對稱加密;
    • 輸出\(C=(capsule,encData)\)
  • \(Decrypt(sk_A,C)\)

    • 計算\(K=Decapsulate(sk_A,cap sule)\)
    • 解密:\(M=IAEAM(K,encData)\)
    • 輸出\(M\);
  • \(ReEncrypt(kFrag,C)\)

    • 計算\(cFrag=ReEncapsulation(kFrag,capsule)\)
    • 輸出\(C'=(cFrag,encData)\)
  • \(DecryptFrags(sk_B,\{C_i'\}_{i=1}^t)\)

    • 其中\(C_i'=(cFrag_i,encData)\)
    • 計算\(K=DecapsulateFrags(sk_B,\left \{cFrag_i \right \}_{i=1}^{t},capsule)\)
    • 計算\(M=IAEAM(K,encData)\)

正確性和安全性參考原文。

程式

源程式:https://github.com/nucypher/pyumbral

pyUmbral是對門限代理重加密方案Umbral的實現,基於python,依賴opensslcryptography庫。

在該程式中,Alice(資料擁有者)透過一個重加密的代理節點,將解密權利授權給Bob。具體說,當有門限個代理節點參與重加密時,Bob能聚合這些重加密的密文,然後使用自己的私鑰解密恢復出原始訊息。

image-20230427224156412

pyUmbral是nucypher背後的加密引擎,nucypher是一個代理重新加密網路,用於在去中心化系統中增強隱私。

安裝

pip3 install umbral

使用

下面以一個例子證明方案功能的正確性(docs/examples/umbral_simple_api.py):

1、金鑰生成

  • Alice生成:

    • 加密公私鑰對(alices_public_key,alices_secret_key)
    • 簽名公私鑰對(alices_verifying_key,alices_signing_key)
  • Bob生成:

    • 加密公私鑰對(bobs_public_key,bobs_secret_key)
import random
from umbral import (
    SecretKey, Signer, CapsuleFrag,
    encrypt, generate_kfrags, reencrypt, decrypt_original, decrypt_reencrypted)

# Generate an Umbral key pair
# ---------------------------
# First, Let's generate two asymmetric key pairs for Alice:
# A delegating key pair and a Signing key pair.

alices_secret_key = SecretKey.random()                  #sk_A
alices_public_key = alices_secret_key.public_key()      #pk_A

alices_signing_key = SecretKey.random()                 #簽名私鑰
alices_verifying_key = alices_signing_key.public_key()  #簽名公鑰
alices_signer = Signer(alices_signing_key)
  
bobs_secret_key = SecretKey.random()            #sk_B
bobs_public_key = bobs_secret_key.public_key()  #pk_B

2、加解密

  • Alice加解密測試:
    • 用公鑰加密明文訊息(Proxy Re-encryption is cool!)
    • 密文結構(capsule,ciphertext),其中ciphertext表示加密的密文,capsule表示膠(e,v,s)
    • 用私鑰解密
  • Bob測試:
    • 用私鑰對密文解密,解密錯誤!
# Encrypt some data for Alice
# ---------------------------
# Now let's encrypt data with Alice's public key.
# Invocation of `pre.encrypt` returns both the `ciphertext`,
# and a `capsule`. Anyone with Alice's public key can perform
# this operation.

plaintext = b'Proxy Re-encryption is cool!'
capsule, ciphertext = encrypt(alices_public_key, plaintext)
print("ciphertext:",ciphertext)

# Decrypt data for Alice
# ----------------------
# Since data was encrypted with Alice's public key,
# Alice can open the capsule and decrypt the ciphertext with her private key.

cleartext = decrypt_original(alices_secret_key, capsule, ciphertext)
print("cleartext:",cleartext)
  
# Bob receives a capsule through a side channel (s3, ipfs, Google cloud, etc)
bob_capsule = capsule

# Attempt Bob's decryption (fail)
try:
    fail_decrypted_data = decrypt_original(bobs_secret_key, bob_capsule, ciphertext)
except ValueError:
    print("Decryption failed! Bob doesn't has access granted yet.")

3、重加密

  • Alice生成重加密金鑰:
    • 根據Alice的私鑰、Bob的公鑰、簽名資訊
    • 產生20個重加密金鑰
  • 代理節點:
    • 從20箇中選出10個重加密金鑰(kfrags)
    • 使用kfrags對capsule重加密得到重加密後的密文(cfrags)
# Alice grants access to Bob by generating kfrags
# -----------------------------------------------
# When Alice wants to grant Bob access to open her encrypted messages,
# she creates *threshold split re-encryption keys*, or *"kfrags"*,
# which are next sent to N proxies or *Ursulas*.
# She uses her private key, and Bob's public key, and she sets a minimum
# threshold of 10, for 20 total shares

kfrags = generate_kfrags(delegating_sk=alices_secret_key,
                         receiving_pk=bobs_public_key,
                         signer=alices_signer,
                         threshold=10,
                         shares=20)                     #Alice產生20個重加密金鑰片段,給代理節點

# Ursulas perform re-encryption
# ------------------------------
# Bob asks several Ursulas to re-encrypt the capsule so he can open it.
# Each Ursula performs re-encryption on the capsule using the `kfrag`
# provided by Alice, obtaining this way a "capsule fragment", or `cfrag`.
# Let's mock a network or transport layer by sampling `threshold` random `kfrags`,
# one for each required Ursula.

kfrags = random.sample(kfrags,  # All kfrags from above
                       10)      # M - Threshold         #從20個重加密金鑰片段中,隨機選出10個用作重加密

# Bob collects the resulting `cfrags` from several Ursulas.
# Bob must gather at least `threshold` `cfrags` in order to open the capsule.

cfrags = list()  # Bob's cfrag collection
for kfrag in kfrags:
    cfrag = reencrypt(kfrags=capsule, kfrag=kfrag)
    cfrags.append(cfrag)  # Bob collects a cfrag

assert len(cfrags) == 10

4、解密

  • Bob先驗證capsule的合法性
    • 使用Alice的公鑰、Bob的公鑰和Alice的簽名公鑰
  • Bob解密:
    • 使用Bob的私鑰、Alice的公鑰、重加密後的密文(cfrage)、capsule和ciphertext
# Bob checks the capsule fragments
# --------------------------------
# If Bob received the capsule fragments in serialized form,
# he can verify that they are valid and really originate from Alice,
# using Alice's public keys.

suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]

cfrags = [cfrag.verify(capsule,
                       verifying_pk=alices_verifying_key,
                       delegating_pk=alices_public_key,
                       receiving_pk=bobs_public_key,
                       )
          for cfrag in suspicious_cfrags]               #驗證重加密後的密文

# Bob opens the capsule
# ------------------------------------
# Finally, Bob decrypts the re-encrypted ciphertext using his key.

bob_cleartext = decrypt_reencrypted(receiving_sk=bobs_secret_key,
                                    delegating_pk=alices_public_key,
                                    capsule=bob_capsule,
                                    verified_cfrags=cfrags,
                                    ciphertext=ciphertext)  #Bob使用sk_B解密得到明文
print("bob_cleartext:",bob_cleartext)
assert bob_cleartext == plaintext

TPRELib演算法

介紹

TPRELib共包含6個演算法,分別為金鑰對生成演算法【GenerateTpreKeyPair】、重加密金鑰生成【GenerateReKey】、加密【Encrypt】、解密演算法【Decrypt】、重加密演算法【ReEncrypt】、解密重加密密文演算法【DecryptFrage】:

假設資料持有方為A,接收方為B,代理者(節點)為proxy

  • \(\mathop{\mathrm{GenerateTpreKeyPair}}(\lambda)\to(p k_{A},s k_{A})\):輸入安全引數\(\lambda\),A和B各自生成公私鑰對\((pk_A,sk_A)\)\((pk_B,sk_B)\)
  • \(\text{}\mathrm{GenerateReKey}(sk_A,pk_B,N,t)\to(\{rk_i\},i\in[1,N])\):輸入A的私鑰\(sk_A\),B的公鑰\(pk_B\),所有代理節點數量\(N\)和門限\(t\),輸出重加密金鑰集合\(rk_i(i\in[1,N])\)。這裡的\(i\)是指代理節點的ID;
  • \(\text{Encrypt}(pk_A,m)\to c\):輸入A的公鑰\(pk_A\)和明文\(m\),輸出密文\(c\)
    • 這裡並不是直接使用\(pk_A\)加密明文,因為會導致效能過低問題,在底層加密時,使用的是對稱加密演算法,在本庫中對稱加密演算法由SM4實現,其對稱金鑰是在加密過程中隨機產生,對稱加密金鑰由公鑰加密保護【明文使用SM4加密,\(pk_A\)加密對稱加密的金鑰】
    • 生成對稱金鑰時,需要用到密碼雜湊函式構建的金鑰派生函式KDF(Key derivation function),本庫使用SM3代替SHA-2等國際演算法,實現該KDF函式。【可以實現指定長度的雜湊字串】
  • \(\operatorname{Decrypt}(sk_A,c)\to m\):輸入A的私鑰\(sk_A\)和密文\(c\),輸出明文\(m\)
    • 這裡是和加密演算法是逆過程,即用\(sk_A\)解密得到對稱加密金鑰,再解密密文
  • \(\text{ReEncrypt}(rk_i,c)\to c'_i\):代理節點輸入重加密金鑰\(rk_i\)和密文\(c\),輸出新密文\(c_{i}^{\prime}\)
  • \(\text{DecryptFrage}(c_{i}^{\prime}(i\in[t,N]),sk_{B})\rightarrow m\):輸入的是門限個數的新密文集合\(c_{i}^{\prime}(i\in[t,N])\)和接收方的私鑰\(sk_{B}\),輸出明文\(m\)

下面是門限代理重加密演算法執行流程:

圖片

\(capsule\):包含對稱金鑰資訊

\(ct\):使用對稱加密的密文

具體演算法

引數設定

假設Alice為資料所有方,Bob為資料需求方,代理者(代理節點)為proxy

設定引數,為了簡單起見,我們將省略其餘函式中的公共引數。

  • \(Setup(sec)\):首先根據安全引數\(sec\)確定一個素數\(q\)的迴圈群\(G\) ,讓\(g,U\in G\)是生成元。
    • \(H_2:G^2\to Z_q\)\(H_3:G^3\to Z_q\)\(H_4:G^3\to Z_q\)表示密碼雜湊函式(假設把它們當做隨機預言機)
    • \(KDF:\to \left \{ 0,1 \right \} ^l\)是一個同樣作為隨機預言機的金鑰衍生函式,其中\(l\)是安全引數 。
    • 全域性公共引數由以下元組表示:\(\mathit{p a r a m s} m s=(\mathbb{G},g,U,H_{2},H_{3},H_{4},\mathrm{KDF})\)

金鑰對生成

  • \(KeyGen()\): Alice在\(Z_q\)中均勻地隨機選擇\(a\),即\(a\in Z_q\),計算\(g^a\)並輸出Alice的金鑰對\((pk_A,sk_A)=(g^a,a)\),Bob同樣輸出金鑰對\((pk_B,sk_B)=(g^b,b)\)

重加密金鑰生成

  • \(ReKeyGen(sk_A,pk_B,N,t)\):輸入\((pk_A,sk_A)\)\(pk_B\),代理節點數\(N\)和閾值\(t\),重加密金鑰生成演算法計算出Alice和Bob之間的\(N\)個代理節點的重加密金鑰:

    • 隨機抽樣\(x_A\in Z_q\)並計算\(X_A=g^{x_A}\)

    • 計算\(d=H_3(X_A,pk_B,(pk_B)^{x_A})=H_3(g^{x_a},g^b,g^{bx_a})\)

      • \(d\)是Bob的金鑰對與臨時金鑰對\((x_A,X_A)\)的非互動式Diffie-Hellman金鑰交換的結果,我們將使用這個共享的金鑰來使該方案的重加密金鑰生成變為非互動式
    • \(f_i\in Z_q\)中隨機抽取\(t-1\)個元素,其中\(i\in [1,t-1]\),並計算\(f_0=(a*d^{-1}) mod q\)

    • 【密秘分享】在\(r-1\)階的\(Z_q[x]\)中構造一個多項式\(f(x)\) ,使得\(f(x)=f_0+f_1x+f_2x^2+...+f_{t-1}x^{t-1}\)

    • 計算\(D=H_6(pk_A,pk_B,pk_B^a)=H_6(g^a,g^b,g^{ba})\)

    • 初始化集合\(KF=0\),重複\(N\)

      • 隨機選取\(y\in Z_q\),計算\(Y=g^y\)
      • 計算\(s_x=H_5(id,D)\),其中\(id\)是代理節點的ID
      • 計算\(rk=f(s_x)\),計算\(U_1=U^{rk}\)
      • 定義一個重加密金鑰片段\(kFrag=(id,rk,X_A,U_1)\)
      • \(KF=KF\cup kFrag\)
      • 最後輸出重加密金鑰片段\(KF\)
  • 封裝演算法\(Encapsulate(pk_A)\):輸入公鑰\(pk_A=g^a\)

    • 首先選擇隨機數\(r,u\in Z_q\) ,並計算\(E=g^r\)\(V=g^u\)

    • 計算\(s=u+r*H_2(E,V)\),計算\(K=KDF((pk_A)^{r+u})=KDF(g^{a(r+u)})\)該元組\((E,V,s)\)被稱為膠囊(capsule)

    • 最後輸出\((K,capsule)\),其中\(K\)為對稱金鑰

  • 檢查函式\(CheckCapsule(capsule)\):在輸入一個\(capsule=(E,V,s)\)時,透過檢查以下方程是否成立來檢查該膠囊的有效性:\(g^{s}\overset{?}{=}V\cdot E^{H_{2}(E,V)}\)

  • 解封演算法\(Decapsulate(sk_A,capsule)\):輸入金鑰\(sk_A=a\)和原始膠囊\(capsule=(E,V,s)\)

    • 首先用CheckCapsule檢查膠囊的有效性,如果檢查失敗,則輸出\(\bot\),否則計算\(K=KDF((E*V)^a)=KDF(g^{a(u+r)})\)
    • 最後輸出\(K\)

封裝演算法是根據公鑰\(pk_A\)生成一個對稱金鑰\(K\)和一個capsule

解封裝演算法可以根據capsule還原出對稱金鑰\(K\)

  • 重封裝演算法\(ReEncapsule(kFrag,capsule)\):輸入一個重加密的金鑰片段\(kFrag=(id,rk,X_A,U_1)\)和一個\(capsule=(E,V,s)\)
    • 首先用CheckCapsule檢查膠囊的有效性,如果檢查失敗,則輸出\(\bot\),否則計算\(E_1=E^{rk}\)\(V_1=V^{rk}\),並輸出膠囊片段\(cFrag=(E_1,V_1,id,X_A)\)
  • 解封裝金鑰演算法\(DecapsuleFrags(sk_B,pk_A,\left \{ cFrag_i \right \}_{i=1}^t )\):輸入金鑰\(sk_B=b\),原始公鑰\(pk_A=g^a\)\(t\)個膠囊片段,每個片段為\(c F r a g_{i}=\left(E_{1,i},V_{1,i},i d_{i},X_{A}\right)\)
    • 計算\(D=H_0(pk_A,pk_B,pk_A^b)\)
    • \(\begin{aligned}& \\&S=\left(\{s_{x,i}\}_{i=1}^{t}\right)\end{aligned}\),其中\(s_{x,i}=H_5(id_i,D)\),對於\(s_{x,i}\in S\),計算\(\lambda_{i,S}=\prod\limits_{j=1,j\neq i}^t\dfrac{s_{x,j}}{s_{x,j}-s_{x,i}}\)
    • 計算\(E'=\prod\limits_{i=1}^t(E_{1,i})^{\lambda_{i,s}}\)\(V'=\prod\limits_{i=1}^t(V_{1,i})^{\lambda_{i,S}}\)
    • 計算\(d=H_{3}\bigl(X_{A},p k_{B},X_{A}^{b}\bigr);\)
      • \(d\)\(B\)是金鑰對和臨時金鑰對\((x_A,X_A)\)之間非互動式Diffie-Hellman金鑰交換的結果。對於所有的\(cFrags\)來說,其值是相同的,這些\(cFrags\)是透過使用重加密金鑰片段集\(KF\)中的\(kFrag\)產生的。
    • 最後輸出對稱金鑰\(K=\mathrm{KDF}{\Big(}\left(E^{\prime}\cdot V^{\prime}\right)^{d}{\Big)}\)

資料加密

  • \(\operatorname{Encrypt}(p k_A,M)\):在輸入公鑰\(pk_A\)和資訊\(M\)時,加密演算法首先計算\((K,capsule)=\operatorname{Encapsulate}(pk_A)\),其中\(encData\)是用金鑰\(K\)\(M\)應用AEAD的結果,\(capsule\)是相關資料
  • 最後輸出密文\(C=(c a p s u l e,e n c D a t a)\)

資料解密

  • \(\operatorname{Decrypt}(sk_A,C)\):在輸入秘密金鑰\(sk_A\)和密文\(C\)時,解密演算法計算出金鑰\(K=\mathrm{Decapsulate}(s k_{A},c a p s u l e)\),並使用AEAD的解密函式對密碼文字\(encData\)進行解密,如果解密正確,則得到訊息\(M\),否則得到\(\bot\) ,最後輸出訊息\(M\)(如果解密無效則輸出\(\bot\))。

資料密文重加密

  • \(\mathrm{ReEncrypt}(kFrag,C)\): 在輸入一個重加密的金鑰片段\(kFrag\)和一個密文\(C\)時,重加密演算法對\(capsule\)應用\(Recapsulate\)以獲得\(cFrag\),並輸出重加密的密文\(C'=(cFrag,encData)\)

資料重加密密文解密

  • \(\operatorname{DecryptFrags}\Big(sk_B,\Big\{C_i^{prime}\Big\}_{i=1}^t\Big)\)。在輸入\(sk_B\)時,一組\(t\)個重加密的密文\(\begin{gathered} C_i' = (cFrag_i,encData) \\\end{gathered}\),片段解密演算法首先用\(\begin{aligned}& \\&\mathrm{DecapsulateFrags}\Big(s_{B},\{c F r a g_{i}\}_{i=1}^{t}\Big) \\&\end{aligned}\)\(cFrag\)進行解密,產生金鑰\(K\),並使用AEAD的解密函式對密碼文字\(encData\)進行解密,金鑰\(K\)和膠囊作為相關資料,如果解密正確,則得到訊息\(M\),否則得到\(\bot\)。最後它輸出訊息\(M\)(如果解密無效,則輸出\(\bot\)),其中對稱密文\(encData\)對於所有的\(C_i^‘\)都是相同的密文\(C\)的重加密

安全性

橢圓曲線安全性

本演算法可以選擇任意素數域橢圓曲線,例如Secp256k1和sm2p256v1,其中sm2p256v1是我國國產密碼學演算法SM2使用的橢圓曲線【2.4】

資料金鑰的安全性

  • 資料金鑰隨機性:資料金鑰的隨機性由隨機數\(pk_A,r,u\)決定,其中\(pk_A=g^a,sk_A=a\in Z_q,r,u\in Z_q\)
  • 資料金鑰的保密性:資料金鑰由公鑰密碼保護,公鑰密碼建立在橢圓曲線離散對數困難問題之上
  • 整個演算法中使用的密碼雜湊函式、金鑰派生函式來源於我國商用密碼SM3
  • 整個演算法中使用的對稱加密演算法來源於我國商用密碼SM4

程式

源程式:https://github.com/secretflow/yacl/tree/main/yacl/crypto/primitives/tpre

介紹

TPRE是umbral的替代品,它是用C++實現的,用SM2、SM3和SM4取代了ECC和AES等演算法。

可用於一下場景:

  • 資料安全共享

  • 資料安全授權

  • 分散式金鑰管理

模組功能:

  • Hash.h/hash.cc:使用的雜湊函式,封裝了SM3
  • kdf.h/kdf.cc:使用的KDF,使用了SM3
  • Keys.h/keys/cc:庫中提供金鑰生成和重加密金鑰生成
  • Capsule.h/capsule. cc:重加密的實現
  • tpre.h/tpre.cc:加密、解密、重加密等

安裝

由於tpre是放在隱語的yacl庫中,安裝編譯參考“yacl使用”

使用

1、金鑰生成

  • 生成曲線引數
  • Alice和Bob各自生成金鑰對
  • 使用Alice的金鑰對和Bob的公鑰生成重加密金鑰
std::unique_ptr<EcGroup> ecc_group = EcGroupFactory::Create("sm2");       //初始化引數,生成SM2對應的曲線引數

Keys keys;
std::pair<Keys::PublicKey, Keys::PrivateKey> key_pair_alice =                 
    keys.GenerateKeyPair(ecc_group);

// According to the official SM2 document
// The hexadecimal of generator is:
// Gx = 32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589
// 334C74C7
// Gy = BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5
// 2139F0A0

// When converting it to decimal, we have :
// "(2296314654723705055947953136255007457880
// 2567295341616970375194840604139615431,
// "85132369209828568825618990617112496413088
// 388631904505083283536607588877201568)";

std::string generator_str =
    "(2296314654723705055947953136255007457880256729534161697037519"
    "4840604139"
    "615431, "
    "85132369209828568825618990617112496413088388631904505083283536"
    "6075888772"
    "01568)";
EXPECT_EQ(ecc_group->GetAffinePoint(key_pair_alice.first.g).ToString(),
          generator_str);                                                 //檢測generator_str點是否在曲線上

std::pair<Keys::PublicKey, Keys::PrivateKey> key_pair_bob =
    keys.GenerateKeyPair(ecc_group);                                      //Bob生成一對公私鑰

std::vector<Keys::KFrag> kfrags =
    keys.GenerateReKey(ecc_group, key_pair_alice.second, key_pair_alice.first,
                       key_pair_bob.first, 5, 4);                         //生成5個重加密金鑰

for (int i = 0; i < 5; i++) {
  EXPECT_TRUE(kfrags[i].id > zero);
}

2、加解密

  • Alice生成公私鑰
  • Alice加解密測試
/************************* Phase 1 *************************/
// Start testing encryption and decryption functions
std::unique_ptr<EcGroup> ecc_group = EcGroupFactory::Create("sm2");       // 引數生成

std::pair<Keys::PublicKey, Keys::PrivateKey> key_pair_A =
    keys.GenerateKeyPair(ecc_group);                                      //Alice生成金鑰對

// test tpre.Encrypt
std::string message = "hellooooooooooooo, I am 63, who are you?";         //明文訊息

std::pair<Capsule::CapsuleStruct, std::vector<uint8_t>> ct_1 =
    tpre.Encrypt(ecc_group, key_pair_A.first, iv, message);               //使用Alice的公鑰加密得到密文(Capsule,encdata)

// test tpre.Decrypt

std::string message_1 =
    tpre.Decrypt(ecc_group, ct_1.first, iv, ct_1.second, key_pair_A.second);  //使用Alice的私鑰解密得到明文

// Determine if decryption was successful
EXPECT_EQ(message, message_1);                                                //測試解密是否成功

// End testing encryption and decryption functions

3、重加密和解密

  • Alice使用公鑰加密訊息
  • 生成N個重加密金鑰片段
  • \(t\)個重加密金鑰片段進行重新加密密文
  • Bob使用私鑰解密,恢復明文
/************************* Phase 2 *************************/
// Start testing encryption, re-encryption, and decryption functions

// Second test tpre.Encrypt
std::string message_2 =
    "If you were a teardrop;In my eye, For fear of losing you, I would never "
    "cry. And if the golden sun, Should cease to shine its light, Just one "
    "smile from you, Would make my whole world bright.";

std::pair<Capsule::CapsuleStruct, std::vector<uint8_t>> ct_2 =
    tpre.Encrypt(ecc_group, key_pair_A.first, iv, message_2);                  //使用Alice的公鑰加密明文訊息

// test keys->GenerateReKey
std::pair<Keys::PublicKey, Keys::PrivateKey> key_pair_B =
    keys.GenerateKeyPair(ecc_group);                                           //Bob生成公私鑰

int N = 5;  // Number of all participants     //代理節點數
int t = 4;  // Threshold                      //閾值大小

std::vector<Keys::KFrag> kfrags = keys.GenerateReKey(
    ecc_group, key_pair_A.second, key_pair_A.first, key_pair_B.first, N, t);     //生成N個重加密金鑰片段

// test tpre.ReEncrypt
std::pair<std::vector<Capsule::CFrag>, std::vector<uint8_t>> re_ct_set;

// You need to meet the number of participants to successfully decrypt,
// otherwise decryption will not be successful

for (int i = 0; i < t; i++) {
  std::pair<Capsule::CapsuleStruct, std::vector<uint8_t>> ct_2_i = {
      ct_2.first, ct_2.second};

  std::pair<Capsule::CFrag, std::vector<uint8_t>> re_ct_i =
      tpre.ReEncrypt(ecc_group, kfrags[i], ct_2_i);                               //使用重加密金鑰片段 重新加密密文 得到re_ct_set

  std::unique_ptr<Capsule::CFrag> cfrag_i_up(
      new Capsule::CFrag(re_ct_i.first));

  re_ct_set.first.push_back(re_ct_i.first);

  re_ct_set.second = re_ct_i.second;
}

// test tpre.DecryptFrags

std::string message_3 =
    tpre.DecryptFrags(ecc_group, key_pair_B.second, key_pair_A.first,
                      key_pair_B.first, iv, re_ct_set);                           //Bob使用私鑰解密

// Determine whether decryption was successful after performing re-encryption

EXPECT_EQ(message_2, message_3);

總結

為了適應資料要素化背景下資料流通需求場景,提出了基於國密的分散式門限代理重加密演算法TPRE,該演算法是由成方金科新技術實驗室密碼學研究員張曙光基於隱語社群的基礎密碼學庫yacl實現。與傳統的訪問控制和許可權管理方法相比,TPRE演算法具有以下優點:

  • 高強度加密:使用門限代理重加密技術對資料進行高強度加密,保護資料的機密性和隱私性。
  • 分散式架構:採用分散式架構實現對分散式資料的訪問控制和授權管理,解決了傳統方法在分散式場景下的難題。
  • 自主可控:基於國密演算法設計,保證了資料的自主可控性,可以有效地避免資料洩露和資訊保安風險。
  • 高效效能:演算法實現簡單、計算量小,可在現有的計算資源下高效地進行資料安全共享和授權管理。

總之,基於國密的分散式門限代理重加密演算法TPRE在資料隱私保護和資料安全共享方面具有廣泛的應用前景和重要意義,為使用者提供了一種安全、高效、自主可控的資料共享和授權管理方案。

相關文章