def test_get_digest_canonical( image_config: ImageConfig, image_config_signed: ImageConfig, config_digest_canonical: str, config_digest_signed_canonical: str, ): """Test canonical digest calculation for signed and unsigned configurations.""" assert image_config.get_digest_canonical() == config_digest_canonical assert image_config_signed.get_digest_canonical() == config_digest_signed_canonical
async def test_minimal(): """Test minimal image configuration (for non-conformant labels)k.""" # Note: At a minimum, [Cc]onfig key must exist with non-null value image_config = ImageConfig(b'{"Config":{}}') config_digest_canonical = image_config.get_digest_canonical() signer = FakeSigner() assert await image_config.sign(signer) == signer.signature_value # A signature should always be able to be added ... assert b"BEGIN FAKE SIGNATURE" in image_config.get_bytes() signatures = image_config.get_signature_list() assert len(signatures) == 1 assert signatures[0]["digest"] == config_digest_canonical assert signatures[0]["signature"] == signer.signature_value
async def test_minimal(gnupg_keypair: GnuPGKeypair): """Test minimal image configuration (for non-conformant labels)k.""" signer = GPGSigner( keyid=gnupg_keypair.keyid, passphrase=gnupg_keypair.passphrase, homedir=gnupg_keypair.gnupg_home, ) # Note: At a minimum, [Cc]onfig key must exist with non-null value image_config = ImageConfig(b'{"Config":{}}') config_digest_canonical = image_config.get_digest_canonical() signature = await image_config.sign(signer) assert "PGP SIGNATURE" in signature # A signature should always be able to be added ... assert b"BEGIN PGP SIGNATURE" in image_config.get_bytes() signatures = image_config.get_signature_list() assert len(signatures) == 1 assert signatures[0].digest == config_digest_canonical assert signatures[0].signature == signature
async def test_sign_endorse_recursive(image_config: ImageConfig): """Test interlaced signatures and endorsements.""" # Stack representation of a binary tree stack = [{"name": "?-Unsigned", "image_config": deepcopy(image_config)}] LOGGER.debug("Unsigned Canonical Digest: %s", image_config.get_digest_canonical()) async def append_new_image_config( *, config: ImageConfig, signature_type: SignatureTypes = SignatureTypes.SIGN, iteration, ): action = f"X{signature_type.name}" signer = FakeSigner(f"[{iteration}-{action: <8}: {{0}}]") await config.sign(signer, signature_type) stack.append({"name": f"{iteration}-{action}", "image_config": config}) iterations = 6 # Breadth first traversal ... for i in range(iterations): LOGGER.debug("Iteration %d", i) for _ in range(len(stack)): frame = stack[0] LOGGER.debug(" Checking %s", frame["name"]) # Validate the signature / endorsement permutations of the first entry on the stack ... signatures = frame["image_config"].get_signature_list() flat_list = "".join([signature["signature"] for signature in signatures]) if f"X{SignatureTypes.RESIGN.name}" in flat_list: # Too lazy to calculate how many signatures were removed ... assert len(signatures) <= i else: assert len(signatures) == i for sig, signature in enumerate(signatures): LOGGER.debug(" %s", signature["signature"]) if f"X{SignatureTypes.ENDORSE.name}" in signature["signature"]: # Endorsement digests should include all entities of a lower order. temp = deepcopy(frame["image_config"]) temp.set_signature_list(temp.get_signature_list()[:sig]) assert signature["digest"] == temp.get_digest_canonical() assert temp.get_digest_canonical() in signature["signature"] else: # Signature digests should be independent of the number of signatures. # Re-signed images should always contain 1 signature. assert signature["digest"] == image_config.get_digest_canonical() assert image_config.get_digest_canonical() in signature["signature"] # Unshift the first image configuration, append three more image configurations on to the stack: ... # ... one signed ... await append_new_image_config( config=deepcopy(frame["image_config"]), iteration=i ) # ... one endorsed ... await append_new_image_config( config=deepcopy(frame["image_config"]), signature_type=SignatureTypes.ENDORSE, iteration=i, ) # ... one resigned ... await append_new_image_config( config=stack.pop(0).get("image_config"), signature_type=SignatureTypes.RESIGN, iteration=i, )