GoPBC实践 (一):适用于链上计算的密文大小固定的属性基加密机制(On-Chain ABE)

1,483 阅读2分钟

关于Go PBC Wrapper库的说明介绍在官方文档中已经详细给出,本系列专栏将不再详细介绍官方文档中已有内容,请读者及时翻阅官方文档查看相关函数。

版本说明

PBC LibraryGolanggpbc
Version0.5.141.19.1v0.0.0-20181205041846-3e516ca0c5d6

适用于区块链的密文大小固定的属性基加密机制 (On-Chain Constant-size ABE)

双线性配对

G,GT\mathbb{G},\mathbb{G}_T 为两个具有相同阶 pp 的循环乘法群。ggG\mathbb{G} 的生成元。将 e^\hat{e} 表示为双线性配对:G×GGT\mathbb{G} \times \mathbb{G} \to \mathbb{G}_T
该双线性配对操作具有以下属性:
(1)双线性:e^(ga,gb)=e^(g,g)ab\hat{e}(g^a, g^b)=\hat{e}(g,g)^{ab},其中 a,bZpa, b \in \mathbb{Z}_p^*
(2)非退化性:e^(g,g)1\hat{e}(g,g) \neq 1
(3)可计算性:对所有的 g1,g2Gg_1, g_2 \in \mathbb{G} 计算 e^(g1,g2)\hat{e}(g_1,g_2) 是很有效的。

ABE Setup

  • U={η1,η2,...,ηn}\mathcal{U}=\{\eta_1,\eta_2,...,\eta_n\} 表示应用系统中的 nn 个属性,其中每个属性都有多个值。

  • Si={vi,1,vi,2,...,vi,n}S_i=\{ v_{i,1}, v_{i,2},..., v_{i,n} \} 表示属性 ηi\eta_i 的多个值。

  • Attribute Authority (AA) 选择两个随机大数 x,yZpx, y \in \mathbb{Z}_p^{*} 作为系统私钥 msk=x,ymsk=\left \langle x, y \right \rangleH0H_0 是一个哈希函数:{0,1}Zp\{0,1\}^* \to \mathbb{Z}_p^*

  • 将属性值 SiS_i 与属性 ηi\eta_i 进行绑定,即设置 Xi,k,Yi,kX_{i,k}, Y_{i,k}
    Xi,k=gH0{xiki},Yi,k=e^(g,g)H0{yiki}X_{i,k}=g^{-H_0\{x||i||k_i\}}, Y_{i,k}=\hat{e}(g, g)^{H_0\{y||i||k_i\}}
    其中 i(1in)i (1\le i \le n)U\mathcal{U} 中的第 ii 个属性 ηi\eta_iki(1kini)k_i (1 \le k_i \le n_i) 是属性值集合 SiS_i 中的第 kk 个值。\

  • 将系统公钥设置为 mpk=g,{Xi,k,Yi,k}mpk=\left \langle g, \{X_{i,k}, Y_{i,k} \} \right \rangle

Setup代码实现

type ABEmsk struct {
	x []byte
	y []byte
}

type ABEmpk struct {
	g   []byte
	X_k [][]string
	Y_k [][]string
}
func OCABE() {
    params := pbc.GenerateA(160, 512)
    pairing := params.NewPairing()

    g := pairing.NewG1().Rand()

    x := pairing.NewZr().Rand()
    y := pairing.NewZr().Rand()
    msk := ABEmsk{
            x: x.Bytes(),
            y: y.Bytes(),
    }

    U := []string{"h1", "h2", "d1", "d2", "p", "c1", "c2", "M"}
    S := [][]string{
            {"a", "b", "c"}, {"e", "f", "g"}, {"h", "i", "j", "k"},
            {"l", "m", "n", "o"}, {"q", "r"}, {"s", "t", "a"}, {"b", "c", "e", "f"}, {"t", "s", "y", "w"},
    }

    X_k, Y_k := computeXY(params.String(), g.Bytes(), U, S, msk)
    mpk := ABEmpk{
            g:   g.Bytes(),
            X_k: X_k,
            Y_k: Y_k,
    }
}

