diff --git a/model_signing/manifest/manifest.py b/model_signing/manifest/manifest.py index 076f4acb..d6a5f34b 100644 --- a/model_signing/manifest/manifest.py +++ b/model_signing/manifest/manifest.py @@ -208,6 +208,32 @@ def __str__(self) -> str: """ return f"{str(self.path)}:{self.start}:{self.end}" + @classmethod + def from_str(cls, s: str) -> Self: + """Builds a file shard from the string representation. + + It is guaranteed that for a shard `shard` and a (valid) string `s` the + following two round-trip properties hold: + + ``` + str(Shard.from_str(s)) == s + Shard.from_str(str(shard)) == shard + ``` + + Raises: + ValueError: if the string argument does not represent a valid shard + serialization (is not in the format `path:start:end`). + """ + parts = s.split(":") + if len(parts) != 3: + raise ValueError(f"Expected 3 components separated by `:`, got {s}") + + path = pathlib.PurePosixPath(parts[0]) + start = int(parts[1]) + end = int(parts[2]) + + return cls(path, start, end) + @dataclasses.dataclass class ShardedFileManifestItem(ManifestItem): diff --git a/model_signing/manifest/manifest_test.py b/model_signing/manifest/manifest_test.py index 28d2515c..a5466098 100644 --- a/model_signing/manifest/manifest_test.py +++ b/model_signing/manifest/manifest_test.py @@ -89,6 +89,43 @@ def test_manifest_has_the_correct_resource_descriptors(self): assert descriptors[1].digest.digest_value == b"hash2" +class TestShard: + + def test_round_trip_from_shard(self): + shard = manifest.Shard(pathlib.PurePosixPath("file"), 0, 42) + shard_str = str(shard) + assert manifest.Shard.from_str(shard_str) == shard + + def test_round_trip_from_string(self): + shard_str = "file:0:42" + shard = manifest.Shard.from_str(shard_str) + assert str(shard) == shard_str + + def test_invalid_shard_str_too_few_components(self): + shard_str = "file" + + with pytest.raises(ValueError, match="Expected 3 components"): + manifest.Shard.from_str(shard_str) + + def test_invalid_shard_str_too_many_components(self): + shard_str = "file:0:1:2" + + with pytest.raises(ValueError, match="Expected 3 components"): + manifest.Shard.from_str(shard_str) + + def test_invalid_shard_bad_type_for_start_offset(self): + shard_str = "file:zero:4" + + with pytest.raises(ValueError, match="invalid literal for int"): + manifest.Shard.from_str(shard_str) + + def test_invalid_shard_bad_type_for_endart_offset(self): + shard_str = "file:0:four" + + with pytest.raises(ValueError, match="invalid literal for int"): + manifest.Shard.from_str(shard_str) + + class TestShardLevelManifest: def test_insert_order_does_not_matter(self): diff --git a/model_signing/signing/in_toto.py b/model_signing/signing/in_toto.py index 66af0fac..8f119340 100644 --- a/model_signing/signing/in_toto.py +++ b/model_signing/signing/in_toto.py @@ -19,11 +19,13 @@ envelope format is DSSE, see https://github.com/secure-systems-lab/dsse. """ -from typing import Final, Self +import pathlib +from typing import Any, Final, Self from in_toto_attestation.v1 import statement from typing_extensions import override +from model_signing.hashing import hashing from model_signing.hashing import memory from model_signing.manifest import manifest as manifest_module from model_signing.signing import signing @@ -40,13 +42,47 @@ class IntotoPayload(signing.SigningPayload): """ predicate_type: Final[str] + statement: Final[statement.Statement] + + @classmethod + def manifest_from_payload( + cls, payload: dict[str, Any] + ) -> manifest_module.Manifest: + """Builds a manifest from an in-memory in-toto payload. + + Delegates to all known subclasses until one matches the provided + `predicateType` (matching `predicate_type` class attribute). + + Args: + payload: the in memory in-toto payload to build a manifest from. + + Returns: + A manifest that can be converted back to the same payload. + + Raises: + ValueError: If the payload cannot be converted. + """ + predicate_type = payload["predicateType"] + subclasses = [ + SingleDigestIntotoPayload, + DigestOfDigestsIntotoPayload, + DigestOfShardDigestsIntotoPayload, + DigestsIntotoPayload, + ShardDigestsIntotoPayload, + ] + + for subcls in subclasses: + if predicate_type == subcls.predicate_type: + return subcls.manifest_from_payload(payload) + + raise ValueError("Unknown in-toto predicate type {predicate_type}") class SingleDigestIntotoPayload(IntotoPayload): """In-toto payload where the model is serialized to just one digest. In this case, we encode the model as the only subject of the statement. We - don't set the name field, and use the digest as the one resulting from the + set the name field to ".", and use the digest as the one resulting from the model serialization. However, since we use custom hashing algorithms, but these are not supported @@ -59,6 +95,7 @@ class SingleDigestIntotoPayload(IntotoPayload): "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "3aab065c...." } @@ -90,7 +127,7 @@ def __init__(self, *, digest_hex: str, digest_algorithm: str): digest_algorithm: the algorithm used to compute the digest. """ digest = {"sha256": digest_hex} - descriptor = statement.ResourceDescriptor(digest=digest).pb + descriptor = statement.ResourceDescriptor(name=".", digest=digest).pb self.statement = statement.Statement( subjects=[descriptor], @@ -125,13 +162,41 @@ def from_manifest(cls, manifest: manifest_module.Manifest) -> Self: digest_algorithm=digest.algorithm, ) + @classmethod + @override + def manifest_from_payload( + cls, payload: dict[str, Any] + ) -> manifest_module.DigestManifest: + """Builds a manifest from an in-memory in-toto payload. + + Args: + payload: the in memory in-toto payload to build a manifest from. + + Returns: + A manifest that can be converted back to the same payload. + + Raises: + ValueError: If the payload does not match the expected payload + format for this class. See `from_manifest`. + """ + subjects = payload["subject"] + predicate = payload["predicate"] + + if len(subjects) != 1: + raise ValueError("Expected one single subject, got {subjects}") + + algorithm = predicate["actual_hash_algorithm"] + digest_value = subjects[0]["digest"]["sha256"] + digest = hashing.Digest(algorithm, digest_value) + return manifest_module.DigestManifest(digest) + def _convert_descriptors_to_hashed_statement( manifest: manifest_module.Manifest, *, predicate_type: str, predicate_top_level_name: str, -): +) -> statement.Statement: """Converts manifest descriptors to an in-toto statement with payload. Args: @@ -151,7 +216,7 @@ def _convert_descriptors_to_hashed_statement( }) digest = {"sha256": hasher.compute().digest_hex} - descriptor = statement.ResourceDescriptor(digest=digest).pb + descriptor = statement.ResourceDescriptor(name=".", digest=digest).pb return statement.Statement( subjects=[descriptor], @@ -176,6 +241,7 @@ class DigestOfDigestsIntotoPayload(IntotoPayload): "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "18b5a4..." } @@ -255,6 +321,50 @@ def from_manifest(cls, manifest: manifest_module.Manifest) -> Self: ) return cls(statement) + @classmethod + @override + def manifest_from_payload( + cls, payload: dict[str, Any] + ) -> manifest_module.FileLevelManifest: + """Builds a manifest from an in-memory in-toto payload. + + Args: + payload: the in memory in-toto payload to build a manifest from. + + Returns: + A manifest that can be converted back to the same payload. + + Raises: + ValueError: If the payload does not match the expected payload + format for this class. See `from_manifest`. + """ + subjects = payload["subject"] + predicate = payload["predicate"] + + if len(subjects) != 1: + raise ValueError("Expected one single subject, got {subjects}") + + hasher = memory.SHA256() + items = [] + for file in predicate["files"]: + path = pathlib.PurePosixPath(file["name"]) + digest = hashing.Digest( + file["algorithm"], bytes.fromhex(file["digest"]) + ) + item = manifest_module.FileManifestItem(path=path, digest=digest) + items.append(item) + hasher.update(digest.digest_value) + + expected_digest = subjects[0]["digest"]["sha256"] + obtained_digest = hasher.compute().digest_hex + if obtained_digest != expected_digest: + raise ValueError( + f"Verification failed. " + f"Expected {expected_digest}, got {obtained_digest}" + ) + + return manifest_module.FileLevelManifest(items) + class DigestOfShardDigestsIntotoPayload(IntotoPayload): """In-toto payload where the subject is a digest of digests of file shards. @@ -272,6 +382,7 @@ class DigestOfShardDigestsIntotoPayload(IntotoPayload): "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "18b5a4..." } @@ -353,10 +464,56 @@ def from_manifest(cls, manifest: manifest_module.Manifest) -> Self: ) return cls(statement) + @classmethod + @override + def manifest_from_payload( + cls, payload: dict[str, Any] + ) -> manifest_module.ShardLevelManifest: + """Builds a manifest from an in-memory in-toto payload. + + Args: + payload: the in memory in-toto payload to build a manifest from. + + Returns: + A manifest that can be converted back to the same payload. + + Raises: + ValueError: If the payload does not match the expected payload + format for this class. See `from_manifest`. + """ + subjects = payload["subject"] + predicate = payload["predicate"] + + if len(subjects) != 1: + raise ValueError("Expected one single subject, got {subjects}") + + hasher = memory.SHA256() + items = [] + for entry in predicate["shards"]: + shard = manifest_module.Shard.from_str(entry["name"]) + digest = hashing.Digest( + entry["algorithm"], bytes.fromhex(entry["digest"]) + ) + item = manifest_module.ShardedFileManifestItem( + path=shard.path, start=shard.start, end=shard.end, digest=digest + ) + items.append(item) + hasher.update(digest.digest_value) + + expected_digest = subjects[0]["digest"]["sha256"] + obtained_digest = hasher.compute().digest_hex + if obtained_digest != expected_digest: + raise ValueError( + f"Verification failed. " + f"Expected {expected_digest}, got {obtained_digest}" + ) + + return manifest_module.ShardLevelManifest(items) + def _convert_descriptors_to_direct_statement( manifest: manifest_module.Manifest, predicate_type: str -): +) -> statement.Statement: """Converts manifest descriptors to an in-toto statement, as subjects. Args: @@ -482,6 +639,36 @@ def from_manifest(cls, manifest: manifest_module.Manifest) -> Self: ) return cls(statement) + @classmethod + @override + def manifest_from_payload( + cls, payload: dict[str, Any] + ) -> manifest_module.FileLevelManifest: + """Builds a manifest from an in-memory in-toto payload. + + Args: + payload: the in memory in-toto payload to build a manifest from. + + Returns: + A manifest that can be converted back to the same payload. + + Raises: + ValueError: If the payload does not match the expected payload + format for this class. See `from_manifest`. + """ + subjects = payload["subject"] + + items = [] + for subject in subjects: + path = pathlib.PurePosixPath(subject["name"]) + algorithm = subject["annotations"]["actual_hash_algorithm"] + digest_value = subject["digest"]["sha256"] + digest = hashing.Digest(algorithm, bytes.fromhex(digest_value)) + item = manifest_module.FileManifestItem(path=path, digest=digest) + items.append(item) + + return manifest_module.FileLevelManifest(items) + class ShardDigestsIntotoPayload(IntotoPayload): """In-toto payload where the subjects are the model shards themselves. @@ -586,3 +773,35 @@ def from_manifest(cls, manifest: manifest_module.Manifest) -> Self: manifest, predicate_type=cls.predicate_type ) return cls(statement) + + @classmethod + @override + def manifest_from_payload( + cls, payload: dict[str, Any] + ) -> manifest_module.ShardLevelManifest: + """Builds a manifest from an in-memory in-toto payload. + + Args: + payload: the in memory in-toto payload to build a manifest from. + + Returns: + A manifest that can be converted back to the same payload. + + Raises: + ValueError: If the payload does not match the expected payload + format for this class. See `from_manifest`. + """ + subjects = payload["subject"] + + items = [] + for subject in subjects: + shard = manifest_module.Shard.from_str(subject["name"]) + algorithm = subject["annotations"]["actual_hash_algorithm"] + digest_value = subject["digest"]["sha256"] + digest = hashing.Digest(algorithm, bytes.fromhex(digest_value)) + item = manifest_module.ShardedFileManifestItem( + path=shard.path, start=shard.start, end=shard.end, digest=digest + ) + items.append(item) + + return manifest_module.ShardLevelManifest(items) diff --git a/model_signing/signing/sigstore.py b/model_signing/signing/sigstore.py new file mode 100644 index 00000000..db0e2e82 --- /dev/null +++ b/model_signing/signing/sigstore.py @@ -0,0 +1,356 @@ +# Copyright 2024 The Sigstore Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Sigstore based signature, signers and verifiers.""" + +import json +import pathlib +from typing import Self + +from google.protobuf import json_format +from sigstore import dsse as sigstore_dsse +from sigstore import models as sigstore_models +from sigstore import oidc as sigstore_oidc +from sigstore import sign as sigstore_signer +from sigstore import verify as sigstore_verifier +from typing_extensions import override + +from model_signing.hashing import hashing +from model_signing.manifest import manifest +from model_signing.signing import as_bytes +from model_signing.signing import in_toto +from model_signing.signing import signing + +_IN_TOTO_JSON_PAYLOAD_TYPE : str = "application/vnd.in-toto+json" +_IN_TOTO_STATEMENT_TYPE : str = "https://in-toto.io/Statement/v1" + + +class SigstoreSignature(signing.Signature): + """Sigstore signature support, wrapping around `sigstore_models.Bundle`.""" + + def __init__(self, bundle: sigstore_models.Bundle): + """Builds an instance of this signature. + + Args: + bundle: the Sigstore `Bundle` to wrap around. + """ + self.bundle = bundle + + @override + def write(self, path: pathlib.Path) -> None: + """Writes the signature to disk, to the given path. + + The Sigstore `Bundle` is written in JSON format, per the + canonicalization defined by the `sigstore-python` library. + + Args: + path: the path to write the signature to. + """ + path.write_text(self.bundle.to_json()) + + @classmethod + @override + def read(cls, path: pathlib.Path) -> Self: + """Reads the signature from disk. + + Does not perform any signature verification, except what is needed to + parse the signature file. + + Args: + path: the path to read the signature from. + + Returns: + A `SigstoreSignature` object wrapping a Sigstore `Bundle`. + + Raises: + ValueError: If the Sigstore `Bundle` could not be deserialized from + the contents of the file pointed to by `path`. + """ + content = path.read_text() + return cls(sigstore_models.Bundle.from_json(content)) + + +class SigstoreSigner(signing.Signer): + """Signing machinery using Sigstore. + + We want to sign both digests and in-toto statements, so we provide two + separate subclasses for the signing. This class will just handle the common + parts needed to work with Sigstore. + """ + + def __init__( + self, + *, + oidc_issuer: str | None = None, + use_ambient_credentials: bool = True, + use_staging: bool = False, + ): + """Initializes Sigstore signers. + + Needs to set-up a signing context to use the public goods instance and + machinery for getting an identity token to use in signing. + + Args: + oidc_issuer: An optional OpenID Connect issuer to use instead of the + default production one. Only relevant if `use_staging = False`. + Default is empty, relying on the Sigstore configuration. + use_ambient_credentials: Use ambient credentials (also known as + Workload Identity). Default is True. If ambient credentials cannot + be used (not available, or option disabled), a flow to get signer + identity via OIDC will start. + use_staging: Use staging configurations, instead of production. This + is supposed to be set to True only when testing. Default is False. + """ + if use_staging: + self._signing_context = sigstore_signer.SigningContext.staging() + self._issuer = sigstore_oidc.Issuer.staging() + else: + self._signing_context = sigstore_signer.SigningContext.production() + if oidc_issuer is not None: + self._issuer = sigstore_oidc.Issuer.production(oidc_issuer) + else: + self._issuer = sigstore_oidc.Issuer.production() + + self._use_ambient_credentials = use_ambient_credentials + + def _get_identity_token(self) -> sigstore_oidc.IdentityToken: + """Obtains an identity token to use in signing.""" + if self._use_ambient_credentials: + token = sigstore_oidc.detect_credential() + if token: + return sigstore_oidc.IdentityToken(token) + + return self._issuer.identity_token(force_oob=True) + + +class SigstoreArtifactSigner(SigstoreSigner): + """A Sigstore signer that only signs artifacts. + + In our case, this instance is only used to sign `as_bytes.BytesPayload` + signing payloads. + """ + + @override + def sign(self, payload: signing.SigningPayload) -> SigstoreSignature: + """Signs the provided signing payload. + + Args: + payload: the payload to sign. + + Returns: + A `SigstoreSignature` object. + + Raises: + TypeError: If the `payload` type is not `as_bytes.BytesPayload`. + """ + if not isinstance(payload, as_bytes.BytesPayload): + raise TypeError("Only `BytesPayload` payloads are supported") + + token = self._get_identity_token() + with self._signing_context.signer(token) as signer: + bundle = signer.sign_artifact(payload.digest) + + return SigstoreSignature(bundle) + + +class SigstoreDSSESigner(SigstoreSigner): + """A Sigstore signer that only signs DSSE statements. + + In our case, this instance is only used to sign `in_toto.IntotoPayload` + signing payloads. + """ + + @override + def sign(self, payload: signing.SigningPayload) -> SigstoreSignature: + """Signs the provided signing payload. + + Args: + payload: the payload to sign. + + Returns: + A `SigstoreSignature` object. + + Raises: + TypeError: If the `payload` type is not `as_bytes.BytesPayload`. + """ + if not isinstance(payload, in_toto.IntotoPayload): + raise TypeError("Only `IntotoPayload` payloads are supported") + + # We need to convert from in-toto statement to Sigstore's DSSE + # version. They both contain the same contents, but there is no way + # to coerce one type to the other. + # See also: https://github.com/sigstore/sigstore-python/issues/1076 + statement = sigstore_dsse.Statement( + json_format.MessageToJson(payload.statement.pb).encode("utf-8") + ) + + token = self._get_identity_token() + with self._signing_context.signer(token) as signer: + bundle = signer.sign_dsse(statement) + + return SigstoreSignature(bundle) + + +class SigstoreVerifier(signing.Verifier): + """Signature verification machinery using Sigstore. + + We want to be able to verify signatures generated from either digests or + in-toto statements, so we provide two separate subclasses for the + verification. This class will just handle the common parts needed to work + with Sigstore. + """ + + def __init__( + self, + *, + identity: str, + oidc_issuer: str, + use_staging: bool = False, + ): + """Initializes Sigstore verifiers. + + When verifying a signature, we also check an identity policy: the + certificate must belong to a given "identity", and must be issued by a + given OpenID Connect issuer. + + Args: + identity: The expected identity that has signed the model. + oidc_issuer: The expected OpenID Connect issuer that provided the + certificate used for the signature. + use_staging: Use staging configurations, instead of production. This + is supposed to be set to True only when testing. Default is False. + """ + if use_staging: + self._verifier = sigstore_verifier.Verifier.staging() + else: + self._verifier = sigstore_verifier.Verifier.production() + + # TODO: https://github.com/sigstore/model-transparency/issues/271 - + # Support additional verification policies + self._policy = sigstore_verifier.policy.Identity( + identity=identity, issuer=oidc_issuer + ) + + +class SigstoreArtifactVerifier(SigstoreVerifier): + """A Sigstore verifier for signatures on simple digests. + + This class only accepts signatures generated by `SigstoreArtifactSigner`. + """ + + def __init__( + self, + expected_digest: bytes, + *, + identity: str, + oidc_issuer: str, + use_staging: bool = False, + ): + """Initializes this verifier. + + Since the signature was generated over a digest, we need to pass the + expected digest as an argument here (as that is what Sigstore's + `verify_artifact` expects). Hence, in order to use this class, the model + on disk needs to be first serialized and the obtained digest can be + passed to signature verification. This is in reverse compared to + manifest based signatures where signature verification results in a + manifest of all expected files and then other layers in the library can + verify the model integrity (optionally with additional policies). + + Args: + expected_digest: Expected digest. Must match what was signed. + identity: The expected identity that has signed the model. + oidc_issuer: The expected OpenID Connect issuer that provided the + certificate used for the signature. + use_staging: Use staging configurations, instead of production. This + is supposed to be set to True only when testing. Default is False. + """ + super().__init__( + identity=identity, oidc_issuer=oidc_issuer, use_staging=use_staging + ) + self._expected_digest = expected_digest + + @override + def verify(self, signature: signing.Signature) -> manifest.DigestManifest: + """Verifies the signature. + + Args: + signature: the signature to verify. + + Returns: + A `manifest.DigestManifest` instance that represents the model as a + single hash. If the function returns without raising an exception, + then verification succeeded. + + Raises: + ValueError: If the signature verification fails. + TypeError: If the signature type is not `SigstoreSignature`. + """ + if not isinstance(signature, SigstoreSignature): + raise TypeError("Only `SigstoreSignature` signatures are supported") + + self._verifier.verify_artifact( + input_=self._expected_digest, + bundle=signature.bundle, + policy=self._policy + ) + + digest = hashing.Digest("sha256", self._expected_digest) + return manifest.DigestManifest(digest) + + +class SigstoreDSSEVerifier(SigstoreVerifier): + """A Sigstore verifier for signatures on simple digests. + + This class only accepts signatures generated by `SigstoreArtifactSigner`. + """ + + @override + def verify(self, signature: signing.Signature) -> manifest.Manifest: + """Verifies the signature. + + Args: + signature: the signature to verify. + + Returns: + A manifest that represents the model when it was signed. + + Raises: + ValueError: If the signature verification fails, or if the DSSE + envelope is not in the expected format. + TypeError: If the signature type is not `SigstoreSignature`. + """ + if not isinstance(signature, SigstoreSignature): + raise TypeError("Only `SigstoreSignature` signatures are supported") + + payload_type, payload = self._verifier.verify_dsse( + bundle=signature.bundle, + policy=self._policy + ) + + if payload_type != _IN_TOTO_JSON_PAYLOAD_TYPE: + raise ValueError( + f"Only {_IN_TOTO_JSON_PAYLOAD_TYPE} DSSE payload acceped, " + f"got {payload_type}" + ) + + payload = json.loads(payload) + + if payload["_type"] != _IN_TOTO_STATEMENT_TYPE: + raise ValueError( + f"Expected in-toto {_IN_TOTO_STATEMENT_TYPE} payload, " + f"but got {payload['_type']}" + ) + + return in_toto.IntotoPayload.manifest_from_payload(payload) diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/deep_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/deep_model_folder index 0b24629a..1295cc7f 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/deep_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/deep_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "18b5a45fe7983f7194e8ffd96c80f5f0ec53191bf4a32b6aff293f043e816d7a" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_file b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_file index ac8470eb..c2f9f5d1 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_file +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_folder index 39e22e6c..9f2c05e5 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/empty_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/model_folder_with_empty_file b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/model_folder_with_empty_file index 59102d5c..2d292903 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/model_folder_with_empty_file +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/model_folder_with_empty_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_file b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_file index 4c594350..3e65762f 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_file +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "002d162f867c5eee944e5080d25829b6625be0e3f081f6fbafc7dd655ca2e178" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_folder index 737465bb..d46d2d61 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/sample_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "143cc682e555951649f18e2761c3d526d2502996f5e32dc187ef7f8a614f8df7" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/symlink_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/symlink_model_folder index 8e737f97..a6b711ca 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/symlink_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfDigestsIntotoPayload/symlink_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "002d162f867c5eee944e5080d25829b6625be0e3f081f6fbafc7dd655ca2e178" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/deep_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/deep_model_folder index 42b52a19..3adb5f70 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/deep_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/deep_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "18b5a45fe7983f7194e8ffd96c80f5f0ec53191bf4a32b6aff293f043e816d7a" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_file b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_file index 898052c3..64ff5def 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_file +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_folder index 898052c3..64ff5def 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/empty_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/model_folder_with_empty_file b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/model_folder_with_empty_file index 898052c3..64ff5def 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/model_folder_with_empty_file +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/model_folder_with_empty_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_file b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_file index 8dee2069..b7907eaf 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_file +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "002d162f867c5eee944e5080d25829b6625be0e3f081f6fbafc7dd655ca2e178" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_folder index 461cee86..f2d4980c 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/sample_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "143cc682e555951649f18e2761c3d526d2502996f5e32dc187ef7f8a614f8df7" } diff --git a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/symlink_model_folder b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/symlink_model_folder index dcba2415..f45b3ae4 100644 --- a/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/symlink_model_folder +++ b/model_signing/signing/testdata/in_toto/TestDigestOfShardDigestsIntotoPayload/symlink_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "002d162f867c5eee944e5080d25829b6625be0e3f081f6fbafc7dd655ca2e178" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/deep_model_folder b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/deep_model_folder index da101c70..a5d1c277 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/deep_model_folder +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/deep_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "36eed9389ebbbe15ac15d33c81dabb60ccb7c945ff641d78f59db9aa9dc47ac9" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_file b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_file index 307b507d..aef4a940 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_file +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_folder b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_folder index a67f143a..0b14981f 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_folder +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/empty_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/model_folder_with_empty_file b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/model_folder_with_empty_file index b1c88436..b5298124 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/model_folder_with_empty_file +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/model_folder_with_empty_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "68efd863f20e083173846a5e98ad11387a1979efe20ded426a7930bab8358a9c" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_file b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_file index ceae1692..8bb6bbba 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_file +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_file @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "3aab065c7181a173b5dd9e9d32a9f79923440b413be1e1ffcdba26a7365f719b" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_folder b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_folder index 9091929d..4a5f4774 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_folder +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/sample_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "310af4fc4c52bf63cd1687c67076ed3e56bc5480a1b151539e6c550506ae0301" } diff --git a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/symlink_model_folder b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/symlink_model_folder index ba3a1c24..7a016b53 100644 --- a/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/symlink_model_folder +++ b/model_signing/signing/testdata/in_toto/TestSingleDigestIntotoPayload/symlink_model_folder @@ -2,6 +2,7 @@ "_type": "https://in-toto.io/Statement/v1", "subject": [ { + "name": ".", "digest": { "sha256": "8372365be7578241d18db47ec83b735bb450a10a1b4298d9b7b0d8bf543b7271" }