Skip to content

Commit ce09d39

Browse files
authored
Merge pull request #849 from sigstore/use_tuf_verifiers_in_updater
Use tuf verifiers in updater
2 parents 12068fc + 6d82e02 commit ce09d39

File tree

3 files changed

+23
-139
lines changed

3 files changed

+23
-139
lines changed

sigstore-java/src/main/java/dev/sigstore/tuf/Updater.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@
1919

2020
import com.google.common.annotations.VisibleForTesting;
2121
import com.google.common.hash.Hashing;
22-
import dev.sigstore.encryption.Keys;
23-
import dev.sigstore.encryption.signers.Verifiers;
22+
import dev.sigstore.tuf.encryption.Verifiers;
2423
import dev.sigstore.tuf.model.*;
2524
import dev.sigstore.tuf.model.TargetMeta.TargetData;
25+
import dev.sigstore.tuf.model.Targets;
26+
import dev.sigstore.tuf.model.Timestamp;
27+
import dev.sigstore.tuf.model.TufMeta;
2628
import java.io.IOException;
27-
import java.nio.charset.StandardCharsets;
2829
import java.security.InvalidKeyException;
2930
import java.security.NoSuchAlgorithmException;
30-
import java.security.PublicKey;
3131
import java.security.SignatureException;
3232
import java.security.spec.InvalidKeySpecException;
3333
import java.time.Clock;
@@ -247,34 +247,34 @@ void verifyDelegate(
247247
// look for the public key that matches the key ID and use it for verification.
248248
var key = publicKeys.get(signature.getKeyId());
249249
if (key != null) {
250-
String publicKeyContents = key.getKeyVal().get("public");
251-
PublicKey pubKey;
252-
// TUF root version 4 and less is raw hex encoded key while 5+ is PEM.
253-
// TODO([email protected]): remove hex handling code once we upgrade the trusted root
254-
// to v5.
255-
if (publicKeyContents.startsWith("-----BEGIN PUBLIC KEY-----")) {
256-
pubKey = Keys.parsePublicKey(publicKeyContents.getBytes(StandardCharsets.UTF_8));
257-
} else {
258-
pubKey = Keys.constructTufPublicKey(Hex.decode(publicKeyContents), key.getScheme());
259-
}
260250
try {
261251
// while we error on keys that are not readable, we are intentionally more permissive
262252
// about signatures. If for ANY reason (except unparsed keys) we cannot validate a
263253
// signature, we continue as long as we find enough valid signatures within the
264254
// threshold. We still warn the user as this could be an indicator of data issues
265255
byte[] signatureBytes = Hex.decode(signature.getSignature());
266-
if (verifiers.newVerifier(pubKey).verify(verificationMaterial, signatureBytes)) {
256+
if (verifiers.newVerifier(key).verify(verificationMaterial, signatureBytes)) {
267257
goodSigs.add(signature.getKeyId());
258+
} else {
259+
log.log(
260+
Level.FINE,
261+
() ->
262+
String.format(
263+
Locale.ROOT,
264+
"TUF: ignored failed signature verification: '%s' for keyid: '%s'",
265+
signature.getSignature(),
266+
signature.getKeyId()));
268267
}
269268
} catch (SignatureException e) {
270269
log.log(
271270
Level.FINE,
272271
() ->
273272
String.format(
274273
Locale.ROOT,
275-
"TUF: ignored unverifiable signature: '%s' for keyid: '%s'",
274+
"TUF: ignored unverifiable signature: '%s' for keyid: '%s', because '%s'",
276275
signature.getSignature(),
277-
signature.getKeyId()));
276+
signature.getKeyId(),
277+
e.getMessage()));
278278
} catch (DecoderException | NoSuchAlgorithmException | InvalidKeyException e) {
279279
log.log(
280280
Level.WARNING,

sigstore-java/src/test/java/dev/sigstore/tuf/UpdaterTest.java

Lines changed: 6 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package dev.sigstore.tuf;
1717

1818
import static dev.sigstore.json.GsonSupplier.GSON;
19-
import static dev.sigstore.testkit.tuf.TestResources.UPDATER_REAL_TRUSTED_ROOT;
2019
import static dev.sigstore.testkit.tuf.TestResources.UPDATER_SYNTHETIC_TRUSTED_ROOT;
2120
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2221
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -27,12 +26,11 @@
2726

2827
import com.google.common.collect.ImmutableList;
2928
import com.google.common.collect.ImmutableMap;
30-
import com.google.common.hash.Hashing;
3129
import com.google.common.io.Resources;
3230
import com.google.gson.JsonSyntaxException;
33-
import dev.sigstore.encryption.signers.Verifier;
34-
import dev.sigstore.encryption.signers.Verifiers;
3531
import dev.sigstore.testkit.tuf.TestResources;
32+
import dev.sigstore.tuf.encryption.Verifier;
33+
import dev.sigstore.tuf.encryption.Verifiers;
3634
import dev.sigstore.tuf.model.Hashes;
3735
import dev.sigstore.tuf.model.ImmutableKey;
3836
import dev.sigstore.tuf.model.ImmutableRootRole;
@@ -41,7 +39,6 @@
4139
import dev.sigstore.tuf.model.Role;
4240
import dev.sigstore.tuf.model.Root;
4341
import dev.sigstore.tuf.model.Signature;
44-
import dev.sigstore.tuf.model.TargetMeta;
4542
import dev.sigstore.tuf.model.Targets;
4643
import io.github.netmikey.logunit.api.LogCapturer;
4744
import java.io.File;
@@ -52,8 +49,6 @@
5249
import java.nio.file.Path;
5350
import java.security.InvalidKeyException;
5451
import java.security.NoSuchAlgorithmException;
55-
import java.security.PublicKey;
56-
import java.security.SignatureException;
5752
import java.security.spec.InvalidKeySpecException;
5853
import java.time.Clock;
5954
import java.time.Instant;
@@ -123,19 +118,6 @@ static void startRemoteResourceServer() throws Exception {
123118
System.out.println("TUF local server listening on: " + remoteUrl);
124119
}
125120

126-
@Test
127-
public void testRootUpdate_fromProdData() throws Exception {
128-
setupMirror(
129-
"real/prod", "1.root.json", "2.root.json", "3.root.json", "4.root.json", "5.root.json");
130-
var updater = createTimeStaticUpdater(localStorePath, UPDATER_REAL_TRUSTED_ROOT);
131-
updater.updateRoot();
132-
assertStoreContains("root.json");
133-
Root oldRoot = TestResources.loadRoot(UPDATER_REAL_TRUSTED_ROOT);
134-
Root newRoot = TestResources.loadRoot(localStorePath.resolve("root.json"));
135-
assertRootVersionIncreased(oldRoot, newRoot);
136-
assertRootNotExpired(newRoot);
137-
}
138-
139121
@Test
140122
public void testRootUpdate_notEnoughSignatures()
141123
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
@@ -603,50 +585,6 @@ public void testTargetsDownload_sha256Only() throws Exception {
603585
assertDoesNotThrow(updater::update);
604586
}
605587

606-
// End to end sanity test on the actual prod sigstore repo.
607-
@Test
608-
public void testUpdate_fromProdData()
609-
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
610-
setupMirror(
611-
"real/prod",
612-
"1.root.json",
613-
"2.root.json",
614-
"3.root.json",
615-
"4.root.json",
616-
"5.root.json",
617-
"69.snapshot.json",
618-
"5.targets.json",
619-
"timestamp.json",
620-
"snapshot.json",
621-
"targets.json",
622-
"root.json",
623-
"targets/0ae7705e02db33e814329746a4a0e5603c5bdcd91c96d072158d71011a2695788866565a2fec0fe363eb72cbcaeda39e54c5fe8d416daf9f3101fdba4217ef35.rekor.pub",
624-
"targets/0f99f47dbc26c5f1e3cba0bfd9af4245a26e5cb735d6ef005792ec7e603f66fdb897de985973a6e50940ca7eff5e1849719e967b5ad2dac74a29115a41cf6f21.fulcio_intermediate_v1.crt.pem",
625-
"targets/4b20747d1afe2544238ad38cc0cc3010921b177d60ac743767e0ef675b915489bd01a36606c0ff83c06448622d7160f0d866c83d20f0c0f44653dcc3f9aa0bd4.ctfe.pub",
626-
"targets/308fd1d1d95d7f80aa33b837795251cc3e886792982275e062409e13e4e236ffc34d676682aa96fdc751414de99c864bf132dde71581fa651c6343905e3bf988.artifact.pub",
627-
"targets/0713252a7fd17f7f3ab12f88a64accf2eb14b8ad40ca711d7fe8b4ecba3b24db9e9dffadb997b196d3867b8f9ff217faf930d80e4dab4e235c7fc3f07be69224.fulcio.crt.pem",
628-
"targets/e83fa4f427b24ee7728637fad1b4aa45ebde2ba02751fa860694b1bb16059a490328f9985e51cc70e4d237545315a1bc866dc4fdeef2f6248d99cc7a6077bf85.ctfe_2022.pub",
629-
"targets/f2e33a6dc208cee1f51d33bbea675ab0f0ced269617497985f9a0680689ee7073e4b6f8fef64c91bda590d30c129b3070dddce824c05bc165ac9802f0705cab6.fulcio_v1.crt.pem");
630-
var updater = createTimeStaticUpdater(localStorePath, UPDATER_REAL_TRUSTED_ROOT);
631-
updater.update();
632-
633-
Root oldRoot = TestResources.loadRoot(UPDATER_REAL_TRUSTED_ROOT);
634-
TrustedMetaStore metaStore = updater.getMetaStore();
635-
TargetStore targetStore = updater.getTargetStore();
636-
Root newRoot = metaStore.getRoot(); // should be present
637-
assertRootVersionIncreased(oldRoot, newRoot);
638-
Targets targets = metaStore.getTargets(); // should be present
639-
Map<String, TargetMeta.TargetData> targetsData = targets.getSignedMeta().getTargets();
640-
for (String file : targetsData.keySet()) {
641-
TargetMeta.TargetData fileData = targetsData.get(file);
642-
byte[] fileBytes = targetStore.readTarget(file);
643-
assertNotNull(fileBytes, "each file from targets data should be present");
644-
assertEquals(fileData.getLength(), fileBytes.length, "file length should match metadata");
645-
assertEquals(
646-
fileData.getHashes().getSha512(), Hashing.sha512().hashBytes(fileBytes).toString());
647-
}
648-
}
649-
650588
private static final byte[] TEST_HASH_VERIFYIER_BYTES =
651589
"testdata".getBytes(StandardCharsets.UTF_8);
652590
private static final String GOOD_256_HASH =
@@ -941,8 +879,8 @@ public void testUpdate_snapshotsAndTimestampHaveNoSizeAndNoHashesInMeta() throws
941879

942880
@Test
943881
public void canCreateMultipleUpdaters() throws IOException {
944-
createTimeStaticUpdater(localStorePath, UPDATER_REAL_TRUSTED_ROOT);
945-
createTimeStaticUpdater(localStorePath, UPDATER_REAL_TRUSTED_ROOT);
882+
createTimeStaticUpdater(localStorePath, UPDATER_SYNTHETIC_TRUSTED_ROOT);
883+
createTimeStaticUpdater(localStorePath, UPDATER_SYNTHETIC_TRUSTED_ROOT);
946884
}
947885

948886
static Key newKey(String keyContents) {
@@ -1027,43 +965,7 @@ static void shutdownRemoteResourceServer() throws Exception {
1027965
}
1028966

1029967
public static final Verifiers.Supplier ALWAYS_VERIFIES =
1030-
publicKey ->
1031-
new Verifier() {
1032-
@Override
1033-
public PublicKey getPublicKey() {
1034-
return null;
1035-
}
1036-
1037-
@Override
1038-
public boolean verify(byte[] artifact, byte[] signature)
1039-
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
1040-
return true;
1041-
}
1042-
1043-
@Override
1044-
public boolean verifyDigest(byte[] artifactDigest, byte[] signature)
1045-
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
1046-
return true;
1047-
}
1048-
};
968+
(key) -> (Verifier) (artifactDigest, signature) -> true;
1049969
public static final Verifiers.Supplier ALWAYS_FAILS =
1050-
publicKey ->
1051-
new Verifier() {
1052-
@Override
1053-
public PublicKey getPublicKey() {
1054-
return null;
1055-
}
1056-
1057-
@Override
1058-
public boolean verify(byte[] artifact, byte[] signature)
1059-
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
1060-
return false;
1061-
}
1062-
1063-
@Override
1064-
public boolean verifyDigest(byte[] artifactDigest, byte[] signature)
1065-
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
1066-
return false;
1067-
}
1068-
};
970+
(key) -> (Verifier) (artifactDigest, signature) -> false;
1069971
}

