Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add support for Statements #570

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from

Conversation

chasen-bettinger
Copy link
Contributor

Fixes issue #:
#562

Description of the changes being introduced by the pull request:

  • Added the Statement model
  • Incorporated the Statement model into the Envelope.
  • Skipped a test because I'm awaiting feedback on whether this is the right direction to take this issue (Add support for ITE-6 Attestations #562). The test is built on the previously-used 'link' model as the payload.

Some notes:

Some things I still don't understand:

  • What are valid predicate_types and where do they come from?
  • What is the expected output for the software after this change? I went through the demo and saw that .link files were being generated. I presumed based on the information on the SLSA website that Links are a subset of Statements and that once in-toto supported Statements (and Envelopes WITH Signatures and Statements), the output would be an Envelope? I don't really understand the bigger picture just yet.
  • I don't know what the values for a predicate hash should be nor where that comes from. This resource was most helpful in giving me breadcrumbs, but I'm still uncertain.

Please verify and check that the pull request fulfills the following
requirements
:

  • The code follows the Code Style Guidelines
  • Tests have been added for the bug fix or new feature
  • Docs have been added for the bug fix or new feature

Copy link
Member

@adityasaky adityasaky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start, @chasen-bettinger! I've left some comments but I think this is looking quite good. I'd like to scope this PR to adding support for the statement layer and tackle predicate implementations separately.

Edit: I also recommend switching to a feature branch instead of using develop.

in_toto/models/statement.py Outdated Show resolved Hide resolved
in_toto/models/statement.py Outdated Show resolved Hide resolved
in_toto/models/statement.py Outdated Show resolved Hide resolved
in_toto/models/statement.py Outdated Show resolved Hide resolved
in_toto/models/statement.py Outdated Show resolved Hide resolved
in_toto/models/statement.py Outdated Show resolved Hide resolved
tests/models/test_statement.py Outdated Show resolved Hide resolved
tests/models/test_statement.py Outdated Show resolved Hide resolved
tests/models/test_statement.py Outdated Show resolved Hide resolved
tests/models/test_statement.py Outdated Show resolved Hide resolved
@marcelamelara
Copy link

Hello! I just came across this issue, thanks @chasen-bettinger . Have you seen the protobuf definition for statement? Instead of implementing a separate Python class for Statement, our recommendation (from the attestation side) would be to build applications based on the protos. This way, all ITE-6 compliant in-toto implementations actually use that same pre-defined format. If you're interested, we would very much welcome a contribution related to using the protos in the Python implementation.

@adityasaky
Copy link
Member

@chasen-bettinger this should unblock you: https://pypi.org/project/in-toto-attestation/

self.environment = {}

if hasattr(self, 'byproducts') == True:
self.byproducts["return-value"] = int(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adityasaky In order to successfully serialize this class into a canonical JSON, I've had to write in this hack. When the class is parsed from a protobuf to a JSON, the value becomes a float, which breaks downstream processes because that is against the rules of canonical JSON. In the current design, we cannot get around this limitation because it is a central part of the Signable class and its signable_bytes property.

As you may have already witnessed, either due to the architecture or my poor Python skills (most likely the latter), this class (and the Statement class) became quite ugly when trying to integrate the protobufs into the existing structure. As the code is written now, I believe there's not a way around using the Signable class as it is integral in generating and validating signatures. I fear a slight refactor might be in the near future, but I wanted your thoughts first.

Any thoughts on the best way to proceed?

cc: @marcelamelara

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, the link predicate shouldn't be Signable at all. The predicate will be embedded in a Statement which is what we'd actually be signing.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that the link was extending Signable. Agree with Aditya that shouldn't be the case. The link proto should be converted into a struct, inserted into the Statement predicate field, and the Statement will then be signed.

I also believe that Statement might not have to extend Signable anymore either. The validation of the structure is now implemented in the Statement wrapper class provided by the in_toto_attestation package, and we can similarly use protobuf APIs directly to expose the signable bytes for a Statement.

Copy link
Member

@adityasaky adityasaky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have more comments about how we'd architect this that I'll submit separately.

in_toto/models/link.py Outdated Show resolved Hide resolved
self.environment = {}

if hasattr(self, 'byproducts') == True:
self.byproducts["return-value"] = int(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, the link predicate shouldn't be Signable at all. The predicate will be embedded in a Statement which is what we'd actually be signing.

.gitignore Outdated Show resolved Hide resolved
- Added the Statement model
- Incorporated the Statement model into the Envelope.
- Skipped a test because I'm awaiting feedback on whether this is
the right direction to take this issue (in-toto#562). The test is built
on the previously-used 'link' model as the payload.

Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
Signed-off-by: Chasen Bettinger <bettingerchasen@gmail.com>
STATEMENT_TYPE = "https://in-toto.io/Statement/v1"


# NOTE: 08/24/23 - This function is an intentional
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adityasaky @marcelamelara Review please

Copy link
Member

@lukpueh lukpueh Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fear there is not much else you can do here, if you want Statement to be a Signable.

FYI: I had a very similar issue in in-toto-golang a while ago and tried the same fix you proposed in secure-systems-lab/securesystemslib#625 but did not use it for the reasons pointed out by @jku. The fix I did end up using there (case-handling json.Number) won't work in Python. But maybe that's okay, because the fix is actually quite a hack.

Because of all these problems with canonical json, I think the best way going forward would be to just not support a canonical json representation for Statements, and use it with DSSE Envelopes only.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like others have said this before: #570 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, understood. I'm curious to learn the ramifications of adding a new method to the Envelope class within this library that doesn't require a Signable instance to instantiate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll correct these tests once the structure of the implementation details have settled. Was using this as my test bed for validating changes.

@@ -60,4 +60,4 @@ target/

#Ipython Notebook
.ipynb_checkpoints
.travis-solo/
.travis-solo/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add this new line back in?

@@ -196,6 +196,13 @@ def parse_password_and_prompt_args(args):
"help": ("generate metadata using dsse (experimental)."),
}

STATEMENT_ARGS = ["--use-statement"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried statement is ambiguous, could you update this to be attestation instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, --generate-attestation.

@@ -588,7 +593,33 @@ def in_toto_run(

if use_dsse:
LOG.info("Generating link metadata using DSSE...")
link_metadata = Envelope.from_signable(link)

if use_statement == False:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential footgun: if I set use_statement (or use_attestation) to True, I shouldn't be expected to set use_dsse to True as well. I see the CLI handles this but this is an issue with direct API use. I suggest we move use_attestation out of the use_dsse block and only support DSSE there. The use_dsse conditional block should only be for legacy links, and something we phase out in a future version as we deprecate legacy links in favour of link predicates.

@@ -223,7 +226,8 @@ def __repr__(self):
separators = (",", ":") if self.compact_json else (",", ": ")

return json.dumps(
{"signatures": self.signatures, "signed": attr.asdict(self.signed)},
{"signatures": self.signatures,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

editor shenanigans?

@@ -438,7 +442,8 @@ def _validate_signatures(self):
)

for signature in self.signatures:
securesystemslib.formats.ANY_SIGNATURE_SCHEMA.check_match(signature)
securesystemslib.formats.ANY_SIGNATURE_SCHEMA.check_match(
signature)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants