Esempio n. 1
0
 def test_round_trip_json(self):
     as_json = self.payload.json()
     payload = EncryptedPayload.from_json(as_json)
     self.assertEquals(IV, payload.iv)
     self.assertEquals(CIPHERTEXT, payload.ciphertext)
     self.assertEquals(KEY, payload.key)
     self.assertEquals(ORBIT_REGION, payload.key_region)
     self.assertEquals(ENCODING, payload.encoding)
Esempio n. 2
0
 def test_round_trip_dynamodb(self):
     dynamodb_item = self.payload.dynamodb_item()
     payload = EncryptedPayload.from_dynamodb_item(dynamodb_item)
     self.assertEquals(IV, payload.iv)
     self.assertEquals(CIPHERTEXT, payload.ciphertext)
     self.assertEquals(KEY, payload.key)
     self.assertEquals(ORBIT_REGION, payload.key_region)
     self.assertEquals(ENCODING, payload.encoding)
Esempio n. 3
0
 def test_set_password_existing(self):
     self.dynamodb.get_item.return_value = {
         'Item': (EncryptedPayload('a', 'b', 'c', ORBIT_REGION,
                                   'utf-8').dynamodb_item())
     }
     was_set = self.password.set_password(self.app_region, PASSWORD_NAME,
                                          lambda: PASSWORD)
     self.assertFalse(was_set)
     self.dynamodb.put_item.assert_not_called()
Esempio n. 4
0
 def test_from_json(self):
     payload = EncryptedPayload.from_json('''{
         "ciphertext": "MDAwMDAwMDAwMDAwMDAwMA==",
         "encoding": "utf-8",
         "iv": "MDAwMDAwMDAwMDAwMDAwMA==",
         "key": "MDAwMDAwMDAwMDAwMDAwMA==",
         "key_region": "us-east-1"
     }''')
     self.assertTestPayload(payload)
Esempio n. 5
0
    def get_password(self, app_region, label, length=64, generate=True):
        """
        Get a password for an application in a region.
        :param app_region:  Application configuration in region.
        :param label: Password label.
        :param length: Password length.
        :param generate: Generate new password if missing.
        :return: Encrypted password.
        """
        app_name = app_region.app.name
        region = app_region.region
        logger.debug('Getting password for %s in %s.', label, app_name)
        table_name = '%s-passwords' % app_region.app.orbit.name
        password_name = '%s:%s' % (app_name, label)

        dynamodb = self._clients.dynamodb(region)
        existing_item = dynamodb.get_item(TableName=table_name,
                                          Key={
                                              'name': {
                                                  'S': password_name
                                              }
                                          }).get('Item')
        if existing_item:
            logger.debug('Found existing password for %s in %s.', label,
                         app_name)
            encrypted = EncryptedPayload.from_dynamodb_item(existing_item)

            def decrypt_func():
                """Decrypt via KMS."""
                return self._kms_crypt.decrypt_payload(encrypted)

            return encrypted, decrypt_func

        if not generate:
            return None, lambda: None

        # Not found, generate:
        logger.debug('Generating password for %s in %s.', label, app_name)
        plaintext = self._generate_password(length)
        encrypted_payload = self._kms_crypt.encrypt(app_region, plaintext)
        password_item = encrypted_payload.dynamodb_item()
        password_item['name'] = {'S': password_name}

        # Persist encrypted password:
        dynamodb.put_item(
            TableName=table_name,
            Item=password_item,
            ConditionExpression='attribute_not_exists(ciphertext)')

        # Plaintext can be returned _once_ without Decrypt() permission
        return encrypted_payload, lambda: plaintext
Esempio n. 6
0
    def get_password(self, app_region, label, length=64, generate=True):
        """
        Get a password for an application in a region.
        :param app_region:  Application configuration in region.
        :param label: Password label.
        :param length: Password length.
        :param generate: Generate new password if missing.
        :return: Encrypted password.
        """
        app_name = app_region.app.name
        region = app_region.region
        logger.debug('Getting password for %s in %s.', label, app_name)
        table_name = '%s-passwords' % app_region.app.orbit.name
        password_name = '%s:%s' % (app_name, label)

        dynamodb = self._clients.dynamodb(region)
        existing_item = dynamodb.get_item(
            TableName=table_name,
            Key={'name': {'S': password_name}}
        ).get('Item')
        if existing_item:
            logger.debug('Found existing password for %s in %s.', label,
                         app_name)
            encrypted = EncryptedPayload.from_dynamodb_item(existing_item)

            def decrypt_func():
                """Decrypt via KMS."""
                return self._kms_crypt.decrypt_payload(encrypted)

            return encrypted, decrypt_func

        if not generate:
            return None, lambda: None

        # Not found, generate:
        logger.debug('Generating password for %s in %s.', label, app_name)
        plaintext = self._generate_password(length)
        encrypted_payload = self._kms_crypt.encrypt(app_region, plaintext)
        password_item = encrypted_payload.dynamodb_item()
        password_item['name'] = {'S': password_name}

        # Persist encrypted password:
        dynamodb.put_item(
            TableName=table_name,
            Item=password_item,
            ConditionExpression='attribute_not_exists(ciphertext)')

        # Plaintext can be returned _once_ without Decrypt() permission
        return encrypted_payload, lambda: plaintext
