Beispiel #1
0
async def test_verify_signatures_manipulated_signatures(
    gnupg_keypair: GnuPGKeypair, image_config: ImageConfig
):
    """Test that signature verification detects manipulated signatures."""

    signer = GPGSigner(
        keyid=gnupg_keypair.keyid,
        passphrase=gnupg_keypair.passphrase,
        homedir=gnupg_keypair.gnupg_home,
    )

    # Add a single signature ...
    await image_config.sign(signer)
    response = await image_config.verify_signatures(
        signer_kwargs={GPGSigner.__name__: {"homedir": gnupg_keypair.gnupg_home}}
    )
    assert response.results[0].valid

    # Modify the digest value of the (first) signature ...
    signatures = image_config.get_signature_list()
    temp = deepcopy(signatures)
    temp[0] = ImageConfigSignatureEntry(
        digest=FormattedSHA256.calculate(b"tampertampertamper"),
        signature=temp[0].signature,
    )
    image_config.set_signature_list(temp)

    # An exception should be raised if digest value from the signature does not match the canonical digest of the image
    # configuration (without any signatures).
    with pytest.raises(DigestMismatchError) as exception:
        await image_config.verify_signatures()
    assert str(exception.value).startswith("Image config canonical digest mismatch:")

    # Restore the unmodified signature and endorse ...
    image_config.set_signature_list(signatures)
    await image_config.sign(signer, SignatureTypes.ENDORSE)
    response = await image_config.verify_signatures(
        signer_kwargs={GPGSigner.__name__: {"homedir": gnupg_keypair.gnupg_home}}
    )
    assert response.results[0].valid

    # Modify the digest value of the second signature ...
    signatures = image_config.get_signature_list()
    temp = deepcopy(signatures)
    temp[1] = ImageConfigSignatureEntry(
        digest=FormattedSHA256.calculate(b"tampertampertamper"),
        signature=temp[1].signature,
    )
    image_config.set_signature_list(temp)

    # An exception should be raised if digest value from the signature does not match the canonical digest of the image
    # configuration (including the first signature).
    with pytest.raises(DigestMismatchError) as exception:
        await image_config.verify_signatures()
    assert str(exception.value).startswith("Image config canonical digest mismatch:")
 async def put_image_layer(
         self, image_name: ImageName, content,
         **kwargs) -> DockerRegistryClientAsyncPutBlobUpload:
     response = await self.docker_registry_client_async.post_blob(
         image_name, **kwargs)
     digest = FormattedSHA256.calculate(content)
     return await self.docker_registry_client_async.put_blob_upload(
         response["location"], digest, data=content, **kwargs)
    def get_digest_canonical(self) -> FormattedSHA256:
        """
        Retrieves the SHA256 digest value of the image configuration in canonical JSON form.

        Returns:
            The SHA256 digest value of the image configuration in canonical JSON form.
        """
        return FormattedSHA256.calculate(self.get_bytes_canonical())
def test_set_tag(archive_repositories: ArchiveRepositories,
                 image_name: ImageName, name: str):
    """Test repository tag assignment."""
    tag = archive_repositories.get_tag(image_name)
    assert tag
    assert FormattedSHA256(tag) == image_name.digest

    digest = FormattedSHA256.calculate(name.encode("utf-8"))
    name = ImageName.parse(name)
    archive_repositories.set_tag(name, digest)
    assert FormattedSHA256(archive_repositories.get_tag(name)) == digest
 async def put_image_layer(self, image_name: ImageName, content,
                           **kwargs) -> FormattedSHA256:
     # TODO: Do we really want to use random garbage here???
     #       Look into moby/.../save.go to find what to use instead.
     digest = FormattedSHA256.calculate("{0}{1}{2}".format(
         str(image_name), datetime.datetime.now(),
         random.randint(1, 101)).encode("utf-8"))
     layer = ArchiveManifest.digest_to_layer(digest)
     with open(self.archive, "rb+") as file_out:
         tar_add_file(file_out, layer, content)
     return digest
 async def put_image_layer_from_disk(self, image_name: ImageName, file,
                                     **kwargs) -> FormattedSHA256:
     # TODO: Convert to async
     if self.dry_run:
         LOGGER.debug("Dry Run: skipping put_image_layer_from_disk")
         return FormattedSHA256("0" * 64)
     # TODO: Do we really want to use random garbage here???
     #       Look into moby/.../save.go to find what to use instead.
     digest = FormattedSHA256.calculate("{0}{1}{2}".format(
         str(image_name), datetime.datetime.now(),
         random.randint(1, 101)).encode("utf-8"))
     layer = ArchiveManifest.digest_to_layer(digest)
     with open(self.archive, "rb+") as file_out:
         tar_mkdir(file_out, os.path.dirname(layer))
         file_out.seek(0)
         tar(file_out, layer, file)
     return digest
