def test_client_no_region_name_without_default(self):
     test = KMSMasterKeyProvider()
     with six.assertRaisesRegex(
             self, UnknownRegionError,
             'No default region found and no region determinable from key id: *'
     ):
         test._client('')
Exemple #2
0
 def test_client_no_region_name_with_default(self, mock_add_client):
     test = KMSMasterKeyProvider()
     test.default_region = sentinel.default_region
     test._regional_clients[sentinel.default_region] = sentinel.default_client
     client = test._client("")
     assert client is sentinel.default_client
     mock_add_client.assert_called_with(sentinel.default_region)
Exemple #3
0
 def test_client_no_region_name_without_default(self):
     test = KMSMasterKeyProvider()
     with pytest.raises(UnknownRegionError) as excinfo:
         test._client("")
     excinfo.match(
         "No default region found and no region determinable from key id: *"
     )
 def test_add_regional_clients_from_list(self, mock_add_client):
     test = KMSMasterKeyProvider()
     test.add_regional_clients_from_list(
         [sentinel.region_a, sentinel.region_b, sentinel.region_c])
     mock_add_client.assert_has_calls(
         (call(sentinel.region_a), call(sentinel.region_b),
          call(sentinel.region_c)))
def setup_kms_master_key_provider():
    """Reads the test_values config file and builds the requested KMS Master Key Provider."""
    config = read_test_config()
    cmk_arn = get_cmk_arn(config)
    botocore_session = setup_botocore_session(config)
    kms_master_key_provider = KMSMasterKeyProvider(botocore_session=botocore_session)
    kms_master_key_provider.add_master_key(cmk_arn)
    return kms_master_key_provider
Exemple #6
0
 def test_new_master_key(self, mock_client):
     mock_client.return_value = self.mock_boto3_client_instance
     key_info = 'example key info asdf'
     test = KMSMasterKeyProvider()
     key = test._new_master_key(key_info)
     check_key = KMSMasterKey(key_id=key_info,
                              client=self.mock_boto3_client_instance)
     assert key == check_key
 def test_client_valid_region_name(self, mock_add_client):
     test = KMSMasterKeyProvider()
     test._regional_clients['us-east-1'] = self.mock_boto3_client_instance
     client = test._client(
         'arn:aws:kms:us-east-1:222222222222:key/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb'
     )
     mock_add_client.assert_called_once_with('us-east-1')
     assert client is self.mock_boto3_client_instance
Exemple #8
0
 def test_init_with_default_region_not_found(self, mock_add_regional_client):
     test = KMSMasterKeyProvider(botocore_session=self.botocore_no_region_session)
     assert test.default_region is None
     with patch.object(test.config.botocore_session, "get_config_variable", return_value=None) as mock_get_config:
         test._process_config()
         mock_get_config.assert_called_once_with("region")
         assert test.default_region is None
         assert not mock_add_regional_client.called
Exemple #9
0
 def test_new_master_key(self, mock_client):
     """v1.2.4 : master key equality is left to the Python object identity now"""
     mock_client.return_value = self.mock_boto3_client_instance
     key_info = "example key info asdf"
     test = KMSMasterKeyProvider()
     key = test._new_master_key(key_info)
     check_key = KMSMasterKey(key_id=key_info, client=self.mock_boto3_client_instance)
     assert key != check_key
Exemple #10
0
def test_remove_bad_client():
    test = KMSMasterKeyProvider()
    test.add_regional_client("us-fakey-12")

    with pytest.raises(BotoCoreError):
        test._regional_clients["us-fakey-12"].list_keys()

    assert not test._regional_clients
 def test_add_regional_client_new(self):
     test = KMSMasterKeyProvider()
     test._regional_clients = {}
     test.add_regional_client('ex_region_name')
     self.mock_boto3_session.assert_called_once_with(
         region_name='ex_region_name', botocore_session=ANY)
     self.mock_boto3_session_instance.client.assert_called_once_with('kms')
     assert test._regional_clients[
         'ex_region_name'] is self.mock_boto3_client_instance
def test_remove_bad_client():
    test = KMSMasterKeyProvider()
    fake_region = "us-fakey-12"
    test.add_regional_client(fake_region)

    with pytest.raises(BotoCoreError):
        test._regional_clients[fake_region].list_keys()

    assert fake_region not in test._regional_clients