func computeXY(params string, g_ []byte, U []string, S [][]string, msk ABEmsk) (Xik, Yik [][]string) {
	pairing, _ := pbc.NewPairingFromString(params)
	g := pairing.NewG1().SetBytes(g_)

	X := make([][]string, len(U))
	Y := make([][]string, len(U))
	p := pairing.NewGT().Pair(g, g)
	for i := 0; i < len(U); i++ {
		for j := 0; j < len(S[i]); j++ {
			i_j := strconv.Itoa(i) + strconv.Itoa(j)
			x_j := i_j + pairing.NewZr().SetBytes(msk.x).String()

			x_j_zr := pairing.NewZr().SetFromStringHash(x_j, sha256.New())
			x_j_zr = pairing.NewZr().Neg(x_j_zr)
			X_i_k := pairing.NewG1().PowZn(g, x_j_zr)
			X[i] = append(X[i], X_i_k.String())

			y_j := i_j + pairing.NewZr().SetBytes(msk.y).String()
			y_j_zr := pairing.NewZr().SetFromStringHash(y_j, sha256.New())
			Y_i_k := pairing.NewGT().PowZn(p, y_j_zr)
			Y[i] = append(Y[i], Y_i_k.String())
		}
	}

	return X, Y
}

ABE KeyGen

  • LL 表示用户拥有的属性列表。对于属性列表中的每个属性的值,AA随机选择一个值 skRZpsk \in_R \mathbb{Z}_p^* 来绑定属性与其对应的值。H3H_3 是一个防碰撞哈希函数:ZpG\mathbb{Z}_p^* \to \mathbb{G}
  • 对于属性列表 LL 中的第 ii 个属性和第 kik_i 个值,其对应的属性密钥 SKL=sk,{σiˉ}SK_L=\left \langle sk, \{\bar{\sigma_i}\} \right \rangle。其中 σiˉ=σi,ki=gH0(yiki)H3(sk)H0(xiki)\bar{\sigma_i}=\sigma_{i,k_i}=g^{H_0(y||i||k_i)}H_3(sk)^{H_0(x||i||k_i)}

KeyGen代码实现

func AttrKeyGen(params string, g_ []byte, msk ABEmsk, n int, userAttr map[string][]int) ([]byte, [][]strin) {
	// fmt.Println("ABE Attributes Key Gen")
	pairing, _ := pbc.NewPairingFromString(params)
	g := pairing.NewG1().SetBytes(g_)
	x := pairing.NewZr().SetBytes(msk.x)
	y := pairing.NewZr().SetBytes(msk.y)
        
	sk := pairing.NewZr().Rand()
	h3_sk_g := pairing.NewG1().SetFromStringHash(sk.String(), sha256.New())

	sigma_keys := make([][]string, n)
	i := 0
	for _, v := range userAttr {
		for j := 0; j < len(v); j++ {
			x_j := strconv.Itoa(i) + strconv.Itoa(v[j]) + x.String()
			y_j := strconv.Itoa(i) + strconv.Itoa(v[j]) + y.String()

			xjk_zr := pairing.NewZr().SetFromStringHash(x_j, sha256.New())
			yjk_zr := pairing.NewZr().SetFromStringHash(y_j, sha256.New())

			tmp1 := pairing.NewG1().PowZn(g, yjk_zr)
			tmp2 := pairing.NewG1().PowZn(h3_sk_g, xjk_zr)
			sigma_key := pairing.NewG1().Mul(tmp1, tmp2)
			sigma_keys[i] = append(sigma_keys[i], sigma_key.String())
		}
		i += 1
	}
	return sk.Bytes(), sigma_keys
}

ABE ENC

  • 由于需要将密文存储的区块链上,所以为了节约存储开销,将密文设置为固定大小。
  • 首先将所有的属性和对应的值整合到一个访问策略 A=iIAAi\mathbb{A}=\bigwedge_{i \in \mathcal{I}_{\mathbb{A}}}\mathbb{A}_i 中。
  • 计算 XA,YA=iIAXˉi,iIAYˉi\left \langle X_{\mathbb{A}}, Y_{\mathbb{A}} \right \rangle = \left \langle \prod\limits_{i \in \mathcal{I}_{\mathbb{A}}} \bar{X}_i, \prod\limits_{i \in \mathcal{I}_{\mathbb{A}}} \bar{Y}_i \right \rangle,其中 Xˉi,Yˉi=Xi,k,Yi,k\left \langle \bar{X}_i, \bar{Y}_i \right \rangle=\left \langle X_{i,k}, Y_{i,k} \right \rangle.
  • 在文献[1]中,作者使用AES密钥 kidk_{id} 来加解密文件,并保护文件ID idid
  • Health Center (HC) 选择一个 sRZps \in_R \mathbb{Z}_p^*, 并计算 cid=ABE.Enc(mpk,idkid,A)=A,C0,C1,C2c_{id}=ABE.Enc(mpk, id||k_{id}, \mathbb{A})=\left \langle \mathbb{A}, C_0, C_1, C_2 \right \rangle。其中 C0=(idkid)YAsC_0=(id||k_{id})\cdot Y_{\mathbb{A}}^sC1=gsC_1=g^sC2=XAsC_2=X_{\mathbb{A}}^s

