mirror of
https://github.com/NationalSecurityAgency/PACE-python.git
synced 2026-11-08 17:34:48 -10:00
200 lines
8.0 KiB
Python
200 lines
8.0 KiB
Python
## **************
|
|
## Copyright 2014 MIT Lincoln Laboratory
|
|
## Project: PACE
|
|
## Authors: ATLH
|
|
## Description: High level module for encrypting records
|
|
## Modifications:
|
|
## Date Name Modification
|
|
## ---- ---- ------------
|
|
## 18 Dec 2014 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 ConfigParser
|
|
from StringIO import StringIO
|
|
from collections import namedtuple
|
|
from pace.encryption.vars import VALID_KEYS
|
|
from pace.encryption.enc_classes import ALGORITHMS
|
|
from pace.encryption.enc_mutation import EncMutation, EncCell, EncRange
|
|
|
|
Keytor = namedtuple('Keytor',['key_id','key_object','cell_key_length'])
|
|
"""
|
|
Key container object, used to encapsulate the information needed to obtain a key for a
|
|
particular user and algorithm and/or generate a cell_key if the algorithm is a VIS_*
|
|
key_id - the identifier passed into the key object, usually the algorithm name
|
|
key_object - a handle on an instance of an EncryptionPKI object
|
|
cell_key_length - (int) for VIS_* algorithm, length in bytes
|
|
(not bits) of the cell_key to be generated.
|
|
"""
|
|
|
|
Encryptor = namedtuple('Encryptor', ['encryption', 'cell_sections', 'key_container'])
|
|
"""
|
|
Container object that is used to contain all the information to encrypt a portion of the
|
|
cell.
|
|
encryption - class instance to be used for that particular portion of the cell
|
|
cell_sections - the portions of the cell to encrypt, can be multiple portions
|
|
of the cell
|
|
key_container - contains a (Keytor) object which is used to extract the necessary
|
|
key material
|
|
"""
|
|
|
|
class ConfigurationException(Exception):
|
|
""" Exception raised when unable to process configuration file
|
|
|
|
Attributes:
|
|
msg - error message for situation
|
|
"""
|
|
def __init__(self, msg):
|
|
self.msg = msg
|
|
|
|
def __str__(self):
|
|
return self.msg
|
|
|
|
|
|
class AccumuloEncrypt(object):
|
|
"""High level interface for encrypting/decrypting accumulo cells.
|
|
Parametrized by a configuration file and a separate
|
|
key management object.
|
|
"""
|
|
def __init__(self, config, key_object):
|
|
"""
|
|
Arguments:
|
|
config - configuration file in the format specified
|
|
by INI files, see README for more details.
|
|
key_object - key management object that meets the
|
|
interface outlined in encryption_pki.py
|
|
"""
|
|
self.config_parser = ConfigParser.ConfigParser()
|
|
|
|
#support for StringIO for testing
|
|
if isinstance(config, StringIO):
|
|
self.config_parser.readfp(config)
|
|
else:
|
|
self.config_parser.read(config)
|
|
|
|
self.key_object = key_object
|
|
self.encrypt_dict = self._config_to_encryptor(self.config_parser,
|
|
self.key_object)
|
|
|
|
@staticmethod
|
|
def _config_to_encryptor(config_parser, key_object):
|
|
'''
|
|
Converts configuration file into associated encryptor
|
|
objects for each part of key
|
|
'''
|
|
for sec in config_parser.sections():
|
|
#make sure all sections correspond to part of key
|
|
if sec not in VALID_KEYS:
|
|
raise ConfigurationException(
|
|
"%s is not a valid part of the key" % sec)
|
|
#check to see if encryption specified
|
|
if not config_parser.has_option(sec, 'encryption'):
|
|
raise ConfigurationException(
|
|
"No encryption set for %s" % sec)
|
|
#make sure identifier information for key_object
|
|
if not config_parser.has_option(sec, 'key_id'):
|
|
raise ConfigurationException(
|
|
"There is no key idenfier information for"+
|
|
"key_object")
|
|
#make sure algorithm is supported
|
|
algorithm = config_parser.get(sec,'encryption')
|
|
if algorithm not in ALGORITHMS.keys():
|
|
raise ConfigurationException(
|
|
"%s is not a supported encryption algorithm" % algorithm)
|
|
|
|
|
|
keys = config_parser.sections()
|
|
encryptors = []
|
|
for sec in config_parser.sections():
|
|
#check if cell_sections exists, if not, just use the current section
|
|
if config_parser.has_option(sec,'cell_sections'):
|
|
cell_sections = config_parser.get(sec,'cell_sections').split(',')
|
|
else:
|
|
cell_sections = [sec]
|
|
|
|
#check if cell_key_length has been specified, if not, use 16 bytes
|
|
if config_parser.has_option(sec, 'cell_key_length'):
|
|
cell_key_length = config_parser.get(sec,'cell_key_length')
|
|
try:
|
|
cell_key_length = int(cell_key_length)
|
|
except ValueError:
|
|
raise ConfigurationException('%s is not an valid integer for key_length' % cell_key_length)
|
|
if cell_key_length not in [16,24,32]:
|
|
raise ConfigurationException('Key_length must be 16,24,or 32 bytes long')
|
|
else:
|
|
cell_key_length=16
|
|
|
|
if not all([cell_sec in VALID_KEYS for cell_sec in cell_sections]):
|
|
raise ConfigurationException("At least one of cell_sections ("+\
|
|
str(cell_sections)+\
|
|
") are not a valid part of a cell")
|
|
encryptors.append(Encryptor(ALGORITHMS[config_parser.get(sec,'encryption')],
|
|
cell_sections,
|
|
Keytor(config_parser.get(sec,'key_id'),
|
|
key_object,
|
|
cell_key_length)))
|
|
if keys == [] or encryptors == []:
|
|
raise ConfigurationException("Configuration was not properly parsed")
|
|
|
|
return dict(zip(keys, encryptors))
|
|
|
|
|
|
def encrypt(self, mutation):
|
|
"""
|
|
Arugments:
|
|
mutation - an plaintext mutation as defined in the
|
|
pyaccumulo interface
|
|
|
|
Returns: A list of new mutations containing the encrypted data
|
|
as specified in the configuration file
|
|
"""
|
|
enc_mut = EncMutation(mutation, self.encrypt_dict)
|
|
return enc_mut.encrypt()
|
|
|
|
def encrypt_search(self, row, columns = None):
|
|
'''
|
|
Functionality to help users to search over
|
|
deterministically encrypted data. Can only
|
|
be used to retrieve one row at a time.
|
|
Because order is not guarenteed in the supported
|
|
deterministic modes
|
|
|
|
Arguments:
|
|
row - the row to search for, this will be passed in as both
|
|
the start and end row into 'scan'
|
|
columns - double nested list of what columns
|
|
to look for, where the first element in the nested list is
|
|
the column_family, the second is the column_qualifier.
|
|
It is possible to just specify the column_family.
|
|
Defaults to None. Example:
|
|
[['cf1','cq1'],['cf2']]
|
|
|
|
|
|
Returns: A tuple containing two things:
|
|
1) the encrypted row value if is deterministically encrypted
|
|
2) A similarly formatted list of columns if they
|
|
are deterministically encrypted and the value
|
|
passed in is not None. If the columns parameter
|
|
is None (as in the default case) None is returned
|
|
as the second part of the tuple.
|
|
'''
|
|
return EncRange.encrypt(row, columns, self.encrypt_dict)
|
|
|
|
|
|
def decrypt(self, cell):
|
|
"""
|
|
Arugments:
|
|
mutation - an cell as defined in pyaccumulo
|
|
|
|
Returns: New cell containing the decrypted data
|
|
as specified in the configuration file
|
|
"""
|
|
return EncCell.decrypt(cell, self.encrypt_dict)
|
|
|
|
|