Exemple #13
0
 def test_add_regional_client_new(self):
     test = KMSMasterKeyProvider()
     test._regional_clients = {}
     test.add_regional_client("ex_region_name")
     self.mock_boto3_session.assert_called_with(
         region_name="ex_region_name", botocore_session=ANY)
     self.mock_boto3_session_instance.client.assert_called_with(
         "kms", config=test._user_agent_adding_config)
     assert test._regional_clients[
         "ex_region_name"] is self.mock_boto3_client_instance
 def test_init_with_default_region_found(self, mock_add_regional_client):
     test = KMSMasterKeyProvider()
     assert test.default_region is None
     with patch.object(
             test.config.botocore_session,
             'get_config_variable',
             return_value=sentinel.default_region) as mock_get_config:
         test._process_config()
         mock_get_config.assert_called_once_with('region')
         assert test.default_region is sentinel.default_region
         mock_add_regional_client.assert_called_once_with(
             sentinel.default_region)
def setup_kms_master_key_provider(cache=True):
    """Build an AWS KMS Master Key Provider."""
    global _KMS_MKP  # pylint: disable=global-statement
    if cache and _KMS_MKP is not None:
        return _KMS_MKP

    cmk_arn = get_cmk_arn()
    kms_master_key_provider = KMSMasterKeyProvider()
    kms_master_key_provider.add_master_key(cmk_arn.encode("utf-8"))

    if cache:
        _KMS_MKP = kms_master_key_provider

    return kms_master_key_provider
def setup_kms_master_key_provider_with_botocore_session(cache=True):
    """Build an AWS KMS Master Key Provider with an explicit botocore_session."""
    global _KMS_MKP_BOTO  # pylint: disable=global-statement
    if cache and _KMS_MKP_BOTO is not None:
        return _KMS_MKP_BOTO

    cmk_arn = get_cmk_arn()
    kms_master_key_provider = KMSMasterKeyProvider(botocore_session=botocore.session.Session())
    kms_master_key_provider.add_master_key(cmk_arn.encode("utf-8"))

    if cache:
        _KMS_MKP_BOTO = kms_master_key_provider

    return kms_master_key_provider
Exemple #17
0
def setup_kms_master_key_provider_with_botocore_session(cache=True):
    """Reads the test_values config file and builds the requested KMS Master Key Provider with botocore_session."""
    global _KMS_MKP_BOTO  # pylint: disable=global-statement
    if cache and _KMS_MKP_BOTO is not None:
        return _KMS_MKP_BOTO

    cmk_arn = get_cmk_arn()
    kms_master_key_provider = KMSMasterKeyProvider(botocore_session=botocore.session.Session())
    kms_master_key_provider.add_master_key(cmk_arn)

    if cache:
        _KMS_MKP_BOTO = kms_master_key_provider

    return kms_master_key_provider
def setup_kms_master_key_provider(cache=True):
    """Reads the test_values config file and builds the requested KMS Master Key Provider."""
    global _KMS_MKP  # pylint: disable=global-statement
    if cache and _KMS_MKP is not None:
        return _KMS_MKP

    cmk_arn = get_cmk_arn()
    kms_master_key_provider = KMSMasterKeyProvider()
    kms_master_key_provider.add_master_key(cmk_arn)

    if cache:
        _KMS_MKP = kms_master_key_provider

    return kms_master_key_provider
def kms_master_key_provider(cache: Optional[bool] = True):
    """Build the expected KMS Master Key Provider based on environment variables."""
    global _KMS_MKP  # pylint: disable=global-statement

    if cache and _KMS_MKP is not None:
        return _KMS_MKP

    cmk_arn = get_cmk_arn()
    _kms_master_key_provider = KMSMasterKeyProvider()
    _kms_master_key_provider.add_master_key(cmk_arn)

    if cache:
        _KMS_MKP = _kms_master_key_provider

    return _kms_master_key_provider