ENC代码实现

func ABEenc(params string, g_ []byte, mpk ABEmpk, policA map[string][]int, U []string, msg_ string) (polic map[string][]int, c0, c1, c2 []byte) {
	pairing, _ := pbc.NewPairingFromString(params)
	g := pairing.NewG1().SetBytes(g_)
	s := pairing.NewZr().Rand()
        
	msg_el, _ := pairing.NewGT().SetString(msg_, 0)

	X_A := pairing.NewG1().Set1()
	Y_A := pairing.NewGT().Set1()

	for k, v := range policA {
		index := indexOf(U, k)
		x_i := mpk.X_k[index]
		y_i := mpk.Y_k[index]

		for j := 0; j < len(v); j++ {
			k_i := v[j]
			xi_el, ok := pairing.NewG1().SetString(x_i[k_i], 0)
			if !ok {
				fmt.Println("error set string!")
			}
			yi_el, ok := pairing.NewGT().SetString(y_i[k_i], 0)
			if !ok {
				fmt.Println("error set string!")
			}

			X_A = pairing.NewG1().Mul(X_A, xi_el)
			Y_A = pairing.NewGT().Mul(Y_A, yi_el)
		}
	}

	Y_A_s := pairing.NewGT().PowZn(Y_A, s)
	c0_el := pairing.NewGT().Mul(msg_el, Y_A_s)
	c1_el := pairing.NewG1().PowZn(g, s)
	c2_el := pairing.NewG1().PowZn(X_A, s)
	polic = policA
	return polic, c0_el.Bytes(), c1_el.Bytes(), c2_el.Bytes()
}

ABE DEC

  • 解密过程,类似解密过程,私钥的整合通过 σA=iIAσˉi\sigma_{\mathbb{A}}=\prod\limits_{i\in \mathcal{I}_{\mathbb{A}}} \bar{\sigma}_i, 其中 σˉi\bar{\sigma}_i 是用户的属性列表 SKLSK_L
  • idkid=C0e^(σA,C1)e^(H3(sk),C2)id||k_{id}=\frac{C_0}{\hat{e}(\sigma_{\mathbb{A}}, C_1)\cdot\hat{e}(H_3(sk), C_2)}C0,C1,C2C_0, C_1, C_2对应的就是如上所述的密文。

DEC代码实现

func ABEdec(params string, c0_, c1_, c2_, sk_ []byte, sigma_keys_ [][]string) (plaintext string) {
	pairing, _ := pbc.NewPairingFromString(params)
	c0 := pairing.NewGT().SetBytes(c0_)
	c1 := pairing.NewG1().SetBytes(c1_)
	c2 := pairing.NewG1().SetBytes(c2_)

	sk := pairing.NewZr().SetBytes(sk_).String()
	sk_h3_el := pairing.NewG1().SetFromStringHash(sk, sha256.New())

	tmp1 := pairing.NewGT().Pair(sk_h3_el, c2)

	sigma_A := pairing.NewG1().Set1()
	for _, sigma_key := range sigma_keys_ {
		for j := 0; j < len(sigma_key); j++ {
			sigma_key_el, _ := pairing.NewG1().SetString(sigma_key[j], 0)
			sigma_A = pairing.NewG1().Mul(sigma_A, sigma_key_el)
		}
	}

	tmp2 := pairing.NewGT().Pair(sigma_A, c1)

	tmp3 := pairing.NewGT().Mul(tmp1, tmp2)

	plain_msg_el := pairing.NewGT().Div(c0, tmp3)\
	plaintext = plain_msg_el.String()
	return

}

程序运行结果展示

image.png

参考文献

[1] M. Wang, Y. Guo, C. Zhang, C. Wang, H. Huang and X. Jia, "MedShare: A Privacy-Preserving Medical Data Sharing System by Using Blockchain," in IEEE Transactions on Services Computing, doi: 10.1109/TSC.2021.3114719.
[2] 李发根,吴威峰著. 基于配对的密码学[M]. 北京:科学出版社, 2014