Skip to content

Commit b440f06

Browse files
authored
Merge pull request #852 from sigstore/fix_tuf_dup_keys
Fix test_duplicate_sig_keyids
2 parents ebacd54 + 602aa10 commit b440f06

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2022 The Sigstore Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package dev.sigstore.tuf;
17+
18+
import dev.sigstore.tuf.model.Signature;
19+
import java.util.List;
20+
import java.util.Locale;
21+
import java.util.stream.Collectors;
22+
23+
/**
24+
* Thrown when the metadata has signatures from the same key even if the threshold is met. <a
25+
* href="https://theupdateframework.github.io/specification/latest/#file-formats-object-format">4.2.1</a>
26+
*/
27+
public class DuplicateKeyIdsException extends TufException {
28+
29+
private final List<Signature> signatures;
30+
private final String keyId;
31+
32+
public DuplicateKeyIdsException(List<Signature> signatures, String keyId) {
33+
super(
34+
String.format(
35+
Locale.ROOT,
36+
"The role has multiple signatures with the same key_id. [Signatures: %s, KeyId: %s]",
37+
signatures.stream()
38+
.map(Signature::getSignature)
39+
.collect(Collectors.joining(",", "(", ")")),
40+
keyId));
41+
this.signatures = signatures;
42+
this.keyId = keyId;
43+
}
44+
45+
public List<Signature> getSignatures() {
46+
return signatures;
47+
}
48+
49+
public String getKeyId() {
50+
return keyId;
51+
}
52+
}

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.Optional;
4343
import java.util.logging.Level;
4444
import java.util.logging.Logger;
45+
import java.util.stream.Collectors;
4546
import org.bouncycastle.util.encoders.DecoderException;
4647
import org.bouncycastle.util.encoders.Hex;
4748

@@ -211,8 +212,7 @@ private boolean hasNewKeys(RootRole oldRole, RootRole newRole) {
211212
}
212213

213214
void verifyDelegate(Root trustedRoot, SignedTufMeta<? extends TufMeta> delegate)
214-
throws SignatureVerificationException, IOException, NoSuchAlgorithmException,
215-
InvalidKeySpecException {
215+
throws SignatureVerificationException, IOException {
216216
verifyDelegate(
217217
delegate.getSignatures(),
218218
trustedRoot.getSignedMeta().getKeys(),
@@ -228,23 +228,31 @@ void verifyDelegate(Root trustedRoot, SignedTufMeta<? extends TufMeta> delegate)
228228
* @param role the key ids and threshold values for role signing
229229
* @param verificationMaterial the contents to be verified for authenticity
230230
* @throws SignatureVerificationException if there are not enough verified signatures
231+
* @throws IOException if an error occurred parsing a key
231232
*/
232233
@VisibleForTesting
233234
void verifyDelegate(
234235
List<Signature> signatures,
235236
Map<String, Key> publicKeys,
236237
Role role,
237238
byte[] verificationMaterial)
238-
throws InvalidKeySpecException, IOException, NoSuchAlgorithmException {
239+
throws IOException {
239240
// use set to not count the same key multiple times towards the threshold.
240241
var goodSigs = new HashSet<>(role.getKeyids().size() * 4 / 3);
241242
// role.getKeyIds() defines the keys allowed to sign for this role.
242243
for (String keyid : role.getKeyids()) {
243-
Optional<Signature> signatureMaybe =
244-
signatures.stream().filter(sig -> sig.getKeyId().equals(keyid)).findFirst();
244+
List<Signature> matchingSignatures =
245+
signatures.stream()
246+
.filter(sig -> sig.getKeyId().equals(keyid))
247+
.collect(Collectors.toList());
248+
// check for any duplicate key_ids:
249+
// https://theupdateframework.github.io/specification/latest/#file-formats-object-format
250+
if (matchingSignatures.size() > 1) {
251+
throw new DuplicateKeyIdsException(matchingSignatures, keyid);
252+
}
245253
// only verify if we find a signature that matches an allowed key id.
246-
if (signatureMaybe.isPresent()) {
247-
var signature = signatureMaybe.get();
254+
if (matchingSignatures.size() == 1) {
255+
var signature = matchingSignatures.get(0);
248256
// look for the public key that matches the key ID and use it for verification.
249257
var key = publicKeys.get(signature.getKeyId());
250258
if (key != null) {

tuf-cli/tuf-cli.xfails

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
test_metadata_bytes_match
2-
test_duplicate_sig_keyids
32
test_unusual_role_name[?]
43
test_unusual_role_name[#]
54
test_unusual_role_name[/delegatedrole]

0 commit comments

Comments
 (0)