mirror of
https://github.com/NationalSecurityAgency/PACE-python.git
synced 2026-11-09 17:35:37 -10:00
343 lines
13 KiB
Python
343 lines
13 KiB
Python
## **************
|
|
## Copyright 2015 MIT Lincoln Laboratory
|
|
## Project: PACE
|
|
## Authors: ATLH
|
|
## Description: Framework for demoing the encryption code
|
|
## Modifications:
|
|
## Date Name Modification
|
|
## ---- ---- ------------
|
|
## 09 Apr 2015 ATLH Original file
|
|
## **************
|
|
|
|
import os
|
|
import sys
|
|
this_dir = os.path.dirname(os.path.dirname(__file__))
|
|
base_dir = os.path.join(this_dir, '../..')
|
|
sys.path.append(base_dir)
|
|
|
|
import pyaccumulo
|
|
from StringIO import StringIO
|
|
import base64
|
|
from Crypto.PublicKey import RSA
|
|
import subprocess
|
|
|
|
from pace.encryption.acc_encrypt import AccumuloEncrypt, Encryptor
|
|
from pace.encryption.encryption_pki import DummyEncryptionPKI, EncryptionPKIAccumulo
|
|
from pace.encryption.AES_encrypt import Pycrypto_AES_CFB
|
|
from pace.encryption.encryption_exceptions import DecryptionException
|
|
|
|
from pace.pki.keygen import KeyGen
|
|
from pace.pki.accumulo_keystore import AccumuloKeyStore, AccumuloAttrKeyStore
|
|
from pace.pki.frontend import KeyStoreFrontEnd
|
|
|
|
ABS_PATH = os.path.dirname(__file__)
|
|
DEMO_USERS = ABS_PATH + '../pki/user_info.cfg'
|
|
PRIV_KEYS = ABS_PATH + '../pki/'
|
|
|
|
# Default elements to use
|
|
expressive_elems = [('MITLL', '(a&b)|c','Analytics','Rooms','1200'),
|
|
('MITLL', '(a&b)|c','Analytics','Employees','4000'),
|
|
('Boston', '(a&b)|c','Analytics','Population','3000000'),
|
|
('Cambridge', '(a&b)|c','Analytics','Population','400000')]
|
|
|
|
#Tuples of accumulo elements: row, vis, colFam, colQual, and value
|
|
vis_elems = [('Analytics', 'a','','','120'),
|
|
('Average', 'a&b','','','200'),
|
|
('Median', '(a&b)|c','','','160'),
|
|
('Total', '(a&b)|(c&d)|(b&d)|(b&c)','','','320')]
|
|
|
|
new_vis_elems = [('Sum', 'c&d','','','1200'),
|
|
('Minimum', 'b','','','120')]
|
|
|
|
empty_vis = [('Sum', '','','','1200')]
|
|
descriptions = {'expressive' : "Row contains: Row encrypted with Pycrypto_AES_SIV\n"+\
|
|
"ColFamily contains: ColFamily & ColQualifier encrypted with Pycrypto_AES_CFB\n"+\
|
|
"ColQualifier contains: Nothing\n"+\
|
|
"Value contains: Value encrypted with AES_GCM\n",
|
|
'identity' : "Row contains: Row unecrypted\n"+\
|
|
"ColFamily contains: ColFamily & ColQualifier concatentated\n"+\
|
|
"ColQualifier contains: Nothing\n"+\
|
|
"Value contains: Value unencrypted \n",
|
|
'vis' : "Value contains: Value encrypted with VIS_AES_CFB",
|
|
'print' : "",
|
|
'vis_print':'',
|
|
'none': "Currently using no encryption scheme"}
|
|
|
|
schema = { 'expressive' : '[row]\n'+\
|
|
'key_id = Pycrypto_AES_SIV\n'+\
|
|
'encryption = Pycrypto_AES_SIV\n'+\
|
|
'[colFamily]\n'+\
|
|
'key_id = Pycrypto_AES_CFB\n'+\
|
|
'cell_sections = colFamily,colQualifier\n'+\
|
|
'encryption = Pycrypto_AES_CFB\n'+\
|
|
'[value]\n'+\
|
|
'key_id = Pycrypto_AES_CFB\n'+\
|
|
'encryption = Pycrypto_AES_GCM',
|
|
'identity': '[row]\n'+\
|
|
'key_id = Identity\n'+\
|
|
'encryption = Identity\n'+\
|
|
'[colFamily]\n'+\
|
|
'key_id = Identity\n'+\
|
|
'cell_sections = colFamily,colQualifier\n'+\
|
|
'encryption = Identity\n'+\
|
|
'[value]\n'+\
|
|
'key_id = Identity\n'+\
|
|
'encryption = Identity',
|
|
'vis' : '[value]\n'+\
|
|
'key_id = VIS_AES_CFB\n'+\
|
|
'cell_key_length = 32\n'+\
|
|
'encryption = VIS_AES_CFB',
|
|
'none': '[row]\n'+\
|
|
'key_id = Identity\n'+\
|
|
'encryption = Identity\n',
|
|
'print':'[row]\n'+\
|
|
'key_id = Identity\n'+\
|
|
'encryption = Identity\n',
|
|
'vis_print':'[row]\n'+\
|
|
'key_id = Identity\n'+\
|
|
'encryption = Identity\n'}
|
|
|
|
|
|
def mutation_from_kv_tuple(tup):
|
|
""" Define a pyaccumulo mutation from a (key, value) formatted tuple.
|
|
"""
|
|
row, cv, cf, cq, val = tup
|
|
print 'Inserting entry: \n Row - %s,\n Column_Visibility - %s,\n Column_Family - %s,\n Column_Qualifier - %s,\n Value - %s' % (row,cv,cf,cq,val)
|
|
m = pyaccumulo.Mutation(row)
|
|
m.put(cf=cf, cq=cq, cv=cv, val=val)
|
|
return m
|
|
|
|
def grant_auths(table='demo', user='user3'):
|
|
"""
|
|
Grants read/write priviledges for the user
|
|
for the necessary tables in the demo, also sets
|
|
user's attributes to a,c,d,e
|
|
"""
|
|
print "Setting ", user, "'s attributes to a,c,d,e"
|
|
_ = subprocess.call("/usr/local/accumulo-1.7.0/bin/accumulo shell -u root -p secret -e 'setauths -u "+user+" -s a,c,d,e'", stdout=subprocess.PIPE, shell=True)
|
|
|
|
tables = ['__VERSION_METADATA__','__KEYWRAP_METADATA__',"VIS_AES_CFB"]
|
|
tables.append(table)
|
|
|
|
print "Granting read/write access to tables:"
|
|
for tab in tables:
|
|
print " Table ", tab
|
|
_ = subprocess.call("/usr/local/accumulo-1.7.0/bin/accumulo shell -u root -p secret -e 'grant Table.READ -t "+tab+" -u "+user+"'", stdout=subprocess.PIPE, shell=True)
|
|
_ = subprocess.call("/usr/local/accumulo-1.7.0/bin/accumulo shell -u root -p secret -e 'grant Table.WRITE -t "+tab+" -u "+user+"'", stdout=subprocess.PIPE, shell=True)
|
|
|
|
def write_list_to_table(conn, encrypter, table, data):
|
|
""" Write a list to an Accumulo table.
|
|
Arguments:
|
|
conn - a pyaccumulo connection to an Accumulo instance
|
|
encrypter - an AccumuloEncrypt instance
|
|
table - the name of the table to write to
|
|
data - the list to write to the table, formatted as a list of
|
|
(key, cf, cq, value) tuples
|
|
"""
|
|
|
|
if not conn.table_exists(table):
|
|
conn.create_table(table)
|
|
|
|
wr = conn.create_batch_writer(table)
|
|
|
|
for tup in data:
|
|
m = mutation_from_kv_tuple(tup)
|
|
enc_mut = encrypter.encrypt(m)
|
|
for mut in enc_mut:
|
|
wr.add_mutation(mut)
|
|
|
|
wr.close()
|
|
|
|
def run_insert(conn=None,
|
|
data=expressive_elems,
|
|
table='demo',
|
|
config_name = 'expressive',
|
|
PKI_object=None):
|
|
""" Insert a list into the accumulo table specified.
|
|
Arguments:
|
|
conn - the Accumulo connection to use
|
|
data - the list to insert into the Accumulo table
|
|
table - the name of the table to insert to
|
|
default_vis - the default visibility label to use. default: '(a&b)|c'
|
|
config_filept - file pointer to the configuration file for encryption
|
|
PKI_object - matches the interface on encryption PKI OBJECT, default is
|
|
DummyEncryptionPKI
|
|
"""
|
|
if conn is None:
|
|
conn = pyaccumulo.Accumulo(host='localhost',
|
|
port=42424,
|
|
user='root',
|
|
password='secret')
|
|
if PKI_object is None:
|
|
PKI_object = DummyEncryptionPKI(conn=conn)
|
|
|
|
config_filept = StringIO(schema[config_name])
|
|
encrypter = AccumuloEncrypt(config_filept, PKI_object)
|
|
print "\nEnrypting with the following schema: \n\n" + descriptions[config_name]
|
|
write_list_to_table(conn, encrypter, table, data)
|
|
|
|
def run_retrieve(conn=None,
|
|
table='demo',
|
|
config_name = 'expressive',
|
|
PKI_object=None):
|
|
""" Retrieves and decrypts values from the specified table,
|
|
outputting the appropriate error messages if not.
|
|
Arguments:
|
|
conn - the Accumulo connection to use
|
|
data - the list to insert into the Accumulo table
|
|
table - the name of the table to insert to
|
|
config_filept - file pointer to the configuration file for encryption
|
|
PKI_object - matches the interface on encryption PKI OBJECT, default is
|
|
DummyEncryptionPKI
|
|
"""
|
|
if conn is None:
|
|
conn = pyaccumulo.Accumulo(host='localhost',
|
|
port=42424,
|
|
user='root',
|
|
password='secret')
|
|
if PKI_object is None:
|
|
PKI_object = DummyEncryptionPKI(conn=conn)
|
|
total = 0
|
|
config_filept = StringIO(schema[config_name])
|
|
decrypter = AccumuloEncrypt(config_filept, PKI_object)
|
|
|
|
for entry in conn.scan(table):
|
|
total = total + 1
|
|
try:
|
|
cell = decrypter.decrypt(entry)
|
|
if config_name == 'print':
|
|
print 'Entry: \n Row - %s,\n Column_Visibility - %s,\n Column_Family - %s,\n Column_Qualifier - %s,\n Value - %s\n' %(base64.b64encode(cell.row),
|
|
cell.cv,
|
|
base64.b64encode(cell.cf),
|
|
base64.b64encode(cell.cq),
|
|
base64.b64encode(cell.val))
|
|
elif config_name == 'vis_print':
|
|
print 'Entry: \n Row - %s,\n Column_Visibility - %s,\n Column_Family - %s,\n Column_Qualifier - %s,\n Value - %s\n' %(cell.row,
|
|
cell.cv,
|
|
cell.cf,
|
|
cell.cq,
|
|
base64.b64encode(cell.val))
|
|
else:
|
|
print 'Entry: \n Row - %s,\n Column_Visibility - %s,\n Column_Family - %s,\n Column_Qualifier - %s,\n Value - %s\n' % (cell.row, cell.cv,cell.cf,cell.cq,cell.val)
|
|
except DecryptionException as ve:
|
|
print 'Error: Entry failed to decrypt.'
|
|
print 'Error message:', ve.msg
|
|
print
|
|
|
|
print 'Finished, decrypted %d total entries.' %(total)
|
|
|
|
def run_search(conn=None,
|
|
table='demo',
|
|
config_name = 'expressive',
|
|
PKI_object=None,
|
|
row_range='Analytics'):
|
|
""" Retrieves and decrypts values from the specified table,
|
|
outputting the appropriate error messages if not.
|
|
Arguments:
|
|
conn - the Accumulo connection to use
|
|
data - the list to insert into the Accumulo table
|
|
table - the name of the table to insert to
|
|
config_filept - file pointer to the configuration file for encryption
|
|
PKI_object - matches the interface on encryption PKI OBJECT, default is
|
|
DummyEncryptionPKI
|
|
row_range - the keyword to search for
|
|
"""
|
|
if conn is None:
|
|
conn = pyaccumulo.Accumulo(host='localhost',
|
|
port=42424,
|
|
user='root',
|
|
password='secret')
|
|
if PKI_object is None:
|
|
PKI_object = DummyEncryptionPKI(conn=conn)
|
|
|
|
total = 0
|
|
enc_config_filept = StringIO(schema[config_name])
|
|
dec_config_filept = StringIO(schema[config_name])
|
|
encrypter = AccumuloEncrypt(enc_config_filept, PKI_object)
|
|
enc_row, _ = encrypter.encrypt_search(row_range, None)
|
|
range = pyaccumulo.Range(srow = enc_row, sinclude = True,
|
|
erow = enc_row, einclude = True)
|
|
|
|
|
|
|
|
for entry in conn.scan(table, scanrange=range):
|
|
total = total + 1
|
|
try:
|
|
cell = encrypter.decrypt(entry)
|
|
print "Entry: (%s, %s, %s, %s)" % (cell.row, cell.cf, cell.cq, cell.val)
|
|
except DecryptionException as ve:
|
|
print 'Error: Entry failed to decrypt.'
|
|
print 'Error message:', ve.msg
|
|
print
|
|
|
|
print 'Finished, decrypted %d total entries.' %(total)
|
|
|
|
|
|
def run_pki_insert(genpath=DEMO_USERS, user='user1', conn=None):
|
|
""" Insert elements into the PKI as specified by the config
|
|
file located at genpath.
|
|
|
|
Returns:
|
|
|
|
ks - the keystore object initialized this way
|
|
"""
|
|
|
|
if conn is None:
|
|
print 'Logging in to Accumulo...'
|
|
conn = pyaccumulo.Accumulo(host='localhost', port=42424,
|
|
user='root', password='secret')
|
|
|
|
print 'Logged in. Creating key store...'
|
|
print 'Deleting existing metadata tables'
|
|
for table_name in ['__VERSION_METADATA__','__ATTR_TABLE__','__KEYWRAP_METADATA__']:
|
|
if conn.table_exists(table_name):
|
|
print 'Deleting table ', table_name
|
|
conn.delete_table(table_name)
|
|
ks = AccumuloAttrKeyStore(conn)
|
|
print 'Key store created.'
|
|
print
|
|
|
|
master_secret = 'master secret key'
|
|
|
|
print 'Initializing key store with dummy master secret "master secret key"...'
|
|
|
|
|
|
|
|
kg = KeyGen(master_secret)
|
|
users = KeyGen.file_to_dict(genpath)
|
|
for _, infos in users.itervalues():
|
|
for _, _, metadata, _ in infos:
|
|
if conn.table_exists(metadata):
|
|
print ' Found existing table:', metadata
|
|
print ' Deleting to have a clean slate.'
|
|
conn.delete_table(metadata)
|
|
|
|
kg.initialize_users(users, ks)
|
|
|
|
#get private keys to unwrap things
|
|
f = open(PRIV_KEYS+user+'_privkey.pem','r')
|
|
priv_key = RSA.importKey(f.read())
|
|
|
|
print 'Key store initialized.'
|
|
return EncryptionPKIAccumulo(conn, user, priv_key), KeyStoreFrontEnd(ks)
|
|
|
|
def run_revoke(user, attr, genpath=DEMO_USERS, conn=None):
|
|
|
|
if conn is None:
|
|
conn = pyaccumulo.Accumulo(host='localhost', port=42424,
|
|
user='root', password='secret')
|
|
|
|
ks = AccumuloAttrKeyStore(conn)
|
|
|
|
master_secret = 'master secret key'
|
|
kg = KeyGen(master_secret)
|
|
|
|
users = KeyGen.file_to_dict(genpath)
|
|
|
|
user_keys = {}
|
|
for k_user, k_tuple in users.iteritems():
|
|
user_keys[k_user] = k_tuple[0]
|
|
|
|
print "Revoking user %s's access to attribute %s." %(user, attr)
|
|
kg.revoke(user, attr, ks, ks, ks, user_keys)
|