Exemple #20
0
def run(aws_kms_cmk, source_plaintext):
    # type: (str, bytes) -> None
    """Demonstrate configuring an AWS KMS master key provider for decryption.

    :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
    :param bytes source_plaintext: Plaintext to encrypt
    """
    # Prepare your encryption context.
    # Remember that your encryption context is NOT SECRET.
    # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
    encryption_context = {
        "encryption": "context",
        "is not": "secret",
        "but adds": "useful metadata",
        "that can help you": "be confident that",
        "the data you are handling": "is what you think it is",
    }

    # Create the master key that determines how your data keys are protected.
    encrypt_master_key = KMSMasterKey(key_id=aws_kms_cmk)

    # Create an AWS KMS master key provider to use on decrypt.
    decrypt_master_key_provider = KMSMasterKeyProvider()

    # Encrypt your plaintext data.
    ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
        source=source_plaintext,
        encryption_context=encryption_context,
        key_provider=encrypt_master_key)

    # Demonstrate that the ciphertext and plaintext are different.
    assert ciphertext != source_plaintext

    # Decrypt your encrypted data using the AWS KMS master key provider.
    #
    # You do not need to specify the encryption context on decrypt
    # because the header of the encrypted message includes the encryption context.
    decrypted, decrypt_header = aws_encryption_sdk.decrypt(
        source=ciphertext, key_provider=decrypt_master_key_provider)

    # Demonstrate that the decrypted plaintext is identical to the original plaintext.
    assert decrypted == source_plaintext

    # Verify that the encryption context used in the decrypt operation includes
    # the encryption context that you specified when encrypting.
    # The AWS Encryption SDK can add pairs, so don't require an exact match.
    #
    # In production, always use a meaningful encryption context.
    assert set(encryption_context.items()) <= set(
        decrypt_header.encryption_context.items())
Exemple #21
0
def configure_key_provider(graph, key_ids):
    """
    Configure a key provider.

    During unit tests, use a static key provider (e.g. without AWS calls).

    """
    if graph.metadata.testing:
        # use static provider
        provider = StaticMasterKeyProvider()
        provider.add_master_keys_from_list(key_ids)
        return provider

    # use AWS provider
    return KMSMasterKeyProvider(key_ids=key_ids)
Exemple #22
0
def run(aws_kms_cmk, aws_kms_additional_cmks, source_plaintext):
    # type: (str, Sequence[str], bytes) -> None
    """Demonstrate how to create a keyring that behaves like an AWS KMS master key provider.

    :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
    :param List[str] aws_kms_additional_cmks: Additional ARNs of secondary AWS KMS CMKs
    :param bytes source_plaintext: Plaintext to encrypt
    """
    # Prepare your encryption context.
    # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
    encryption_context = {
        "encryption": "context",
        "is not": "secret",
        "but adds": "useful metadata",
        "that can help you": "be confident that",
        "the data you are handling": "is what you think it is",
    }

    # This is the master key provider whose behavior we want to reproduce.
    #
    # When encrypting, this master key provider generates the data key using the first CMK in the list
    # and encrypts the data key using all specified CMKs.
    # However, when decrypting, this master key provider attempts to decrypt
    # any data keys that were encrypted under an AWS KMS CMK.
    master_key_provider_cmks = [aws_kms_cmk] + aws_kms_additional_cmks
    _master_key_provider_to_replicate = KMSMasterKeyProvider(  # noqa: intentionally never used
        key_ids=master_key_provider_cmks, )

    # Create a CMK keyring that encrypts and decrypts using the specified AWS KMS CMKs.
    #
    # This keyring reproduces the encryption behavior of the AWS KMS master key provider.
    #
    # The AWS KMS keyring requires that you explicitly identify the CMK
    # that you want the keyring to use to generate the data key.
    cmk_keyring = AwsKmsKeyring(generator_key_id=aws_kms_cmk,
                                key_ids=aws_kms_additional_cmks)

    # Create an AWS KMS discovery keyring that will attempt to decrypt
    # any data keys that were encrypted under an AWS KMS CMK.
    discovery_keyring = AwsKmsKeyring(is_discovery=True)

    # Combine the CMK and discovery keyrings
    # to create a keyring that behaves like an AWS KMS master key provider.
    #
    # The CMK keyring reproduces the encryption behavior
    # and the discovery keyring reproduces the decryption behavior.
    # This also means that it does not matter if the CMK keyring fails to decrypt.
    # For example, if you configured the CMK keyring with aliases,
    # it works on encrypt but fails to match any encrypted data keys on decrypt
    # because the serialized key name is the resulting CMK ARN rather than the alias name.
    # However, because the discovery keyring attempts to decrypt any AWS KMS-encrypted
    # data keys that it finds, the message still decrypts successfully.
    keyring = MultiKeyring(generator=cmk_keyring, children=[discovery_keyring])

    # Encrypt your plaintext data.
    ciphertext, _encrypt_header = aws_encryption_sdk.encrypt(
        source=source_plaintext,
        encryption_context=encryption_context,
        keyring=keyring)

    # Demonstrate that the ciphertext and plaintext are different.
    assert ciphertext != source_plaintext

    # Decrypt your encrypted data using the same keyring you used on encrypt.
    #
    # You do not need to specify the encryption context on decrypt
    # because the header of the encrypted message includes the encryption context.
    decrypted, decrypt_header = aws_encryption_sdk.decrypt(source=ciphertext,
                                                           keyring=keyring)

    # Demonstrate that the decrypted plaintext is identical to the original plaintext.
    assert decrypted == source_plaintext

    # Verify that the encryption context used in the decrypt operation includes
    # the encryption context that you specified when encrypting.
    # The AWS Encryption SDK can add pairs, so don't require an exact match.
    #
    # In production, always use a meaningful encryption context.
    assert set(encryption_context.items()) <= set(
        decrypt_header.encryption_context.items())
