密码学学习笔记(四)——(t, n)门限的属性加密CP-ABE
传统的公钥密码只能实现一对一的通信的方式,即加密者用目标用户的公钥来进行加密,也只有目标用户能够实现正确解密。而这种加密方式却不能满足将云存储平台上数据方便而又安全的共享给多个用户的需求。2005年,Sahai等人在欧洲密码学会议上提出了FIBE,并由此提出了基于属性加密(Attribute- Based Encryption, ABE)的概念。这里的模糊身份不再是如原来身份信息的一一对应,而是一
本章资源
1. 属性加密
传统的公钥密码只能实现一对一的通信的方式,即加密者用目标用户的公钥来进行加密,也只有目标用户能够实现正确解密。而这种加密方式却不能满足将云存储平台上数据方便而又安全的共享给多个用户的需求。2005年,Sahai等人在欧洲密码学会议上提出了FIBE,并由此提出了基于属性加密(Attribute- Based Encryption, ABE)的概念。这里的模糊身份不再是如原来身份信息的一一对应,而是一个一对多的对应过程。同时ABE密码体制中还引入了访问结构来完成对多个属性的细粒度控制。当前,云存储的发展主要受限于其安全性和访问控制问题,由于ABE密码体制对云存储环境有极强的适用性,使得它有望成为突破云存储发展困境的有效解决方法之一。尤其在云环境用户数量极多的条件下,ABE密码体制较其它公钥密码体制有着无可比拟的优势。
ABE方案分为密文策略属性加密方案(Ciphertext-Policy Attribute-Based Encryption,CP-ABE)和密钥策略属性加密方案(Key-Policy Attribute-Based Encryption,KP-ABE)。在CP-ABE方案中,访问结构与密文相关,由加密者来指定访问结构,用户私钥与相应的属性集合相关。在KP-ABE方案中,访问结构与密钥相关,属性集合和密文相关。由于在CP-ABE中,数据发送者指定访问结构,因此,常用在访问控制类应用中。
2. Pbc库处理
我们首先对Pbc库进行处理,将我们需要用到的类与操作进行定义。
#include "pbc-class.h"
#include <stdio.h>
#include <stdarg.h>
#ifdef DEBUG
#include <assert.h>
#endif // DEBUG
/// <summary>
/// 全局变量
/// </summary>
namespace PbcInit {
/// <summary>
/// 双线性对
/// </summary>
pairing_t bilinear_pairing;
bool inited = false;
}
/// <summary>
/// 通过cmd参数初始化双线性对参数
/// <para>main函数的书写格式必需为</para>
/// <para>int main(int argc, char *argv[])</para>
/// <para>建议程序刚启动就立即调用</para>
/// </summary>
/// <param name="argc"> : 来自main函数</param>
/// <param name="argv"> : 来自main函数</param>
void pbc_init(int argc, char *argv[]) {
if(PbcInit::inited) return;
PbcInit::inited = true;
pbc_demo_pairing_init(PbcInit::bilinear_pairing, argc, argv);
return;
}
/// <summary>
/// 释放内存,主函数结束之前必需调用
/// </summary>
void pbc_clear() {
if(!PbcInit::inited)return;
PbcInit::inited = false;
pairing_clear(PbcInit::bilinear_pairing);
return;
}
#ifdef _CALL_DEBUG_
const char *const dbg_fmt = "[%2d:<%s> is called ]\n";
#endif // _CALL_DEBUG_
/*========= PBC_Element =========*/
PBC_Element::~PBC_Element() {
#ifdef _CALL_DEBUG_
static int cnt = 0;
printf(dbg_fmt, ++cnt, __FUNCTION__);
#endif // _CALL_DEBUG_
element_clear(this->val);
}
void PBC_Element::rand() {
this->val->field->random(this->val);
return;
}
void PBC_Element::set0() {
this->val->field->set0(this->val);
return;
}
void PBC_Element::set1() {
this->val->field->set1(this->val);
return;
}
int PBC_Element::size() {
return element_length_in_bytes(this->val);
}
bool PBC_Element::isEqualTo(PBC_Element &X) {
return !element_cmp(this->val, X.val);
}
void PBC_Element::println() {
element_printf("%B\n", this->val);
return;
}
void PBC_Element::println(const char *const fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
element_printf("%B\n", this->val);
return;
}
void PBC_Element::copy(PBC_Element &n) {
this->val->field->set(this->val, n.val);
return;
}
int PBC_Element::toBytes(void *Buf) {
return element_to_bytes((unsigned char *)Buf, this->val);
}
int PBC_Element::setFromBytes(void *Buf) {
return element_from_bytes(this->val, (unsigned char *)Buf);
}
/*============= Zr =============*/
Zr::Zr() {
#ifdef _CALL_DEBUG_
static int cnt = 0;
printf(dbg_fmt, ++cnt, __FUNCTION__);
#endif // _CALL_DEBUG_
element_init_Zr(this->val, PbcInit::bilinear_pairing);
}
void Zr::add(Zr &X, Zr &Y) {
this->val->field->add(this->val, X.val, Y.val);
return;
}
void Zr::sub(Zr &X, Zr &Y) {
this->val->field->sub(this->val, X.val, Y.val);
return;
}
void Zr::mul(Zr &X, Zr &Y) {
this->val->field->mul(this->val, X.val, Y.val);
return;
}
void Zr::div(Zr &X, Zr &Y) {
this->val->field->div(this->val, X.val, Y.val);
return;
}
void Zr::inv(Zr &X) {
this->val->field->invert(this->val, X.val);
return;
}
void Zr::neg(Zr &X) {
this->val->field->neg(this->val, X.val);
return;
}
Zr &Zr::operator = (Zr &X) {
//this->copy(X);
this->val->field->set(this->val, X.val);
return *this;
}
Zr &Zr::operator -() {
static Zr res;
//res.neg(*this);
res.val->field->neg(this->val, this->val);
return res;
}
Zr &operator + (Zr &X, Zr &Y) {
static Zr res;
//res.add(X, Y);
res.val->field->add(res.val, X.val, Y.val);
return res;
}
Zr &operator - (Zr &X, Zr &Y) {
static Zr res;
//res.sub(X, Y);
res.val->field->sub(res.val, X.val, Y.val);
return res;
}
Zr &operator * (Zr &X, Zr &Y) {
static Zr res;
//res.mul(X, Y);
res.val->field->mul(res.val, X.val, Y.val);
return res;
}
Zr &operator / (Zr &X, Zr &Y) {
static Zr res;
//res.div(X, Y);
res.val->field->div(res.val, X.val, Y.val);
return res;
}
bool operator == (Zr &X, Zr &Y) {
return !element_cmp(X.val, Y.val);
}
bool operator != (Zr &X, Zr &Y) {
return !!element_cmp(X.val, Y.val);
}
/*============= G1 =============*/
G1::G1() {
#ifdef _CALL_DEBUG_
static int cnt = 0;
printf(dbg_fmt, ++cnt, __FUNCTION__);
#endif // _CALL_DEBUG_
element_init_G1(this->val, PbcInit::bilinear_pairing);
}
void G1::add(G1 &P, G1 &Q) {
this->val->field->add(this->val, P.val, Q.val);
return;
}
void G1::sub(G1 &P, G1 &Q) {
this->val->field->sub(this->val, P.val, Q.val);
return;
}
void G1::neg(G1 &Q) {
this->val->field->neg(this->val, Q.val);
return;
}
void G1::DotPro(Zr &n, G1 &P) {
element_pow_zn(this->val, P.val, n.val);
return;
}
void G1::Double() {
this->val->field->doub(this->val, this->val);
return;
}
void G1::Double(G1 &P) {
this->val->field->doub(this->val, P.val);
return;
}
G1 &G1::operator = (G1 &P) {
//this->copy(P);
this->val->field->set(this->val, P.val);
return *this;
}
G1 &G1::operator -() {
static G1 res;
//res.neg(*this);
res.val->field->neg(res.val, this->val);
return res;
}
G1 &operator + (G1 &P, G1 &Q) {
static G1 res;
//res.add(P, Q);
res.val->field->add(res.val, P.val, Q.val);
return res;
}
G1 &operator - (G1 &P, G1 &Q) {
static G1 res;
//res.sub(P, Q);
res.val->field->sub(res.val, P.val, Q.val);
return res;
}
bool operator == (G1 &P, G1 &Q) {
return !element_cmp(P.val, Q.val);
}
bool operator != (G1 &P, G1 &Q) {
return !!element_cmp(P.val, Q.val);
}
/*============= G2 =============*/
G2::G2() {
#ifdef _CALL_DEBUG_
static int cnt = 0;
printf(dbg_fmt, ++cnt, __FUNCTION__);
#endif // _CALL_DEBUG_
element_init_G2(this->val, PbcInit::bilinear_pairing);
}
void G2::add(G2 &P, G2 &Q) {
this->val->field->add(this->val, P.val, Q.val);
return;
}
void G2::sub(G2 &P, G2 &Q) {
this->val->field->sub(this->val, P.val, Q.val);
return;
}
void G2::neg(G2 &Q) {
this->val->field->neg(this->val, Q.val);
return;
}
void G2::DotPro(Zr &n, G2 &P) {
element_pow_zn(this->val, P.val, n.val);
return;
}
void G2::Double() {
this->val->field->doub(this->val, this->val);
return;
}
void G2::Double(G2 &P) {
this->val->field->doub(this->val, P.val);
return;
}
G2 &G2::operator = (G2 &P) {
//this->copy(P);
this->val->field->set(this->val, P.val);
return *this;
}
G2 &G2::operator -() {
static G2 res;
//res.neg(*this);
res.val->field->neg(res.val, this->val);
return res;
}
G2 &operator + (G2 &P, G2 &Q) {
static G2 res;
//res.add(P, Q);
res.val->field->add(res.val, P.val, Q.val);
return res;
}
G2 &operator - (G2 &P, G2 &Q) {
static G2 res;
//res.sub(P, Q);
res.val->field->sub(res.val, P.val, Q.val);
return res;
}
bool operator == (G2 &P, G2 &Q) {
return !element_cmp(P.val, Q.val);
}
bool operator != (G2 &P, G2 &Q) {
return !!element_cmp(P.val, Q.val);
}
/*============= GT =============*/
GT::GT() {
#ifdef _CALL_DEBUG_
static int cnt = 0;
printf(dbg_fmt, ++cnt, __FUNCTION__);
#endif // _CALL_DEBUG_
element_init_GT(this->val, PbcInit::bilinear_pairing);
}
void GT::pow(GT &GX, Zr &n) {
element_pow_zn(this->val, GX.val, n.val);
return;
}
void GT::E(G1 &P1, G2 &P2) {
pairing_apply(this->val, P1.val, P2.val, PbcInit::bilinear_pairing);
return;
}
void GT::inv(GT &GX) {
this->val->field->invert(this->val, GX.val);
return;
}
void GT::mul(GT &GX, GT &GY) {
this->val->field->mul(this->val, GX.val, GY.val);
return;
}
void GT::div(GT &GX, GT &GY) {
this->val->field->div(this->val, GX.val, GY.val);
return;
}
GT >::operator = (GT &V) {
//this->copy(V);
this->val->field->set(this->val, V.val);
return *this;
}
GT &operator * (GT &GX, GT &GY) {
static GT res;
//res.mul(GX, GY);
res.val->field->mul(res.val, GX.val, GY.val);
return res;
}
GT &operator / (GT &GX, GT &GY) {
static GT res;
//res.div(GX, GY);
res.val->field->div(res.val, GX.val, GY.val);
return res;
}
bool operator == (GT &GX, GT &GY) {
return !element_cmp(GX.val, GY.val);
}
bool operator != (GT &GX, GT &GY) {
return !!element_cmp(GX.val, GY.val);
}
//Zr ~ G1
G1 &operator * (Zr &a, G1 &P) {
static G1 res;
//res.DotPro(a, P);
element_pow_zn(res.val, P.val, a.val);
return res;
}
//Zr ~ G2
G2 &operator * (Zr &a, G2 &P) {
static G2 res;
//res.DotPro(a, P);
element_pow_zn(res.val, P.val, a.val);
return res;
}
//Zr ~ GT
GT &operator ^ (GT &X, Zr &n) {
static GT res;
//res.pow(X, n);
element_pow_zn(res.val, X.val, n.val);
return res;
}
//G1 ~ G2
GT &fE(G1 &P1, G2 &P2) {
static GT res;
//res.E(P1, P2);
pairing_apply(res.val, P1.val, P2.val, PbcInit::bilinear_pairing);
return res;
}
3. CP-ABE函数解析
首先,我们将整个项目中都需要用到的函数(或预处理函数)写入到一个源文件中:
#include "cpabe_common.h"
#include "sm3.h"
namespace Zr_Random_Buff {
bool IsInited = false;
Zr* BUF = 0; //Zr BUF[All]; //每个属性对应的随机数
}
void InitZrRandomBuff(int All) {
if (!Zr_Random_Buff::IsInited) {
Zr_Random_Buff::BUF = new Zr[All];
for (int i = 0; i < All; i++) {
Zr_Random_Buff::BUF[i].rand();
Zr_Random_Buff::BUF[i].println("BUF[%d]:",i);
}
Zr_Random_Buff::IsInited = true;
}
return;
}
void ClearZrRandomBuff() {
if (Zr_Random_Buff::IsInited) {
Zr_Random_Buff::IsInited = false;
delete[] Zr_Random_Buff::BUF;
Zr_Random_Buff::BUF = 0;
}
return;
}
///
int& UsrAtt::operator[](const int index) {
return this->U[(index % w + w) % w];
}
UsrAtt::UsrAtt(const int w) {
this->U = new int[w];
this->w = w;
this->RandBuf = Zr_Random_Buff::BUF;
}
UsrAtt::~UsrAtt() {
if (this->U) {
delete[] this->U;
this->U = 0;
//this->n = this->t = 0;
}
}
///
int ABEPK::toBytes(U8* buf) {
int cnt = 0, ret;
ret = this->Y.toBytes(buf);
cnt += ret;
buf += ret;
ret = this->h.toBytes(buf);
cnt += ret;
buf += ret;
return cnt;
}
int ABEPK::Read(U8* buf) {
int cnt = 0, ret;
ret = this->Y.setFromBytes(buf);
cnt += ret;
buf += ret;
ret = this->h.setFromBytes(buf);
cnt += ret;
buf += ret;
return cnt;
}
ABEPK::ABEPK() {
}
ABEPK::~ABEPK() {
}
///
int ABESK::toBytes(U8* buf) {
int cnt = 0, ret;
ret = this->y.toBytes(buf);
cnt += ret;
buf += ret;
ret = this->b.toBytes(buf);
cnt += ret;
buf += ret;
return cnt;
}
int ABESK::Read(U8* buf) {
int cnt = 0, ret;
ret = this->y.setFromBytes(buf);
cnt += ret;
buf += ret;
ret = this->b.setFromBytes(buf);
cnt += ret;
buf += ret;
return cnt;
}
ABESK::ABESK() {
}
ABESK::~ABESK() {
}
///
int USRSK::toBytes(U8* Buf) {
int ret, cnt = 0, i;
ret = this->skD.toBytes(Buf);
Buf += ret;
cnt += ret;
for (i = 0; i < w; i++) {
ret = this->D[i].toBytes(Buf);
Buf += ret;
cnt += ret;
}
for (i = 0; i < w; i++) {
ret = this->d[i].toBytes(Buf);
Buf += ret;
cnt += ret;
}
return cnt;
}
int USRSK::Read(U8* Buf) {
int ret, cnt = 0, i;
ret = this->skD.setFromBytes(Buf);
Buf += ret;
cnt += ret;
for (i = 0; i < w; i++) {
ret = this->D[i].setFromBytes(Buf);
Buf += ret;
cnt += ret;
}
for (i = 0; i < w; i++) {
ret = this->d[i].setFromBytes(Buf);
Buf += ret;
cnt += ret;
}
return cnt;
}
USRSK::USRSK( const int w) {
this->w = w;
this->D = new G1[w];
this->d = new G2[w];
}
USRSK::~USRSK() {
if (this->D) {
delete[] this->D;
this->D = 0;
}
if (this->d) {
delete[] this->d;
this->d = 0;
}
}
/*//
int ABEMSG::toBytes(U8* Buf) {
return this->M.toBytes(Buf);
}
int ABEMSG::Read(U8* Buf) {
return this->M.setFromBytes(Buf);
}
//*/
int CPHR::toBytes(U8* Buf) {
int cnt = 0, ret, i;
ret = this->C_Bar.toBytes(Buf);
Buf += ret;
cnt += ret;
for (i = 0; i < n; i++) {
this->C[i].toBytes(Buf);
Buf += ret;
cnt += ret;
}
for (i = 0; i < n; i++) {
this->c[i].toBytes(Buf);
Buf += ret;
cnt += ret;
}
return cnt;
}
int CPHR::Read(U8* Buf) {
int cnt = 0, ret, i;
ret = this->C_Bar.setFromBytes(Buf);
Buf += ret;
cnt += ret;
for (i = 0; i < n; i++) {
this->C[i].setFromBytes(Buf);
Buf += ret;
cnt += ret;
}
for (i = 0; i < n; i++) {
this->c[i].setFromBytes(Buf);
Buf += ret;
cnt += ret;
}
return cnt;
}
CPHR::CPHR(const int t, const int n) {
this->n = n;
this->t = t;
this->C = new G1[n];
this->c = new G2[n];
}
CPHR::~CPHR() {
if (this->C) {
delete[] this->C;
this->C = 0;
}
if (this->c) {
delete[] this->c;
this->c = 0;
}
}
///
U8STR::U8STR() {
this->m = 0;
}
U8STR::U8STR(const int len) {
this->m = new U8[len];
}
U8STR::~U8STR() {
if (this->m) {
delete[] this->m;
this->m = 0;
}
}
U8 U8STR::operator [] (const int index)const {
return this->m[index];
}
///
SM3KDF::SM3KDF(PBC_Element& M) {
this->len = M.size() + 4;
this->buff = new U8[len];
M.toBytes(buff + 4);
*(U32*)this->buff = 0;
sm3(this->buff, this->len, this->Hash);
}
SM3KDF::~SM3KDF() {
delete[]this->buff;
}
void SM3KDF::reHash() {
(*(U32*)this->buff)++;
sm3(this->buff, this->len, this->Hash);
return;
}
U8 SM3KDF::operator [] (const int index)const {
return this->Hash[index];
}
///
U8 char2U8(const char x) {
switch (x) {
case '0':return 0;
case '1':return 1;
case '2':return 2;
case '3':return 3;
case '4':return 4;
case '5':return 5;
case '6':return 6;
case '7':return 7;
case '8':return 8;
case '9':return 9;
case 'a':case 'A':return 10;
case 'b':case 'B':return 11;
case 'c':case 'C':return 12;
case 'd':case 'D':return 13;
case 'e':case 'E':return 14;
case 'f':case 'F':return 15;
default:
break;
}
return 0;
}
int str2hex(const char* str, U8* res) {
int len = strlen(str) / 2;
for (int i = 0; i < len; i++) {
res[i] = (char2U8(str[i * 2]) << 4) | char2U8(str[i * 2 + 1]);
}
return len;
}
int readstr(char** p) {
char* str = *p;
int ans = 0;
while (*str < '0' || *str > '9')str++;
ans = *str - '0';
str++;
while ('0' <= *str && *str <= '9') {
ans = ans * 10 + *str - '0';
str++;
}
*p = str;
return ans;
}
3.1 初始化算法
- 建立双线性映射: G 1 × G 2 → G T G_1 \times G_2 \to G_T G1×G2→GT,其中 G 1 G_1 G1、 G 2 G_2 G2和 G T G_T GT都是 p p p阶的, G 1 G_1 G1与 G 2 G_2 G2的生成元分别为 P 1 P_1 P1和 P 2 P_2 P2,作为全局变量;
- 公开一个特殊的函数 H : { 0 , 1 } ∗ → G 1 H:\{0,1\}^* \to G_1 H:{0,1}∗→G1(将输入的一个数映射到 G 1 G_1 G1的一个点中);
- 随机选取 y , β ∈ Z p y,\beta \in \mathbb{Z}_p y,β∈Zp,计算 Y = E ( P 1 + P 2 ) y Y = E(P_1 + P_2) ^ y Y=E(P1+P2)y, h = β ⋅ P 2 h = \beta \cdot P_2 h=β⋅P2;
- 输出:公钥 P K = ( Y , h ) PK = (Y,h) PK=(Y,h),主私钥 M S K = ( y , β ) MSK = (y,\beta) MSK=(y,β)。
#include "cpabe_setup.h"
void Setup(G1& P1, G2& P2, ABEPK& PK, ABESK& SK)
{
SK.y.rand();
SK.b.rand();
GT E;
E.E(P1, P2); // E = e(P1,P2)
PK.Y.pow(E, SK.y);
PK.h.DotPro(SK.b, P2);
return;
}
void Setup(U8* p1, U8* p2, U8* pk, U8* sk)
{
G1 P1; G2 P2;
ABEPK PK;
ABESK SK;
P1.setFromBytes(p1);
P2.setFromBytes(p2);
Setup(P1, P2, PK, SK);
PK.toBytes(pk);
SK.toBytes(sk);
return;
}
3.2 密钥生成算法
- 输入:用户属性集合 w w w,公钥 P K PK PK,主私钥 M S K MSK MSK;
- 选择随机数
r
∈
Z
p
r \in \mathbb{Z}_p
r∈Zp,对于
w
w
w中的每个属性
j
∈
w
j \in w
j∈w,选择一个随机数
r
j
∈
Z
p
r_j \in \mathbb{Z}_p
rj∈Zp,计算出属性集合
w
w
w对应的私钥:
S K w = { D ′ = ( ( y + r ) β − 1 m o d p ) P 1 , ∀ j ∈ w : D j = r P 1 + r j H ( j ) , D j ′ = r j P 2 } SK_w = \{D' = ((y + r)\beta^{-1}\;mod\;p)P_1,\ \forall j \in w:D_j = rP_1 + r_jH(j),\ D_j' = r_jP_2\} SKw={D′=((y+r)β−1modp)P1, ∀j∈w:Dj=rP1+rjH(j), Dj′=rjP2} - 输出:用户私钥 S K w SK_w SKw。
#include "cpabe_kengen.h"
void KeyGen(G1& P1, G2& P2, UsrAtt& Usr, ABESK& SK, USRSK& uSK)
{
int Wu = Usr.w;
int i, k;
Zr Fx_t, Ri, b_inv, a, R;
G1 m1, m2, H; G2 n;
R.rand();
a.add(R, SK.y);//a = R + y
b_inv.inv(SK.b); //b_inv = b^(-1)
a.mul(a, b_inv); //a = a * b_inv
uSK.skD.DotPro(a, P1); //USRSK.skD = a * P1
U8 RI[1000] = { 0 }; int Ri_len;
for (i = 0; i < Wu; i++) {
//对用户的第i个属性
k = Usr[i];
Ri.copy(Usr.RandBuf[k]);
//从随机数缓存区中获得一个随机数
//属性i对应随机数 RandBuf[Usr[i]]
m1.DotPro(R, P1);//m1 = R * P1
Ri_len = Ri.toBytes(RI);
element_from_hash(H.val, RI, Ri_len);
m2.DotPro(Ri, H);
uSK.D[i].add(m1, m2);
uSK.d[i].DotPro(Ri, P2);
}
return;
}
void KeyGen(int w, U8* p1, U8* p2, UsrAtt& Usr, U8* sk)
{
int uLen, i;
char path[20] = "\0";
U8 usk[3000] = { 0 };
G1 P1; G2 P2;
ABESK SK;
USRSK uSK(w);
for (i = 0; i < w; i++)
scanf("%d", &Usr[i]);
P1.setFromBytes(p1);
P2.setFromBytes(p2);
SK.Read(sk);
KeyGen(P1, P2, Usr, SK, uSK);
uLen = uSK.toBytes(usk);
sprintf(path, "user.key");
FILE* fp = fopen(path, "wb");
fprintf(fp, "####用户私钥文件####\n\n");
fprintf(fp, "//用户门限w\nthreshold = %d\n", w);
fprintf(fp, "\n\n//用户属性\nattribute = %d", Usr[0]);
for (i = 1; i < w; i++) {
fprintf(fp, ",%d", Usr[i]);
}
fprintf(fp, "\n\n");
fprintf(fp, "\n\n/*\n* 用户私钥\n* 共%d字节\n* 仅用于解密\n*/\n", uLen);
fprintf(fp, "key = ");
for (i = 0; i < uLen; i++) {
fprintf(fp, "%02X", usk[i]);
// printf( "%02X", usk[i]);
}
fprintf(fp, "#");
printf(" 已生成私钥文件%s\n", path);
fclose(fp);
return;
}
3.3 加密算法
- 输入:门限 ( t , n ) (t,n) (t,n),明文文件 M M M,公钥 P K PK PK;
- 随机选取
s
∈
Z
p
s \in \mathbb{Z}_p
s∈Zp,生成一个
t
−
1
t-1
t−1次多项式
f ( x ) = ( a t − 1 x t − 1 + ⋯ + a 1 x + s ) m o d p f(x) = (a_{t-1}x^{t-1} + \cdots +a_1x + s)\ mod\;p f(x)=(at−1xt−1+⋯+a1x+s) modp - 计算密文分量
C ∗ = M ⊕ K D F ( e ( P 1 , P 2 ) y s ) C ∗ ∗ = s ⋅ h = s β ⋅ P 2 C i = f ( i ) P 2 C i ′ = f ( i ) H ( i ) C^* = M \oplus KDF(e(P_1,P_2)^{ys})\\ C^{**} = s \cdot h = s\beta \cdot P_2\\ C_i = f(i)P_2\\ C_i' = f(i)H(i) C∗=M⊕KDF(e(P1,P2)ys)C∗∗=s⋅h=sβ⋅P2Ci=f(i)P2Ci′=f(i)H(i) - 输出:密文文件 C = ( C ∗ , C ∗ ∗ , C i , C i ′ ) C = (C^*,C^{**},C_i,C_i') C=(C∗,C∗∗,Ci,Ci′)。
#include "cpabe_encrypt.h"
void EnCrypt(G1& P1, G2& P2, UsrAtt& Admin, ABEPK& PK, CPHR& C, GT& M)
{
int Ta = C.t, Na = C.n;
int i, j, k;
Zr s, Fx_t, X;
G1 H;
s.rand();
Zr* Fx = new Zr[Ta]; // Zr Fx[Ta]
// 生成 t-1 次多项式 {Fx[0] ... F[t-1]}
Fx[0].copy(s);
for (i = 1; i < Ta; i++) {
Fx[i].rand();
}
C.C_Bar.DotPro(s, PK.h); //C' = s * h;
for (i = 0; i < Na; i++) {
U8 X_data[1000] = { 0 };
int X_len = 0;
k = Admin[i];
X.copy(Admin.RandBuf[k]);
Fx_t.set0();//先假设Fx_t = 0
for (j = Ta - 1; j >= 0; j--) {
Fx_t.mul(Fx_t, X); // Fx_t = Fx_t * X
Fx_t.add(Fx_t, Fx[j]); // Fx_t = Fx_t + Fx[j]
}//Fx_t = f(X)
C.c[i].DotPro(Fx_t, P2);
X_len = X.toBytes(X_data);
element_from_hash(H.val, X_data, X_len);
// H.println("H%d = ", k);
C.C[i].DotPro(Fx_t, H);
}
M.copy(PK.Y);
M.pow(M, s);
delete[] Fx;
return;
}
void FileEnCrypt(int t, int n, U8* p1, U8* p2, UsrAtt& Admin, U8* pk, const char* path)
{
int i, ret = 0, mlen, clen;
for (i = 0; i < n; i++)
scanf("%d", &Admin[i]);
char cipher_path[100] = "\0";
sprintf(cipher_path, "%s.enc", path);
FILE* fpI = fopen(path, "rb");
FILE* fpO = fopen(cipher_path, "wb");
G1 P1; G2 P2; GT M;
ABEPK PK; CPHR C(t, n);
U8* c = 0, * m = 0;
fseek(fpI, 0, SEEK_END);
mlen = ftell(fpI);
rewind(fpI);
m = new U8[mlen];
c = new U8[mlen + 1000 * n];
U8* pm = m, * pc = c;
fread(m, 1, mlen, fpI);
*pc = t;
pc++;
*pc = n;
pc++;
for (i = 0; i < n; i++) {
*pc = (U8)(Admin[i]);
pc++;
}
// c = admin | C | H(M)^Msg
P1.setFromBytes(p1);
P2.setFromBytes(p2);
PK.Read(pk);
// PK.Y.println("\nY = ");
EnCrypt(P1, P2, Admin, PK, C, M);
// C.C_Bar.println();
ret = C.toBytes(pc);
pc += ret;
SM3KDF T(M); // SM3自动机
int len = mlen;
while (len >= 32) {
//以32字节为单位异或
for (i = 0; i < 32; i++) {
*pc = *pm ^ T[i];
// printf("0x%02x ", *pc);
pc++; pm++;
}
// printf("\n");
T.reHash();
len -= 32;
}
// 最后不足32字节的时候
for (i = 0; i < len; i++) {
*pc = *pm ^ T[i];
// printf("0x%02x ", *pc);
pc++; pm++;
}
// printf("\n\n");
clen = ret + mlen;
clen += n + 2;
fwrite(c, 1, clen, fpO);
delete[]c;
delete[]m;
fclose(fpI);
fclose(fpO);
// printf("已加密,生成文件%s\n", cipher_path);
return;
}
3.4 解密算法
- 输入:密文文件 C C C,用户私钥 S K w SK_w SKw;
- 计算:
e ( D i , C i ) = e ( r P 1 + r i H ( i ) , f ( i ) P 2 ) e ( C i ′ , D i ′ ) = e ( f ( i ) H ( i ) , r i P 2 ) e ( D i , C i ) e ( C i ′ , D i ′ ) = e ( P 1 , P 2 ) r ⋅ f ( i ) ∏ i ∈ t ′ e ( P 1 , P 2 ) r ⋅ f ( i ) ⋅ Δ i = e ( P 1 , P 2 ) r s e ( D ′ , C ∗ ∗ ) = e ( ( ( y + r ) β − 1 m o d p ) P 1 , s β ⋅ P 2 ) = e ( y P 1 , s P 2 ) e ( r P 1 , s P 2 ) e ( D ′ , C ∗ ∗ ) ( e ( P 1 , P 2 ) r s ) − 1 = e ( y P 1 , s P 2 ) e(D_i,C_i) = e(rP_1 + r_iH(i),f(i)P_2)\\ e(C_i',D_i') = e(f(i)H(i),r_iP_2)\\ \frac{e(D_i,C_i)}{e(C_i',D_i')} = e(P_1,P_2)^{r\cdot f(i)}\\ \prod _{i \in t'}e(P_1,P_2)^{r \cdot f(i) \cdot \Delta i} = e(P_1,P_2)^{rs}\\ e(D',C^{**}) = e(((y + r)\beta^{-1}\;mod\,p)P_1,s\beta\cdot P_2) = e(yP_1,sP_2)e(rP_1,sP_2)\\ e(D',C^{**})(e(P_1,P_2)^{rs})^{-1} = e(yP_1,sP_2) e(Di,Ci)=e(rP1+riH(i),f(i)P2)e(Ci′,Di′)=e(f(i)H(i),riP2)e(Ci′,Di′)e(Di,Ci)=e(P1,P2)r⋅f(i)i∈t′∏e(P1,P2)r⋅f(i)⋅Δi=e(P1,P2)rse(D′,C∗∗)=e(((y+r)β−1modp)P1,sβ⋅P2)=e(yP1,sP2)e(rP1,sP2)e(D′,C∗∗)(e(P1,P2)rs)−1=e(yP1,sP2) - 求解 M = K D F ( e ( y P 1 , s P 2 ) ) ⊕ C ∗ M = KDF(e(yP_1,sP_2)) \oplus C^* M=KDF(e(yP1,sP2))⊕C∗;
- 输出:明文文件 M M M。
#include "cpabe_decrypt.h"
bool DeCrypt(G1& P1, G2& P2, UsrAtt& admin, UsrAtt& usr, USRSK& uSK, CPHR& C, GT& M)
{
int Wu = usr.w, Ta = C.t, Na = C.n;
if (Ta > Wu) {
return false;
}
UsrAtt WD(Ta);
int i, j, k = 0;
GT* Temp = new GT[Na];
GT C_T, c_T;
for (i = 0; i < Na; i++) {
for (j = 0; j < Wu; j++) {
if (admin[i] == usr[j]) {
WD[k] = admin[i];
C_T.E(uSK.D[j], C.c[i]); //C_T = E(D, C)
c_T.E(C.C[i], uSK.d[j]); //c_T = E(C', D')
Temp[k].div(C_T, c_T);
k++;
}
}
if (k >= Ta)
break;
}
if (k < Ta) {
return false;
}
GT A, B;
Zr Li, Lu, Ld; // L = Lu / Ld;
Zr Wi, Wj, e, tZr;
A.set1();
for (i = 0; i < Ta; i++) {
Lu.set1();
Ld.set1();
k = WD[i]; //temp[i]
Wi.copy(WD.RandBuf[k]);
//计算 L(usr[i],w)
for (j = 0; j < Ta; j++) {
if (i == j)continue;
//i != j
Wj.copy(WD.RandBuf[WD[j]]);
Lu.mul(Lu, Wj); // Lu = Lu * Wj
tZr.sub(Wj, Wi); // tZr = Wj - Wi
Ld.mul(Ld, tZr); // Ld = Ld * (Wj - Wi)
}
Li.div(Lu, Ld);
B.pow(Temp[i], Li); //B = Temp[usr[i]]^Li
A.mul(A, B); //A = A * B
}
//A == e(P1, P2) ^ (s * r)
B.E(uSK.skD, C.C_Bar);
A.inv(A);
M.mul(A, B);
delete[] Temp;
return true;
}
bool FileDeCrypt(U8* p1, U8* p2, UsrAtt& usr, U8* usk, const char* path)
{
char Mpath[100], * ptr;
strcpy(Mpath, path);
ptr = strstr(Mpath, ".enc");
if (ptr == NULL) {
return false;
}
*ptr = '\0';
FILE* fpI = fopen(path, "rb");
FILE* fpO = fopen(Mpath, "wb");
int clen, mlen;
fseek(fpI, 0, SEEK_END);
clen = ftell(fpI);
rewind(fpI);
U8* c = new U8[clen];
U8* m = new U8[clen];
U8* pc = c, * pm = m;
fread(c, 1, clen, fpI);
int T, N;
T = *pc;
pc++;
N = *pc;
pc++;
UsrAtt admin(N);
for (int i = 0; i < N; i++) {
admin[i] = *pc;
pc++;
}
//
printf("\t\t密文中嵌入的属性向量为\n\t\t[ ");
for (int ii = 0; ii < N; ii++) {
printf("%d ", admin[ii]);
}
printf("]\n");
G1 P1; G2 P2; GT M;
USRSK uSK(usr.w); CPHR C(T, N);
int i, ret;
P1.setFromBytes(p1);
P2.setFromBytes(p2);
uSK.Read(usk);
ret = C.Read(pc);
pc += ret;
clen -= ret;
mlen = clen;
if (!DeCrypt(P1, P2, admin, usr, uSK, C, M)) {
delete[] c;
delete[] m;
fclose(fpI);
fclose(fpO);
return false;
}
SM3KDF t(M);
while (clen >= 32) {
for (i = 0; i < 32; i++) {
*pm = *pc ^ t[i];
pm++; pc++;
}
t.reHash();
clen -= 32;
}
for (i = 0; i < clen; i++) {
*pm = *pc ^ t[i];
pm++; pc++;
}
mlen -= N + 2;
fwrite(m, 1, mlen, fpO);
delete[] c;
delete[] m;
fclose(fpI);
fclose(fpO);
return true;
}
4. 算法整体实现
最后,我们将这些函数整合,写出用于测试的主函数:
#include "cpabe_common.h"
#include "cpabe_setup.h"
#include "cpabe_kengen.h"
#include "cpabe_encrypt.h"
#include "cpabe_decrypt.h"
#include <time.h>
#include "config io.h"
#define MAX_KEY_SIZE 100000
namespace abeDemo {
U8 p1[600], p2[600], pk[MAX_KEY_SIZE], sk[MAX_KEY_SIZE];
int All = 0;
bool init = false;
}
int read() {
int ans = 0;
char ch;
do {
ch = getchar();
} while (ch < '0' || ch > '9');
ans += ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
ans = ans * 10 + ch - '0';
}
return ans;
}
int main(int argc, char* argv[]){
pbc_init(argc, argv); // 初始化环境
printf("\t输入系统中所有属性的数量:");
while (1 != scanf("%d", &abeDemo::All)) fflush(stdin);
if (abeDemo::All <= 0) {
printf(" 初始化未完成\n ");
return 0;
}
InitZrRandomBuff(abeDemo::All);
G1 P1; G2 P2;
P1.rand(); P2.rand();
P1.toBytes(abeDemo::p1);
P2.toBytes(abeDemo::p2);//*
for (int i = 0; i < 150; i++)
printf("%02x", abeDemo::p1[i]);
printf("\n");
for (int i = 0; i < 150; i++)
printf("%02x", abeDemo::p2[i]);//*/
int start, end;
double dura;
Setup(abeDemo::p1, abeDemo::p2, abeDemo::pk, abeDemo::sk);
printf("\n\n 系统已初始化完成\n ");
abeDemo::init = true;
if (abeDemo::init == false) {
printf(" 系统没有初始化,无法使用\n ");
return 0;
}
system("pause");
system("cls");
///
int w;
printf(" 当前系统共有%d个属性\n", abeDemo::All);
printf(" 输入用户属性w:");
w = read();
printf(" 输入%d个属性,范围在[0 ~ %d]内:", w, abeDemo::All - 1);
UsrAtt Usr(w);
KeyGen(w, abeDemo::p1, abeDemo::p2, Usr, abeDemo::sk);
system("pause");
system("cls");
///
int t, n;
char path[100] = "\0";
printf("\n\n将待加密文件拖拽到窗口:");
do {
gets_s<100>(path);
} while (strlen(path) == 0);
printf(" 当前系统共有%d个属性\n", abeDemo::All);
printf(" 输入密文门限(t,n):");
t = read(); n = read();
printf(" 输入%d个属性,范围在[0 ~ %d]内:", n, abeDemo::All - 1);
UsrAtt Admin(n);
FileEnCrypt(t, n, abeDemo::p1, abeDemo::p2, Admin, abeDemo::pk, path);
printf("\n 已生成文件%s.encrypt\n", path);
system("pause");
system("cls");
///
char fpath[100] = "\0", kpath[100] = "\0";
map<string, string> Key;
printf("\n将用户私钥拖拽到窗口:");
do {
gets_s<100>(kpath);
} while (strlen(kpath) == 0);
LoadConfig(kpath, Key);
U8 usk[2000];
int klen = str2hex(Key[string("key")].c_str(), usk);
char usrbuff[100] = "\0", attbuf[100] = "\0";
strcpy(usrbuff, Key[string("threshold")].c_str());
strcpy(attbuf, Key[string("attribute")].c_str());
char* ptr = usrbuff; int w;
w = readstr(&ptr);
UsrAtt usr(w);
ptr = attbuf;
for (int t = 0; t < w; t++) {
usr[t] = readstr(&ptr);
}
printf("\n将密文文件拖拽到窗口:");
gets_s<100>(fpath);
if (FileDeCrypt(abeDemo::p1, abeDemo::p2, usr, usk, fpath)) {
printf(" 已成功解密\n");
}
else {
printf(" 解密失败\n");
}
system("pause");
pbc_clear(); // 释放内存
ClearZrRandomBuff(); // 释放内存
return 0;
}
注:该方案为一个大的系统的子系统,文件解密后的文件名和路径与原文件一致,在具体实现的时候注意对其进行更改。
更多推荐
所有评论(0)