tuf-cli/tuf-cli.xfails

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
test_metadata_bytes_match
22
test_client_downloads_expected_file_in_sub_dir
33
test_duplicate_sig_keyids
4-
test_keytype_and_scheme[rsa/rsassa-pss-sha256]
5-
test_keytype_and_scheme[ed25519/ed25519]
64
test_unusual_role_name[?]
75
test_unusual_role_name[#]
86
test_unusual_role_name[/delegatedrole]
@@ -26,20 +24,4 @@ test_targetfile_search[targetpath matches wildcard]
2624
test_targetfile_search[targetpath with separators x]
2725
test_targetfile_search[targetpath with separators y]
2826
test_targetfile_search[targetpath is not delegated by all roles in the chain]
29-
test_root_rotation[1-of-1-key-rotation]
30-
test_root_rotation[1-of-1-key-rotation-unused-signatures]
31-
test_root_rotation[3-of-5-sign-with-different-keycombos]
32-
test_root_rotation[3-of-5-one-key-rotated]
33-
test_root_rotation[3-of-5-one-key-rotated-with-intermediate-step]
34-
test_root_rotation[3-of-5-all-keys-rotated-with-intermediate-step]
35-
test_root_rotation[1-of-3-threshold-increase-to-2-of-3]
36-
test_root_rotation[2-of-3-threshold-decrease-to-1-of-3]
37-
test_root_rotation[1-of-2-threshold-increase-to-2-of-2]
38-
test_non_root_rotations[1-of-1-key-rotation]
39-
test_non_root_rotations[1-of-1-key-rotation-unused-signatures]
40-
test_non_root_rotations[3-of-5-sign-first-combo]
41-
test_non_root_rotations[3-of-5-sign-second-combo]
42-
test_non_root_rotations[3-of-5-sign-third-combo]
43-
test_non_root_rotations[3-of-5-sign-fourth-combo]
44-
test_non_root_rotations[3-of-5-sign-fifth-combo]
4527
test_snapshot_rollback[with hashes]

0 commit comments

Comments
 (0)