def run(aws_kms_cmk, source_plaintext):
    # type: (str, bytes) -> None
    """Demonstrate configuring a master key provider to use an AWS KMS CMK and an RSA wrapping key.

    :param str aws_kms_cmk: The ARN of an AWS KMS CMK that protects data keys
    :param bytes source_plaintext: Plaintext to encrypt
    """
    # Prepare your encryption context.
    # Remember that your encryption context is NOT SECRET.
    # https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
    encryption_context = {
        "encryption": "context",
        "is not": "secret",
        "but adds": "useful metadata",
        "that can help you": "be confident that",
        "the data you are handling": "is what you think it is",
    }

    # Generate an RSA private key to use with your master key.
    # In practice, you should get this key from a secure key management system such as an HSM.
    #
    # The National Institute of Standards and Technology (NIST) recommends a minimum of 2048-bit keys for RSA.
    # https://www.nist.gov/publications/transitioning-use-cryptographic-algorithms-and-key-lengths
    #
    # Why did we use this public exponent?
    # https://crypto.stanford.edu/~dabo/pubs/papers/RSA-survey.pdf
    private_key = rsa.generate_private_key(public_exponent=65537,
                                           key_size=4096,
                                           backend=default_backend())

    # Serialize the RSA private key to PEM encoding.
    # This or DER encoding is likely to be what you get from your key management system in practice.
    private_key_pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption(),
    )

    # Collect the public key from the private key.
    public_key = private_key.public_key()

    # Serialize the RSA public key to PEM encoding.
    # This or DER encoding is likely to be what you get from your key management system in practice.
    public_key_pem = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )

    # Create the encrypt master key that only has access to the public key.
    escrow_encrypt_master_key = RawMasterKey(
        # The provider ID and key ID are defined by you
        # and are used by the raw RSA master key
        # to determine whether it should attempt to decrypt
        # an encrypted data key.
        provider_id=
        "some managed raw keys",  # provider ID corresponds to key namespace for keyrings
        key_id=
        b"my RSA wrapping key",  # key ID corresponds to key name for keyrings
        wrapping_key=WrappingKey(
            wrapping_key=public_key_pem,
            wrapping_key_type=EncryptionKeyType.PUBLIC,
            # The wrapping algorithm tells the raw RSA master key
            # how to use your wrapping key to encrypt data keys.
            #
            # We recommend using RSA_OAEP_SHA256_MGF1.
            # You should not use RSA_PKCS1 unless you require it for backwards compatibility.
            wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1,
        ),
    )

    # Create the decrypt master key that has access to the private key.
    escrow_decrypt_master_key = RawMasterKey(
        # The key namespace and key name MUST match the encrypt master key.
        provider_id=
        "some managed raw keys",  # provider ID corresponds to key namespace for keyrings
        key_id=
        b"my RSA wrapping key",  # key ID corresponds to key name for keyrings
        wrapping_key=WrappingKey(
            wrapping_key=private_key_pem,
            wrapping_key_type=EncryptionKeyType.PRIVATE,
            # The wrapping algorithm MUST match the encrypt master key.
            wrapping_algorithm=WrappingAlgorithm.RSA_OAEP_SHA256_MGF1,
        ),
    )

    # Create the AWS KMS master key that you will use for decryption during normal operations.
    kms_master_key = KMSMasterKeyProvider(key_ids=[aws_kms_cmk])

    # Add the escrow encrypt master key to the AWS KMS master key.
    kms_master_key.add_master_key_provider(escrow_encrypt_master_key)

    # Encrypt your plaintext data using the combined master keys.
    ciphertext, encrypt_header = aws_encryption_sdk.encrypt(
        source=source_plaintext,
        encryption_context=encryption_context,
        key_provider=kms_master_key)

    # Verify that the header contains the expected number of encrypted data keys (EDKs).
    # It should contain one EDK for AWS KMS and one for the escrow key.
    assert len(encrypt_header.encrypted_data_keys) == 2

    # Demonstrate that the ciphertext and plaintext are different.
    assert ciphertext != source_plaintext

    # Decrypt your encrypted data separately using the AWS KMS master key and the escrow decrypt master key.
    #
    # You do not need to specify the encryption context on decrypt
    # because the header of the encrypted message includes the encryption context.
    decrypted_kms, decrypt_header_kms = aws_encryption_sdk.decrypt(
        source=ciphertext, key_provider=kms_master_key)
    decrypted_escrow, decrypt_header_escrow = aws_encryption_sdk.decrypt(
        source=ciphertext, key_provider=escrow_decrypt_master_key)

    # Demonstrate that the decrypted plaintext is identical to the original plaintext.
    assert decrypted_kms == source_plaintext
    assert decrypted_escrow == source_plaintext

    # Verify that the encryption context used in the decrypt operation includes
    # the encryption context that you specified when encrypting.
    # The AWS Encryption SDK can add pairs, so don't require an exact match.
    #
    # In production, always use a meaningful encryption context.
    assert set(encryption_context.items()) <= set(
        decrypt_header_kms.encryption_context.items())
    assert set(encryption_context.items()) <= set(
        decrypt_header_escrow.encryption_context.items())
 def test_init_with_region_names(self, mock_add_clients):
     region_names = (sentinel.region_name_1, sentinel.region_name_2)
     test = KMSMasterKeyProvider(region_names=region_names)
     mock_add_clients.assert_called_once_with(region_names)
     assert test.default_region is sentinel.region_name_1
 def test_init_with_key_ids(self, mock_add_keys):
     mock_ids = (sentinel.id_1, sentinel.id_2)
     KMSMasterKeyProvider(key_ids=mock_ids)
     mock_add_keys.assert_called_once_with(mock_ids)
 def test_init_bare(self, mock_process_config):
     KMSMasterKeyProvider()
     mock_process_config.assert_called_once_with()
Exemple #27
0
 def test_add_regional_client_exists(self):
     test = KMSMasterKeyProvider(
         botocore_session=self.botocore_no_region_session)
     test._regional_clients["ex_region_name"] = sentinel.existing_client
     test.add_regional_client("ex_region_name")
     assert not self.mock_boto3_session.called
Exemple #28
0
def test_init_with_regionless_key_ids_and_region_names():
    key_ids = ("alias/key_1", )
    region_names = ("test-region-1", )
    provider = KMSMasterKeyProvider(region_names=region_names, key_ids=key_ids)
    assert provider.master_key(
        "alias/key_1").config.client.meta.region_name == region_names[0]
 def test_add_regional_client_exists(self):
     test = KMSMasterKeyProvider()
     test._regional_clients['ex_region_name'] = sentinel.existing_client
     test.add_regional_client('ex_region_name')
     assert not self.mock_boto3_session.called
Exemple #30
0
def _master_key_provider() -> KMSMasterKeyProvider:
    """Build the V0 master key provider."""
    master_key_provider = KMSMasterKeyProvider()
    master_key_provider.add_master_key_provider(NullMasterKey())
    master_key_provider.add_master_key_provider(CountingMasterKey())
    return master_key_provider