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
Exemple #3
0
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,
            )