学习文章:TPRE:分布式门限代理重加密
成方金科新技术实验室与隐语团队合作,构建了“基于国密的分布式门限代理重加密算法TPRE”,为用户提供了一种安全、高效、自主可控的数据共享和授权管理方案。在数据隐私保护和数据安全共享方面具有广泛的应用前景。
⚠️:该算法由成方金科密码学研究员张曙光(知乎:六三)基于隐语的密码库yacl实现,其提供了易于开发的密码工具接口,TPRE算法目前已经贡献至yacl中。https://github.com/secretflow/yacl/tree/main/yacl/crypto/primitives/tpre
TPRE算法具有以下优势:
代理重加密是一种公钥密码方案,它允许代理将一个公钥加密成的密文转换到另一个公钥加密成的密文,而代理者不能了解关于原始消息的任何信息,要做到这一点,代理必须拥有一个重加密密钥。
一个代理重加密算法通常包含三种角色,即数据拥有者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
代理重加密适合在云计算场景中使用,即代理节点为计算性能较强的单节点,这与现有隐私计算体系架构不符,因为现在隐私计算架构通常是分布式架构。因此,需要对传统代理重加密方案进行改造,使之能够适应分布式计算环境。
分布式代理重加密是指将传统代理重加密中的单Proxy节点,拆分为多个Proxy节点。因而在对数据重加密时,需要多个Proxy节点参与合作计算。
考虑到选取参与计算的Proxy节点的灵活性,需要将分布式代理重加密重新设计为基于门限的分布式代理重加密。
Shamir Secret Sharing是一种加密技术,可以将秘密信息分散成多个部分,并分配给不同的人,只有所有部分被汇集在一起才能重构出原来的秘密信息。它是由以色列密码学家Adi Shamir在1979年发明的,被广泛应用于保护机密信息,例如在密码学、数字签名、多方计算等领域。其基本思想是通过多项式插值来实现信息的分散和重构,具有高度的安全性和可靠性。
Shamir密秘分享:假设有一个秘密信息,例如一个密码或者一个私钥,需要将这个秘密信息分配给两个人,可以使用Shamir Secret Sharing算法来实现。
例如:假设秘密信息是密码“5”,需要将它分配给两个人。
具体参考:https://www.cnblogs.com/pam-sh/p/17179097.html#shamir秘密分享
下图是我国商用密码SM2选定的素数域椭圆曲线"sm2p256v1"的参数选择:
更多可参考SM2国标:
使用的哈希算法构造如下:
\(h_x= (1 + Bignum(SM3(x)||SM3(SM3(x))) )mod n-1\)
其中\(n\)是椭圆曲线的阶,\(x\)是函数的输入
SM3套wa?
由于公钥密码是运行在代数计算上的密码算法,其计算效率远低于对称密码,因此,在待加密数据量较大时,使用公钥密码直接对数据加密不是一个良好选择。在该场景中,可使用混合加密体制KEM/DEM:
这两个机制联合使用,可以提供数据的加解密效率,在密文需要传输时,也可降低通信开销。具体而言,DEM机制是用作保护原始数据,即使用对称加密算法,对原始数据进行加密保护;KEM是用作保护加密原始数据时所使用的对称密钥,使用公钥加密算法对对称密钥进行加密保护。
关于更多密钥封装可参考:密钥封装和公钥加密的联系和区别?
方案:https://github.com/nucypher/umbral-doc
UMBRAL是一个代理重加密方案(A THRESHOLD PROXY RE-ENCRYPTION SCHEME),根据一个密钥封装算法改进而来。Alice作为数据产生方,通过\(N\)个代理阶段的重加密机制可以将密文的解密权限委托给Bob,具体来说,Bob得到\((t,N)\)个节点重加密信息才能解密(恢复)出该明文。
代理重加密的使用场景:公共网络中任意数量的参与者实现私人数据共享,但不向中间实体(代理者)泄漏密钥信息。
UMBRAL是阈值代理重加密算法,是非交互式的、单向的,且在重加密后可以验证的,在阈值方面采用Shamir 秘密分享技术。
一个代理重加密【Proxy Re-Encryption,PRE】算法的简易框架如下:
算法中用户角色主要有:
代理重加密(PRE)由KEM(密钥封装)和DEM(分组加密)组成,其中DEM不涉及重加密,所以下图只讨论KEM部分。
下面对KEM的算法功能模块分别介绍:
1、密钥生成算法
Alice得到一对公私钥对\((pk_A,sk_A)\)
输入:\(sk_A=a\)、\(pk_B=g^b\)、片段\(N\)、阈值\(t\)
输出:\(N\)个重加密密钥片段\(kFrag\)
2、封装和解封装算法
输入:\(pk_A\)
输出:对称加密密钥\(K\)和封装结果\(capsule\)
输入:\(sk_A\)和\(capsule\)
输出:对称加密密钥\(K\)
3、重封装和解封装片段算法
输入:重加密密钥片段\(kFrag\)和封装结果\(capsule\)
输出:重新封装的片段(部分)\(cFrag\)
输入:Bob的私钥\(sk_B=b\)、\(t\)个重封装片段\(cFrag\)
输出:对称加密密钥\(K\)
下面详细介绍KDM算法流程:
1、参数设置
2、密钥生成算法
\(KeyGen()\)
\(ReKeyGen(sk_A,pk_B,N,t)\)
3、封装和解封装算法
\(Encapsulate(pk_A)\)
\(CheckCapsule(capsule)\)
\(Decapsulate(sk_A,capsule)\)
4、重封装和解封装片段算法
\(ReEncapsulation(kFrag,capsule)\)
\(DecapsulateFrags(sk_B,\left \{cFrag_i \right \}_{i=1}^{t},capsule)\)
结合上述KEM算法,加入\(DEM\)后,封装和解封装算法变为了加密和解密算法,另外需后续验证,所以DEM具体为认证加密算法(AEAD),下面给出完整的KEM/DEM算法:
\(Encrypt(pk_A,M)\):
\(Decrypt(sk_A,C)\):
\(ReEncrypt(kFrag,C)\):
\(DecryptFrags(sk_B,\{C_i'\}_{i=1}^t)\):
正确性和安全性参考原文。
源程序:https://github.com/nucypher/pyumbral
pyUmbral是对门限代理重加密方案Umbral的实现,基于python,依赖openssl和cryptography库。
在该程序中,Alice(数据拥有者)通过一个重加密的代理节点,将解密权利授权给Bob。具体说,当有门限个代理节点参与重加密时,Bob能聚合这些重加密的密文,然后使用自己的私钥解密恢复出原始消息。
pyUmbral是nucypher背后的加密引擎,nucypher是一个代理重新加密网络,用于在去中心化系统中增强隐私。
pip3 install umbral
下面以一个例子证明方案功能的正确性(docs/examples/umbral_simple_api.py):
1、密钥生成
Alice生成:
Bob生成:
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、加解密
# 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 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 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共包含6个算法,分别为密钥对生成算法【GenerateTpreKeyPair】、重加密密钥生成【GenerateReKey】、加密【Encrypt】、解密算法【Decrypt】、重加密算法【ReEncrypt】、解密重加密密文算法【DecryptFrage】:
假设数据持有方为A,接收方为B,代理者(节点)为proxy
下面是门限代理重加密算法执行流程:
\(capsule\):包含对称密钥信息
\(ct\):使用对称加密的密文
假设Alice为数据所有方,Bob为数据需求方,代理者(代理节点)为proxy
设置参数,为了简单起见,我们将省略其余函数中的公共参数。
\(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})\)
在\(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\)次
封装算法\(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)\)
封装算法是根据公钥\(pk_A\)生成一个对称密钥\(K\)和一个capsule
解封装算法可以根据capsule还原出对称密钥\(K\)
本算法可以选择任意素数域椭圆曲线,例如Secp256k1和sm2p256v1,其中sm2p256v1是我国国产密码学算法SM2使用的椭圆曲线【2.4】
源程序:https://github.com/secretflow/yacl/tree/main/yacl/crypto/primitives/tpre
TPRE是umbral的替代品,它是用C++实现的,用SM2、SM3和SM4取代了ECC和AES等算法。
可用于一下场景:
数据安全共享
数据安全授权
分布式密钥管理
模块功能:
由于tpre是放在隐语的yacl库中,安装编译参考“yacl使用”
1、密钥生成
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、加解密
/************************* 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、重加密和解密
/************************* 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在数据隐私保护和数据安全共享方面具有广泛的应用前景和重要意义,为用户提供了一种安全、高效、自主可控的数据共享和授权管理方案。