Esempio n. 7
0
    def encrypt(self, app_region, plaintext, create_key=True):
        """
        Encrypt data for an application.
        :param app_region:  SpaceAppRegion.
        :param plaintext: Plaintext blob.
        :param create_key: Create key if missing (else fail).
        :return: EncryptedPayload.
        """
        region = app_region.region
        # Get DEK:
        logger.debug('Fetching fresh data key...')
        try:
            alias_name = self._kms_key.get_key_alias(app_region)
            kms = self._clients.kms(region)
            data_key = kms.generate_data_key(KeyId=alias_name,
                                             KeySpec='AES_256')
        except ClientError as e:
            e_message = e.response['Error'].get('Message', '')
            if create_key and 'is not found' in e_message:
                # Key not found, create and try again:
                self._kms_key.create_key(app_region)
                return self.encrypt(app_region, plaintext)
            raise e

        # Encode and pad data:
        encoding = 'bytes'
        if isinstance(plaintext, six.string_types):
            encoding = 'utf-8'
            if six.PY3:  # pragma: no cover
                plaintext = bytes(plaintext, encoding)
            else:  # pragma: no cover
                plaintext = plaintext.encode(encoding)
        pad_length = BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE)
        padded = plaintext + (pad_length * bchr(pad_length))
        logger.debug('Padded %s %s to %s.', len(plaintext), encoding,
                     len(padded))

        logger.debug('Encrypting data with data key...')
        iv = self._random.read(BLOCK_SIZE)

        cipher = AES.new(data_key['Plaintext'], CIPHER_MODE, iv)
        ciphertext = cipher.encrypt(padded)

        encrypted_key = data_key['CiphertextBlob']
        return EncryptedPayload(iv, ciphertext, encrypted_key, region,
                                encoding)
Esempio n. 8
0
    def test_get_password_existing(self):
        self.dynamodb.get_item.return_value = {
            'Item': (EncryptedPayload('a', 'b', 'c', ORBIT_REGION,
                                      'utf-8').dynamodb_item())
        }

        encrypted, decrypt_func = self.password.get_password(
            self.app_region, PASSWORD_NAME)
        self.assertIsInstance(encrypted, EncryptedPayload)
        self.assertEquals(encrypted.iv, 'a')
        self.dynamodb.put_item.assert_not_called()
        self.kms_crypto.encrypt.assert_not_called()

        # Decrypt is deferred until absolutely necessary:
        self.kms_crypto.decrypt_payload.assert_not_called()
        decrypt_func()
        self.kms_crypto.decrypt_payload.assert_called_with(ANY)
Esempio n. 9
0
 def test_from_json_invalid_json(self):
     payload = EncryptedPayload.from_json('{}')
     self.assertIsNone(payload)
Esempio n. 10
0
 def test_from_json_not_json(self):
     payload = EncryptedPayload.from_json('meow')
     self.assertIsNone(payload)
Esempio n. 11
0
import unittest

from spacel.security.payload import EncryptedPayload
from test import ORBIT_REGION

IV = b'0000000000000000'
CIPHERTEXT = b'0000000000000000'
KEY = b'0000000000000000'
ENCODING = 'utf-8'
ENCRYPTED_PAYLOAD = EncryptedPayload(IV, CIPHERTEXT, KEY, ORBIT_REGION,
                                     ENCODING)


class TestEncryptedPayload(unittest.TestCase):
    def setUp(self):
        self.payload = ENCRYPTED_PAYLOAD

    def test_round_trip_dynamodb(self):
        dynamodb_item = self.payload.dynamodb_item()
        payload = EncryptedPayload.from_dynamodb_item(dynamodb_item)
        self.assertEquals(IV, payload.iv)
        self.assertEquals(CIPHERTEXT, payload.ciphertext)
        self.assertEquals(KEY, payload.key)
        self.assertEquals(ORBIT_REGION, payload.key_region)
        self.assertEquals(ENCODING, payload.encoding)

    def test_round_trip_json(self):
        as_json = self.payload.json()
        payload = EncryptedPayload.from_json(as_json)
        self.assertEquals(IV, payload.iv)
        self.assertEquals(CIPHERTEXT, payload.ciphertext)
Esempio n. 12
0
from spacel.security.payload import EncryptedPayload

IV = b'0000000000000000'
CIPHERTEXT = b'0000000000000000'
KEY = b'0000000000000000'
ENCODING = 'utf-8'
REGION = 'us-east-1'

PAYLOAD = EncryptedPayload(IV, CIPHERTEXT, KEY, REGION, ENCODING)
Esempio n. 13
0
 def test_json_round_trip(self):
     as_json = PAYLOAD.json()
     self.assertIsInstance(as_json, str)
     from_json = EncryptedPayload.from_json(as_json)
     self.assertTestPayload(from_json)