'''
Shashank Agrawal, Melissa Chase
| From: "FAME: Fast Attribute-based Message Encryption"
| Published in: 2017
| Available from: https://eprint.iacr.org/2017/807
| Notes: Implemented the scheme in Section 3
| Security Assumption: a variant of k-linear, k>=2
|
| type: ciphertext-policy attribute-based encryption
| setting: Pairing
:Authors: Shashank Agrawal
:Date: 05/2016
'''
from charm.toolbox.pairinggroup import PairingGroup, ZR, G1, G2, GT, pair
from charm.toolbox.ABEnc import ABEnc
from charm.toolbox.msp import MSP
debug = False
[docs]class AC17CPABE(ABEnc):
def __init__(self, group_obj, assump_size, verbose=False):
ABEnc.__init__(self)
self.group = group_obj
self.assump_size = assump_size # size of linear assumption, at least 2
self.util = MSP(self.group, verbose)
[docs] def setup(self):
"""
Generates public key and master secret key.
"""
if debug:
print('\nSetup algorithm:\n')
# generate two instances of the k-linear assumption
A = []
B = []
for i in range(self.assump_size):
A.append(self.group.random(ZR))
B.append(self.group.random(ZR)) # note that A, B are vectors here
# vector
k = []
for i in range(self.assump_size + 1):
k.append(self.group.random(ZR))
# pick a random element from the two source groups and pair them
g = self.group.random(G1)
h = self.group.random(G2)
e_gh = pair(g, h)
# now compute various parts of the public parameters
# compute the [A]_2 term
h_A = []
for i in range(self.assump_size):
h_A.append(h ** A[i])
h_A.append(h)
# compute the e([k]_1, [A]_2) term
g_k = []
for i in range(self.assump_size + 1):
g_k.append(g ** k[i])
e_gh_kA = []
for i in range(self.assump_size):
e_gh_kA.append(e_gh ** (k[i] * A[i] + k[self.assump_size]))
# the public key
pk = {'h_A': h_A, 'e_gh_kA': e_gh_kA}
# the master secret key
msk = {'g': g, 'h': h, 'g_k': g_k, 'A': A, 'B': B}
return pk, msk
[docs] def keygen(self, pk, msk, attr_list):
"""
Generate a key for a list of attributes.
"""
if debug:
print('\nKey generation algorithm:\n')
# pick randomness
r = []
sum = 0
for i in range(self.assump_size):
rand = self.group.random(ZR)
r.append(rand)
sum += rand
# compute the [Br]_2 term
# first compute just Br as it will be used later too
Br = []
for i in range(self.assump_size):
Br.append(msk['B'][i] * r[i])
Br.append(sum)
# now compute [Br]_2
K_0 = []
for i in range(self.assump_size + 1):
K_0.append(msk['h'] ** Br[i])
# compute [W_1 Br]_1, ...
K = {}
A = msk['A']
g = msk['g']
for attr in attr_list:
key = []
sigma_attr = self.group.random(ZR)
for t in range(self.assump_size):
prod = 1
a_t = A[t]
for l in range(self.assump_size + 1):
input_for_hash = attr + str(l) + str(t)
prod *= (self.group.hash(input_for_hash, G1) ** (Br[l]/a_t))
prod *= (g ** (sigma_attr/a_t))
key.append(prod)
key.append(g ** (-sigma_attr))
K[attr] = key
# compute [k + VBr]_1
Kp = []
g_k = msk['g_k']
sigma = self.group.random(ZR)
for t in range(self.assump_size):
prod = g_k[t]
a_t = A[t]
for l in range(self.assump_size + 1):
input_for_hash = '01' + str(l) + str(t)
prod *= (self.group.hash(input_for_hash, G1) ** (Br[l] / a_t))
prod *= (g ** (sigma / a_t))
Kp.append(prod)
Kp.append(g_k[self.assump_size] * (g ** (-sigma)))
return {'attr_list': attr_list, 'K_0': K_0, 'K': K, 'Kp': Kp}
[docs] def encrypt(self, pk, msg, policy_str):
"""
Encrypt a message msg under a policy string.
"""
if debug:
print('\nEncryption algorithm:\n')
policy = self.util.createPolicy(policy_str)
mono_span_prog = self.util.convert_policy_to_msp(policy)
num_cols = self.util.len_longest_row
# pick randomness
s = []
sum = 0
for i in range(self.assump_size):
rand = self.group.random(ZR)
s.append(rand)
sum += rand
# compute the [As]_2 term
C_0 = []
h_A = pk['h_A']
for i in range(self.assump_size):
C_0.append(h_A[i] ** s[i])
C_0.append(h_A[self.assump_size] ** sum)
# compute the [(V^T As||U^T_2 As||...) M^T_i + W^T_i As]_1 terms
# pre-compute hashes
hash_table = []
for j in range(num_cols):
x = []
input_for_hash1 = '0' + str(j + 1)
for l in range(self.assump_size + 1):
y = []
input_for_hash2 = input_for_hash1 + str(l)
for t in range(self.assump_size):
input_for_hash3 = input_for_hash2 + str(t)
hashed_value = self.group.hash(input_for_hash3, G1)
y.append(hashed_value)
# if debug: print ('Hash of', i+2, ',', j2, ',', j1, 'is', hashed_value)
x.append(y)
hash_table.append(x)
C = {}
for attr, row in mono_span_prog.items():
ct = []
attr_stripped = self.util.strip_index(attr) # no need, re-use not allowed
for l in range(self.assump_size + 1):
prod = 1
cols = len(row)
for t in range(self.assump_size):
input_for_hash = attr_stripped + str(l) + str(t)
prod1 = self.group.hash(input_for_hash, G1)
for j in range(cols):
# input_for_hash = '0' + str(j+1) + str(l) + str(t)
prod1 *= (hash_table[j][l][t] ** row[j])
prod *= (prod1 ** s[t])
ct.append(prod)
C[attr] = ct
# compute the e(g, h)^(k^T As) . m term
Cp = 1
for i in range(self.assump_size):
Cp = Cp * (pk['e_gh_kA'][i] ** s[i])
Cp = Cp * msg
return {'policy': policy, 'C_0': C_0, 'C': C, 'Cp': Cp}
[docs] def decrypt(self, pk, ctxt, key):
"""
Decrypt ciphertext ctxt with key key.
"""
if debug:
print('\nDecryption algorithm:\n')
nodes = self.util.prune(ctxt['policy'], key['attr_list'])
if not nodes:
print ("Policy not satisfied.")
return None
prod1_GT = 1
prod2_GT = 1
for i in range(self.assump_size + 1):
prod_H = 1
prod_G = 1
for node in nodes:
attr = node.getAttributeAndIndex()
attr_stripped = self.util.strip_index(attr) # no need, re-use not allowed
# prod_H *= key['K'][attr_stripped][i] ** coeff[attr]
# prod_G *= ctxt['C'][attr][i] ** coeff[attr]
prod_H *= key['K'][attr_stripped][i]
prod_G *= ctxt['C'][attr][i]
prod1_GT *= pair(key['Kp'][i] * prod_H, ctxt['C_0'][i])
prod2_GT *= pair(prod_G, key['K_0'][i])
return ctxt['Cp'] * prod2_GT / prod1_GT