diff --git a/buildspec.yml b/buildspec.yml index 583fae56f..4665ac89e 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -296,7 +296,28 @@ batch: buildspec: codebuild/py312/decrypt_keyrings_with_js.yml env: image: aws/codebuild/standard:7.0 - + - identifier: py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/generate_hkeyring_decrypt_vectors.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_hkeyring_with_masterkey + depend-on: + - py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/decrypt_hkeyring_with_masterkey.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_hkeyring_with_keyrings + depend-on: + - py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/decrypt_hkeyring_with_keyrings.yml + env: + image: aws/codebuild/standard:7.0 + - identifier: py312_decrypt_hkeyring_with_net + depend-on: + - py312_generate_hkeyring_decrypt_vectors + buildspec: codebuild/py312/decrypt_hkeyring_with_net.yml + env: + image: aws/codebuild/standard:7.0 - identifier: code_coverage buildspec: codebuild/coverage/coverage.yml diff --git a/codebuild/py312/decrypt_hkeyring_with_keyrings.yml b/codebuild/py312/decrypt_hkeyring_with_keyrings.yml new file mode 100644 index 000000000..5bcd26738 --- /dev/null +++ b/codebuild/py312/decrypt_hkeyring_with_keyrings.yml @@ -0,0 +1,32 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + # This manifest has coverage for both HKeyring and required encryption context CMM + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest.zip + - unzip 312_hkeyring_reccmm_manifest.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_hkeyring_reccmm_manifest/manifest.json \ + --keyrings \ No newline at end of file diff --git a/codebuild/py312/decrypt_hkeyring_with_masterkey.yml b/codebuild/py312/decrypt_hkeyring_with_masterkey.yml new file mode 100644 index 000000000..be67235d7 --- /dev/null +++ b/codebuild/py312/decrypt_hkeyring_with_masterkey.yml @@ -0,0 +1,31 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + # This manifest has coverage for both HKeyring and required encryption context CMM + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest.zip + - unzip 312_hkeyring_reccmm_manifest.zip + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers + - | + tox -- \ + --input ../312_hkeyring_reccmm_manifest/manifest.json diff --git a/codebuild/py312/decrypt_hkeyring_with_net.yml b/codebuild/py312/decrypt_hkeyring_with_net.yml new file mode 100644 index 000000000..6a3b321eb --- /dev/null +++ b/codebuild/py312/decrypt_hkeyring_with_net.yml @@ -0,0 +1,50 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b35311ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + pre_build: + commands: + # Download previously generated vectors + # This manifest has coverage for both HKeyring and required encryption context CMM + - aws s3 cp s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest.zip + - unzip 312_hkeyring_reccmm_manifest.zip + - export DAFNY_AWS_ESDK_TEST_VECTOR_MANIFEST_PATH="${PWD}/312_hkeyring_reccmm_manifest/manifest.json" + + # Download dafny + - curl https://github.com/dafny-lang/dafny/releases/download/v4.7.0/dafny-4.7.0-x64-ubuntu-20.04.zip -L -o dafny.zip + - unzip -qq dafny.zip && rm dafny.zip + - export PATH="$PWD/dafny:$PATH" + + # Clone SDK-Dafny repo to get test vectors runner + - git clone --recurse-submodules https://github.com/aws/aws-encryption-sdk-dafny.git + # TODO: Change branch to published when available + - cd aws-encryption-sdk-dafny + - git checkout lucmcdon/hkeyring-vectors + - git pull + - cd AwsEncryptionSDK/ + - make transpile_net + - cd ../mpl/TestVectorsAwsCryptographicMaterialProviders/ + - make transpile_net + + # Change TestVectors to reference the published .NET ESDK + - cd ../../AwsEncryptionSDK/runtimes/net/TestVectorsNative/TestVectors + # - sed -i -e 's///g' AWSEncryptionSDKTestVectorLib.csproj + # - cd ../TestVectors + + build: + commands: + - dotnet test --framework net6.0 \ No newline at end of file diff --git a/codebuild/py312/generate_hkeyring_decrypt_vectors.yml b/codebuild/py312/generate_hkeyring_decrypt_vectors.yml new file mode 100644 index 000000000..b0a755360 --- /dev/null +++ b/codebuild/py312/generate_hkeyring_decrypt_vectors.yml @@ -0,0 +1,33 @@ +version: 0.2 + +env: + variables: + TOXENV: "py312-full_decrypt_generate-mpl" + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID: >- + arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_KEY_ID_2: >- + arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_1: >- + arn:aws:kms:us-west-2:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + AWS_ENCRYPTION_SDK_PYTHON_INTEGRATION_TEST_AWS_KMS_MRK_KEY_ID_2: >- + arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7 + +phases: + install: + runtime-versions: + python: 3.12 + build: + commands: + - pip install "tox < 4.0" + - cd test_vector_handlers/test/aws-crypto-tools-test-vector-framework + # Checkout WIP branch with manifest containing HKeyring and required EC CMM test cases + - git checkout lucmcdon/hierarchy-test-vectors + - git pull + - cd ../.. + - | + tox -- \ + --input test/aws-crypto-tools-test-vector-framework/features/CANONICAL-GENERATED-MANIFESTS/0007-hkeyring-reccmm-generate-manifest.json \ + --output 312_hkeyring_reccmm_manifest \ + --keyrings + - zip -r 312_hkeyring_reccmm_manifest.zip 312_hkeyring_reccmm_manifest + - aws s3 cp 312_hkeyring_reccmm_manifest.zip s3://generated-vectors-artifacts-bucket/$CODEBUILD_RESOLVED_SOURCE_VERSION/312_hkeyring_reccmm_manifest.zip diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py index c2a233ca6..79df4f2cf 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt.py @@ -225,7 +225,7 @@ class MessageDecryptionTestScenario(object): master_key_provider_fn = attr.ib(validator=attr.validators.is_callable()) result = attr.ib(validator=attr.validators.instance_of(MessageDecryptionTestResult)) keyrings = attr.ib(validator=attr.validators.instance_of(bool)) - cmm_type = attr.ib(validator=attr.validators.instance_of(str)) + cmm_type = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(str))) decryption_method = attr.ib( default=None, validator=attr.validators.optional(attr.validators.instance_of(DecryptionMethod)) ) @@ -292,6 +292,7 @@ def from_scenario( else: master_key_specs = [ MasterKeySpec.from_scenario(spec) for spec in raw_master_key_specs + if spec["type"] != "aws-kms-hierarchy" ] def master_key_provider_fn(): @@ -310,7 +311,8 @@ def master_key_provider_fn(): encryption_context = {} # MPL test vectors add CMM types to the test vectors manifests - if "cmm" in scenario: + if "cmm" in scenario \ + and scenario["cmm"] is not None: if scenario["cmm"] == "Default": # Master keys and keyrings can handle default CMM cmm_type = scenario["cmm"] @@ -323,11 +325,17 @@ def master_key_provider_fn(): else: return None else: - raise ValueError("Unrecognized cmm_type: " + cmm_type) + raise ValueError("Unrecognized cmm_type: " + scenario["cmm"]) else: # If unspecified, set "Default" as the default cmm_type = "Default" + # If this scenario does not have any key providers, + # do not create a scenario. + # Caller logic should expect `None` to mean "no scenario". + if master_key_provider_fn() is None: + return None + return cls( ciphertext_uri=scenario["ciphertext"], ciphertext=ciphertext_reader(scenario["ciphertext"]), @@ -358,6 +366,9 @@ def scenario_spec(self): spec["decryption-method"] = self.decryption_method.value if self.description is not None: spec["description"] = self.description + spec["cmm"] = self.cmm_type + spec["encryption-context"] = self.encryption_context + return spec def _one_shot_decrypt(self): diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py index 93510d891..63f8b8ff3 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/decrypt_generation.py @@ -81,7 +81,7 @@ # We only actually need these imports when running the mypy checks pass -SUPPORTED_VERSIONS = (2,) +SUPPORTED_VERSIONS = (2, 4, ) class TamperingMethod: @@ -410,6 +410,8 @@ class MessageDecryptionTestScenarioGenerator(object): decryption_master_key_provider_fn = attr.ib(validator=attr.validators.is_callable()) result = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(MessageDecryptionTestResult))) keyrings = attr.ib(validator=attr.validators.instance_of(bool)) + cmm_type = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(str))) + encryption_context = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(dict))) @classmethod def from_scenario(cls, scenario, keys, plaintexts, keyrings, keys_uri): @@ -432,6 +434,10 @@ def from_scenario(cls, scenario, keys, plaintexts, keyrings, keys_uri): keyrings, keys_uri, ) + + if encryption_scenario is None: + return None + tampering = scenario.get("tampering") tampering_method = TamperingMethod.from_tampering_spec(tampering) decryption_method_spec = scenario.get("decryption-method") @@ -457,6 +463,16 @@ def decryption_master_key_provider_fn(): result_spec = scenario.get("result") result = MessageDecryptionTestResult.from_result_spec(result_spec, None) if result_spec else None + try: + encryption_context = encryption_scenario_spec["encryption-context"] + except KeyError: + encryption_context = None + + try: + cmm_type = encryption_scenario_spec["cmm"] + except KeyError: + cmm_type = None + return cls( encryption_scenario=encryption_scenario, tampering_method=tampering_method, @@ -465,6 +481,8 @@ def decryption_master_key_provider_fn(): decryption_master_key_provider_fn=decryption_master_key_provider_fn, result=result, keyrings=keyrings, + cmm_type=cmm_type, + encryption_context=encryption_context, ) def run(self, ciphertext_writer, plaintext_uri): @@ -494,8 +512,8 @@ def decryption_test_scenario_pair(self, ciphertext_writer, ciphertext_to_decrypt decryption_method=self.decryption_method, result=expected_result, keyrings=self.keyrings, - cmm_type="Default", - encryption_context={} + cmm_type=self.cmm_type, + encryption_context=self.encryption_context, ), ) @@ -533,6 +551,7 @@ def _generate_plaintexts(plaintexts_specs): @classmethod def from_file(cls, input_file, keyrings): + # pylint: disable=too-many-locals # type: (IO) -> MessageDecryptionGenerationManifest """Load from a file containing a full message encrypt manifest. diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py index 82c1740de..81581fa8e 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/full_message/encrypt.py @@ -83,6 +83,7 @@ class MessageEncryptionTestScenario(object): master_key_specs = attr.ib(validator=iterable_validator(list, MasterKeySpec)) master_key_provider_fn = attr.ib(validator=attr.validators.is_callable()) keyrings = attr.ib(validator=attr.validators.instance_of(bool)) + cmm = attr.ib(validator=attr.validators.instance_of(str)) @classmethod def from_scenario(cls, scenario, keys, plaintexts, keyrings, keys_uri): @@ -114,6 +115,25 @@ def master_key_provider_fn(): return keyring_from_master_key_specs(keys_uri, master_key_specs, "encrypt") return master_key_provider_from_master_key_specs(keys, master_key_specs) + # MPL test vectors add CMM types to the test vectors manifests + if "cmm" in scenario: + if scenario["cmm"] == "Default": + # Master keys and keyrings can handle default CMM + cmm_type = scenario["cmm"] + elif scenario["cmm"] == "RequiredEncryptionContext": + # Skip RequiredEncryptionContext CMM for master keys; + # RequiredEncryptionContext is unsupported for master keys. + # Caller logic should expect `None` to mean "no scenario". + if keyrings: + cmm_type = scenario["cmm"] + else: + return None + else: + raise ValueError("Unrecognized cmm_type: " + cmm_type) + else: + # If unspecified, set "Default" as the default + cmm_type = "Default" + return cls( plaintext_name=scenario["plaintext"], plaintext=plaintexts[scenario["plaintext"]], @@ -123,6 +143,7 @@ def master_key_provider_fn(): master_key_specs=master_key_specs, master_key_provider_fn=master_key_provider_fn, keyrings=keyrings, + cmm=cmm_type, ) def run(self, materials_manager=None): diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py index 7c55a1617..ce8bf756f 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/keys.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/keys.py @@ -19,6 +19,7 @@ from awses_test_vectors.internal.mypy_types import ( # noqa pylint: disable=unused-import AWS_KMS_KEY_SPEC, + AWS_KMS_HIERARCHY_KEY_SPEC, KEY_SPEC, KEYS_MANIFEST, MANIFEST_VERSION, @@ -100,6 +101,51 @@ def manifest_spec(self): } +@attr.s(init=False) +class AwsKmsHierarchyKeySpec(KeySpec): + """AWS KMS hierarchy key specification. + + :param bool encrypt: Key can be used to encrypt + :param bool decrypt: Key can be used to decrypt + :param str type_name: Master key type name (must be "static-branch-key") + :param str key_id: Branch key ID + """ + + # pylint: disable=too-few-public-methods + + type_name = attr.ib(validator=membership_validator(("static-branch-key",))) + + # noqa pylint: disable=line-too-long,too-many-arguments + def __init__(self, encrypt, decrypt, type_name, key_id, branch_key_version, branch_key, beacon_key): # noqa=D107 + # type: (bool, bool, str, str) -> None + # Workaround pending resolution of attrs/mypy interaction. + # https://github.com/python/mypy/issues/2088 + # https://github.com/python-attrs/attrs/issues/215 + self.type_name = type_name + self.branch_key_version = branch_key_version + self.branch_key = branch_key + self.beacon_key = beacon_key + super(AwsKmsHierarchyKeySpec, self).__init__(encrypt, decrypt, key_id) + + @property + def manifest_spec(self): + # type: () -> AWS_KMS_HIERARCHY_KEY_SPEC + """Build a key specification describing this key specification. + + :return: Key specification JSON + :rtype: dict + """ + return { + "encrypt": self.encrypt, + "decrypt": self.decrypt, + "type": self.type_name, + "key-id": self.key_id, + "branchKeyVersion": self.branch_key_version, + "branchKey": self.branch_key, + "beaconKey": self.beacon_key, + } + + @attr.s(init=False) class ManualKeySpec(KeySpec): # pylint: disable=too-many-arguments @@ -196,8 +242,22 @@ def key_from_manifest_spec(key_spec): key_id = key_spec["key-id"] # type: str return AwsKmsKeySpec(encrypt=encrypt, decrypt=decrypt, type_name=type_name, key_id=key_id) - algorithm = key_spec["algorithm"] # type: str + if key_spec["type"] == "static-branch-key": + branch_key_version = key_spec["branchKeyVersion"] # type: str + branch_key = key_spec["branchKey"] # type: str + beacon_key = key_spec["beaconKey"] # type: str + return AwsKmsHierarchyKeySpec( + encrypt=encrypt, + decrypt=decrypt, + type_name=type_name, + key_id=key_id, + branch_key_version=branch_key_version, + branch_key=branch_key, + beacon_key=beacon_key, + ) + bits = key_spec["bits"] # type: int + algorithm = key_spec["algorithm"] encoding = key_spec["encoding"] # type: str material = key_spec["material"] # type: str return ManualKeySpec( diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py index 49b517eae..7e64f9983 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/master_key.py @@ -34,7 +34,7 @@ # We only actually need these imports when running the mypy checks pass -KNOWN_TYPES = ("aws-kms", "aws-kms-mrk-aware", "aws-kms-mrk-aware-discovery", "raw") +KNOWN_TYPES = ("aws-kms", "aws-kms-mrk-aware", "aws-kms-mrk-aware-discovery", "raw", ) KNOWN_ALGORITHMS = ("aes", "rsa") KNOWN_PADDING = ("pkcs1", "oaep-mgf1") KNOWN_PADDING_HASH = ("sha1", "sha256", "sha384", "sha512") @@ -301,7 +301,17 @@ def master_key_provider_from_master_key_specs(keys, master_key_specs): :return: Master key provider combining all loaded master keys :rtype: MasterKeyProvider """ - master_keys = [spec.master_key(keys) for spec in master_key_specs] + master_keys = [] + for spec in master_key_specs: + try: + master_keys.append(spec.master_key(keys)) + # If spec is not a valid master key + # (e.g. hierarchical keyring) + # do not make a master key + except KeyError: + pass + if len(master_keys) == 0: + return None primary = master_keys[0] others = master_keys[1:] for master_key in others: diff --git a/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py b/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py index 566bd7f55..dbabeb3c7 100644 --- a/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py +++ b/test_vector_handlers/src/awses_test_vectors/manifests/mpl_keyring.py @@ -36,10 +36,20 @@ # Ignore pylint not being able to read a module that requires the MPL # pylint: disable=no-name-in-module from awses_test_vectors.internal.mpl.keyvectors_provider import KeyVectorsProvider +from awses_test_vectors.internal.util import membership_validator from awses_test_vectors.manifests.keys import KeysManifest # noqa: disable=F401 +from .master_key import KNOWN_TYPES as MASTER_KEY_KNOWN_TYPES, MasterKeySpec -from .master_key import MasterKeySpec +try: # Python 3.5.0 and 3.5.1 have incompatible typing modules + from typing import Iterable # noqa pylint: disable=unused-import + + from awses_test_vectors.internal.mypy_types import MASTER_KEY_SPEC # noqa pylint: disable=unused-import +except ImportError: # pragma: no cover + # We only actually need these imports when running the mypy checks + pass + +KEYRING_ONLY_KNOWN_TYPES = ("aws-kms-hierarchy", ) @attr.s @@ -56,6 +66,30 @@ class KeyringSpec(MasterKeySpec): # pylint: disable=too-many-instance-attribute :param str padding_hash: Wrapping key padding hash (required for raw master keys) """ + type_name = attr.ib(validator=membership_validator( + set(MASTER_KEY_KNOWN_TYPES).union(set(KEYRING_ONLY_KNOWN_TYPES)) + )) + + @classmethod + def from_scenario(cls, spec): + # type: (MASTER_KEY_SPEC) -> KeyringSpec + """Load from a keyring specification. + + :param dict spec: Master key specification JSON + :return: Loaded master key specification + :rtype: MasterKeySpec + """ + return cls( + type_name=spec["type"], + key_name=spec.get("key"), + default_mrk_region=spec.get("default-mrk-region"), + discovery_filter=cls._discovery_filter_from_spec(spec.get("aws-kms-discovery-filter")), + provider_id=spec.get("provider-id"), + encryption_algorithm=spec.get("encryption-algorithm"), + padding_algorithm=spec.get("padding-algorithm"), + padding_hash=spec.get("padding-hash"), + ) + def keyring(self, keys_uri, mode): # type: (KeysManifest) -> IKeyring """Build a keyring using this specification. @@ -73,8 +107,8 @@ def keyring(self, keys_uri, mode): "key": self.key_name, "provider-id": self.provider_id, "encryption-algorithm": self.encryption_algorithm, - } + if self.padding_algorithm is not None and self.padding_algorithm != "": input_kwargs["padding-algorithm"] = self.padding_algorithm if self.padding_hash is not None: