From cc32d4b676eb8bdaf6da5adac9a9101432b316f0 Mon Sep 17 00:00:00 2001 From: Appu Goundan Date: Fri, 19 Jan 2024 17:04:58 -0500 Subject: [PATCH] Preserve raw signed json segments in TUF metadata Signed-off-by: Appu Goundan --- .../java/dev/sigstore/tuf/model/Root.java | 7 +- .../dev/sigstore/tuf/model/SignedTufMeta.java | 18 +++- .../java/dev/sigstore/tuf/model/Snapshot.java | 7 +- .../java/dev/sigstore/tuf/model/Targets.java | 7 +- .../dev/sigstore/tuf/model/Timestamp.java | 7 +- .../java/dev/sigstore/tuf/UpdaterTest.java | 16 ++++ .../4.root.json | 65 +++++++++++++++ .../5.root.json | 82 +++++++++++++++++++ .../root-update-with-unknown-fields/README.md | 6 ++ 9 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/4.root.json create mode 100644 sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/5.root.json create mode 100644 sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/README.md diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Root.java b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Root.java index 2fb60cc8..f93b1a9f 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Root.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Root.java @@ -17,12 +17,15 @@ import org.immutables.gson.Gson; import org.immutables.value.Value; +import org.immutables.value.Value.Derived; /** Signed envelope of the Root metadata. */ @Gson.TypeAdapters @Value.Immutable public interface Root extends SignedTufMeta { @Override - @Gson.Named("signed") - RootMeta getSignedMeta(); + @Derived + default RootMeta getSignedMeta() { + return getSignedMeta(RootMeta.class); + } } diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/model/SignedTufMeta.java b/sigstore-java/src/main/java/dev/sigstore/tuf/model/SignedTufMeta.java index ee517615..3504a639 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/model/SignedTufMeta.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/model/SignedTufMeta.java @@ -15,10 +15,14 @@ */ package dev.sigstore.tuf.model; +import com.google.gson.JsonElement; import dev.sigstore.json.GsonSupplier; import dev.sigstore.json.canonicalizer.JsonCanonicalizer; import java.io.IOException; import java.util.List; +import org.immutables.gson.Gson; +import org.immutables.value.Value.Derived; +import org.immutables.value.Value.Lazy; /** * Signed wrapper around {@link TufMeta}. @@ -32,7 +36,19 @@ public interface SignedTufMeta { /** The role metadata that has been signed. */ T getSignedMeta(); + /** An internal helper to translate raw signed json to a useable type. */ + @Derived + default T getSignedMeta(Class type) { + return GsonSupplier.GSON.get().fromJson(getRawSignedMeta(), type); + } + + /** The raw signed json, just verify signature over this to prevent loss of unknown fields */ + @Gson.Named("signed") + JsonElement getRawSignedMeta(); + + @Lazy default byte[] getCanonicalSignedBytes() throws IOException { - return new JsonCanonicalizer(GsonSupplier.GSON.get().toJson(getSignedMeta())).getEncodedUTF8(); + return new JsonCanonicalizer(GsonSupplier.GSON.get().toJson(getRawSignedMeta())) + .getEncodedUTF8(); } } diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Snapshot.java b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Snapshot.java index ed38cba3..4d54656d 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Snapshot.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Snapshot.java @@ -17,12 +17,15 @@ import org.immutables.gson.Gson; import org.immutables.value.Value; +import org.immutables.value.Value.Derived; /** Signed envelope of the Snapshot metadata. */ @Gson.TypeAdapters @Value.Immutable public interface Snapshot extends SignedTufMeta { @Override - @Gson.Named("signed") - SnapshotMeta getSignedMeta(); + @Derived + default SnapshotMeta getSignedMeta() { + return getSignedMeta(SnapshotMeta.class); + } } diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Targets.java b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Targets.java index 1ed6715a..5b2aeff3 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Targets.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Targets.java @@ -17,12 +17,15 @@ import org.immutables.gson.Gson; import org.immutables.value.Value; +import org.immutables.value.Value.Derived; /** Signed envelope of the Targets metadata. */ @Gson.TypeAdapters @Value.Immutable public interface Targets extends SignedTufMeta { @Override - @Gson.Named("signed") - TargetMeta getSignedMeta(); + @Derived + default TargetMeta getSignedMeta() { + return getSignedMeta(TargetMeta.class); + } } diff --git a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Timestamp.java b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Timestamp.java index 418311f8..d3417d8c 100644 --- a/sigstore-java/src/main/java/dev/sigstore/tuf/model/Timestamp.java +++ b/sigstore-java/src/main/java/dev/sigstore/tuf/model/Timestamp.java @@ -17,6 +17,7 @@ import org.immutables.gson.Gson; import org.immutables.value.Value; +import org.immutables.value.Value.Derived; /** Signed envelope of the Timestamp metadata. */ @Gson.TypeAdapters @@ -24,6 +25,8 @@ public interface Timestamp extends SignedTufMeta { @Override - @Gson.Named("signed") - TimestampMeta getSignedMeta(); + @Derived + default TimestampMeta getSignedMeta() { + return getSignedMeta(TimestampMeta.class); + } } diff --git a/sigstore-java/src/test/java/dev/sigstore/tuf/UpdaterTest.java b/sigstore-java/src/test/java/dev/sigstore/tuf/UpdaterTest.java index e8cd81c7..b73bff35 100644 --- a/sigstore-java/src/test/java/dev/sigstore/tuf/UpdaterTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/tuf/UpdaterTest.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.hash.Hashing; +import com.google.common.io.Resources; import com.google.gson.JsonSyntaxException; import dev.sigstore.encryption.signers.Verifier; import dev.sigstore.encryption.signers.Verifiers; @@ -126,6 +127,21 @@ public void testRootUpdate_notEnoughSignatures() } } + @Test + public void testRootUpdate_newRootHasUnknownFields() throws Exception { + setupMirror("synthetic/root-update-with-unknown-fields", "4.root.json", "5.root.json"); + Path startingRoot = + Path.of( + Resources.getResource( + "dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/4.root.json") + .getPath()); + var updater = createTimeStaticUpdater(localStorePath, startingRoot); + + updater.updateRoot(); + Root root = TestResources.loadRoot(localStorePath.resolve("root.json")); + assertEquals(5, root.getSignedMeta().getVersion()); + } + @Test public void testRootUpdate_expiredRoot() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { diff --git a/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/4.root.json b/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/4.root.json new file mode 100644 index 00000000..e506177b --- /dev/null +++ b/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/4.root.json @@ -0,0 +1,65 @@ +{ + "signed": { + "_type": "root", + "spec_version": "1.0", + "version": 4, + "expires": "2029-03-05T22:50:21Z", + "keys": { + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXMZ7rD8tWDE4lK/+naJN7INMxNC7\nbMMANDqTQE7WpzyzffWOg59hc/MwbvJtvuxhO9mEu3GD3Cn0HffFlmVRiA==\n-----END PUBLIC KEY-----\n" + } + }, + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { + "keytype": "ecdsa-sha2-nistp256", + "scheme": "ecdsa-sha2-nistp256", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" + } + } + }, + "roles": { + "root": { + "keyids": [ + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" + ], + "threshold": 1 + }, + "targets": { + "keyids": [ + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "314ae73abd3012fc73bfcc3783e31d03852716597642b891d6a33155c4baf600" + ], + "threshold": 1 + } + }, + "consistent_snapshot": true + }, + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "3044022006fe8fff51d18753aeff141f81a962b8ac33f49831bbbec1334b2733ea96890002206e6f343c9c7b98a2ebd1f0b51aa5286ed3a4d48e271c77d88ea77499231bff5c" + } + ] +} \ No newline at end of file diff --git a/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/5.root.json b/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/5.root.json new file mode 100644 index 00000000..54f5a731 --- /dev/null +++ b/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/5.root.json @@ -0,0 +1,82 @@ +{ + "signatures": [ + { + "keyid": "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "sig": "3045022016f788e5c90b169b4a9b28c56bf73df988b67c2d73f4e49205db50f9f0ae8066022100b4aabd9298ff12205724bae48bf9118a262581a7ca4b5d1a1b4c68498bd5fdde" + }, + { + "keyid": "762cb22caca65de5e9b7b6baecb84ca989d337280ce6914b6440aea95769ad93", + "sig": "3045022100f5a9447b9d240905408106e183ef90914d248a43a869585e5f2001570663f51002207b359b6a39b7ee45c98aca93ba90043bc1f14f9f1223d0cc19eba1c9841e2e82" + } + ], + "signed": { + "_type": "root", + "consistent_snapshot": true, + "expires": "2034-01-06T09:35:12Z", + "keys": { + "762cb22caca65de5e9b7b6baecb84ca989d337280ce6914b6440aea95769ad93": { + "keytype": "ecdsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEohqIdE+yTl4OxpX8ZxNUPrg3SL9H\nBDnhZuceKkxy2oMhUOxhWweZeG3bfM1T4ZLnJimC6CAYVU5+F5jZCoftRw==\n-----END PUBLIC KEY-----\n" + }, + "scheme": "ecdsa-sha2-nistp256", + "x-tuf-on-ci-keyowner": "@jku" + }, + "858f105a894358684339a8d05f7f07d09010b35bcb0808ac8b09c26fce5434b6": { + "keytype": "ecdsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEs+efoKU658vT/rNOsCalHpNadxVtbAei\n0xpIP4bpszlnrYYflOrdDr53rNiQJ33O4eR6Zv1NE6nauR7CKvfuuCNGrtJNEToU\nvWd0uw0zv2ZcL/s7ybmje3qvhhMcN5Ot\n-----END PUBLIC KEY-----\n" + }, + "scheme": "ecdsa-sha2-nistp384", + "x-tuf-on-ci-online-uri": "gcpkms:projects/projectsigstore-staging/locations/global/keyRings/tuf-keyring/cryptoKeys/tuf-staging-key/cryptoKeyVersions/1" + }, + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda": { + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "ecdsa-sha2-nistp256", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL3vL/VeaH6nBbo4rekyO4cc/QthS\n+nlyJXCXSnyIMAtLmVTa8Pf0qG6YIVaR0TmLkyk9YoSVsZakxuMTuaEwrg==\n-----END PUBLIC KEY-----\n" + }, + "scheme": "ecdsa-sha2-nistp256", + "x-tuf-on-ci-keyowner": "@-repo-import" + } + }, + "roles": { + "root": { + "keyids": [ + "c8e09a68b5821b75462ae0df52151c81deb7f1838246dc1da8c34cc91ec12bda", + "762cb22caca65de5e9b7b6baecb84ca989d337280ce6914b6440aea95769ad93" + ], + "threshold": 1 + }, + "snapshot": { + "keyids": [ + "858f105a894358684339a8d05f7f07d09010b35bcb0808ac8b09c26fce5434b6" + ], + "threshold": 1, + "x-tuf-on-ci-expiry-period": 3650, + "x-tuf-on-ci-signing-period": 365 + }, + "targets": { + "keyids": [ + "762cb22caca65de5e9b7b6baecb84ca989d337280ce6914b6440aea95769ad93" + ], + "threshold": 1 + }, + "timestamp": { + "keyids": [ + "858f105a894358684339a8d05f7f07d09010b35bcb0808ac8b09c26fce5434b6" + ], + "threshold": 1, + "x-tuf-on-ci-expiry-period": 14, + "x-tuf-on-ci-signing-period": 7 + } + }, + "spec_version": "1.0", + "version": 5, + "x-tuf-on-ci-expiry-period": 3650, + "x-tuf-on-ci-signing-period": 365 + } +} \ No newline at end of file diff --git a/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/README.md b/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/README.md new file mode 100644 index 00000000..c4be11a0 --- /dev/null +++ b/sigstore-java/src/test/resources/dev/sigstore/tuf/synthetic/root-update-with-unknown-fields/README.md @@ -0,0 +1,6 @@ +# Setup test data + +There are generated by root-signing-staging and were tests that may not exist on the main branch anymore + +`4.root.json` : https://github.com/sigstore/root-signing-staging/blob/fecfda76bb9a1721b37dd581f713b72e41270447/metadata/root_history/4.root.json +`5.root.json` : https://github.com/sigstore/root-signing-staging/blob/fecfda76bb9a1721b37dd581f713b72e41270447/metadata/root_history/5.root.json