Beispiel #7
0
    def get_combined_layerid(parent: FormattedSHA256,
                             layer: FormattedSHA256) -> FormattedSHA256:
        """
        Retrieves the layer identifier for a given parent-layer combination.

        Args:
            parent: The parent layer identifier.
            layer:  The layer identifier.

        Returns:
            The corresponding layer identifier.
        """
        result = layer
        if parent:
            # Note: The image layer is the digest value of the formatted string.
            result = FormattedSHA256.calculate("{0} {1}".format(
                parent, layer).encode("utf-8"))
        return result
    async def put_image_layer_from_disk(self, image_name: ImageName, file,
                                        **kwargs) -> FormattedSHA256:
        # pylint: disable=protected-access
        file_is_async = kwargs.get("file_is_async", True)
        if file_is_async:
            file = (
                file._file
            )  # DUCK PUNCH: Unwrap the file handle from the asynchronous object

        # TODO: Do we really want to use random garbage here???
        #       Look into moby/.../save.go to find what to use instead.
        digest = FormattedSHA256.calculate("{0}{1}{2}".format(
            str(image_name), datetime.datetime.now(),
            random.randint(1, 101)).encode("utf-8"))
        layer = ArchiveManifest.digest_to_layer(digest)
        with open(self.archive, "rb+") as file_out:
            tar_mkdir(file_out, str(Path(layer).parent))
            file_out.seek(0)
            tar_add_file_from_disk(file_out, layer, file)
        return digest
Beispiel #9
0
def get_test_data() -> Generator[TypingGetTestData, None, None]:
    """Dynamically initializes test data."""
    for endpoint in ["endpoint.io", "endpoint:port", None]:
        for image in [
                "image", "ns0/image", "ns0/ns1/image", "ns0/ns1/ns2/image"
        ]:
            for tag in ["tag", None]:
                for digest in [FormattedSHA256.calculate(b""), None]:
                    # Construct a complex string ...
                    string = image
                    if tag:
                        string = f"{string}:{tag}"
                    if digest:
                        string = f"{string}@{digest}"
                    if endpoint:
                        string = f"{endpoint}/{string}"
                    yield {
                        "digest": digest,
                        "endpoint": endpoint,
                        "image": image,
                        "object": ImageName.parse(string),
                        "string": string,
                        "tag": tag,
                    }
def test_calculate():
    """Test that a formatted SHA256 can be calculated."""
    assert (
        FormattedSHA256.calculate(b"test data") ==
        "sha256:916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9"
    )
Beispiel #11
0
def test_get_digest(json_bytes_data: TypingJsonBytesData):
    """Test raw image json_bytes retrieval."""
    digest = json_bytes_data["json_bytes"].get_digest()
    assert digest == FormattedSHA256.calculate(json_bytes_data["bytes"])
Beispiel #12
0
def test_get_digest(manifest_data: TypingManifestData):
    """Test raw image manifest retrieval."""
    digest = manifest_data["manifest"].get_digest()
    assert digest == FormattedSHA256.calculate(manifest_data["bytes"])