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

feat: add unit test for online tlog verification #296

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

vishal-chdhry
Copy link
Contributor

@vishal-chdhry vishal-chdhry commented Sep 26, 2024

Summary

Closes: #53
Adds test for online verification of artifact transparency log using mock rekor

Release Note

Documentation

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
@vishal-chdhry vishal-chdhry requested a review from a team as a code owner September 26, 2024 11:12
@vishal-chdhry
Copy link
Contributor Author

@cmurphy @haydentherapper I tried to add mock test by creating mock Rekor Entry client and adding inclusion proof to GenerateTlogEntry but I could not figure out how to:

  1. generate the correct inclusion proof checkpoint and,
  2. generate hashes required to compute the inclusion proof

It kept on failing while verifying rekorLogEntry. I don't know enough about rekor internals to create them properly. Meanwhile I have created a test that verifies one of the examples using sigstore public rekor.

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
@vishal-chdhry
Copy link
Contributor Author

vishal-chdhry commented Sep 26, 2024

The test that uses public rekor is passing

I have also added the unfinished test to the PR, What I don't understand is

  1. What will be the root hash
  2. How to calculate the "hashes"
  3. Right way to calculate checkpoints

@cmurphy
Copy link
Contributor

cmurphy commented Sep 27, 2024

What will be the root hash

The CT RFC has an explainer on merkle tree hash calculations: https://www.rfc-editor.org/rfc/rfc6962.html#section-2.1

The simplest merkle tree will contain only a single leaf, and the root hash will be the SHA-256 hash of the leaf prepended by a null character. The inclusion proof won't need to include additional hashes because there is only one hash needed to verify the leaf's inclusion in the log: https://gist.github.com/cmurphy/1b1203f4fec5b2363c30901d4684c2c4#file-treesize1-go

I think simplifying GenerateTlogEntry to make the tree only have one node is probably fine. The unit test doesn't need to validate the transparency-dev merkle library, just the sigstore-go usage of it.

If you did need to generate a more complex tree, the RFC explains how the root hash is calculated by concatenating the root hashes of its subtrees, and it would look something like this: https://gist.github.com/cmurphy/1b1203f4fec5b2363c30901d4684c2c4#file-treesize2-go

The test that uses public rekor is passing

It would be better if the unit tests did not make real network calls to the public infrastructure. Unit tests should be self-contained and never rely on external services, because:

  1. If the external service is down or lagging, the CI running the unit tests will also slow down
  2. Unit tests are run often and will significantly add to the network traffic hitting the public infrastructure, which could artificially degrade the service or increase infrastructure costs
  3. Developers may want to run unit tests on a machine not connected to the internet

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
@vishal-chdhry
Copy link
Contributor Author

vishal-chdhry commented Sep 28, 2024

Thank you so much for the amazing explanation @cmurphy, I learned a lot about how tlogs actually work.

I fixed inclusion and checkpoint but I still had this problem where SET verification were failing. I noticed that while verifying SETs in offline mode we are converting the log IDs to hex:

rekorPayload := RekorPayload{
Body: entry.logEntryAnon.Body,
IntegratedTime: *entry.logEntryAnon.IntegratedTime,
LogIndex: *entry.logEntryAnon.LogIndex,
LogID: hex.EncodeToString([]byte(*entry.logEntryAnon.LogID)),
}

But the rekor library does not do that https://github.com/sigstore/rekor/blob/8ca864f6b2bcece1b0b9b004938375bbe8fff03c/pkg/verify/verify.go#L190-L195

When I convert logID to hex in my mock log entry anon, SET verification using rekor library starts to pass, see: 50f5a75 (#296)

Is there a reason why we do that? I don't understand why I should convert it to hex in my mock to make it work

@cmurphy
Copy link
Contributor

cmurphy commented Sep 30, 2024

Is there a reason why we do that? I don't understand why I should convert it to hex in my mock to make it work

It looks like rekor stores the LogID of the log entry as hex encoded:

https://github.com/sigstore/rekor/blob/1e6728717d221a87792a8fc0c4cf588cc31dceb0/pkg/api/entries.go#L92
https://github.com/sigstore/rekor/blob/1e6728717d221a87792a8fc0c4cf588cc31dceb0/pkg/api/api.go#L177

It doesn't need to re-encoded it during verification. But the mock should ensure the log entry is encoded the way the verifier expects it to be.

@vishal-chdhry
Copy link
Contributor Author

okay, makes sense now.

I have hex encoded the log entry and the PR is ready for review

Copy link
Contributor

@cmurphy cmurphy left a comment

Choose a reason for hiding this comment

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

Could you update the PR description to reflect the current purpose of this PR and add a reference to the issue it's addressing?

pkg/verify/tlog_test.go Outdated Show resolved Hide resolved
pkg/tlog/entry.go Outdated Show resolved Hide resolved
pkg/testing/ca/ca.go Outdated Show resolved Hide resolved
pkg/verify/tlog.go Outdated Show resolved Hide resolved
vishal-chdhry and others added 2 commits October 2, 2024 19:38
Co-authored-by: Colleen Murphy <cmurphy@users.noreply.github.com>
Signed-off-by: Vishal Choudhary <vishal.chdhry.work@gmail.com>
Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
pkg/verify/tlog.go Outdated Show resolved Hide resolved
pkg/tlog/entry.go Outdated Show resolved Hide resolved
pkg/tlog/entry.go Outdated Show resolved Hide resolved
IntegratedTime: swag.Int64(tlogEntry.IntegratedTime().Unix()),
LogIndex: swag.Int64(tlogEntry.LogIndex()),
LogID: swag.String(hex.EncodeToString([]byte(tlogEntry.LogKeyID()))),
Verification: tlogEntry.Verification(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking closer at the functional changes in entry.go that were needed to make this work, I'm seeing that this test is tautological - it's using the entity returned by the attestation as the response to the mock rekor client, and effectively comparing it to itself. I think this can be avoided, and the changes in entry.go can be discarded, if we create the mocked response from scratch with some added helpers to VirtualSigstore: https://gist.github.com/cmurphy/44a742f9f08e5cb56bc79dccfbd3b327

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cmurphy that makes sense, thank you so much for helping with this!

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`)
integratedTime := time.Now().Add(5 * time.Minute)
entity, err := virtualSigstore.AttestAtTime("foo@example.com", "issuer", statement, integratedTime, true)
Copy link
Contributor

Choose a reason for hiding this comment

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

One last thing - this change adds one test, TestOnlineVerification, but it's possible to have inclusion proofs without verifying them online and vice versa. Could there be an additional test that tests online verification without the inclusion proof, and maybe one that tests with the inclusion proof with offline verification?

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.

Add unit tests for online log verification and inclusion proofs
2 participants