From 2726a5d58d05babba56b32920ad256d0ed0fedb1 Mon Sep 17 00:00:00 2001 From: udf2457 Date: Fri, 19 Jun 2026 12:56:31 +0100 Subject: [PATCH] Address VerifyDelegate last-wins signature selection and uniqueness check At present `VerifyDelegate` has no `break` and the last matching signature wins silently. Adding the `break` statement makes it clear we only want one signature per keyID. In addition, it is slightly more efficient as we stop searching after finding a match Second, adding some defense in depth by catching duplicate keyIDs both during loading and verification. Signed-off-by: udf2457 --- metadata/metadata.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/metadata/metadata.go b/metadata/metadata.go index 2aec6f2d..c3d5f894 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -258,6 +258,26 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata log.Info("Verifying", "role", delegatedRole) + // Early check for duplicate signatures in the delegated metadata + switch d := delegatedMetadata.(type) { + case *Metadata[RootType]: + if err := checkUniqueSignatures(*d); err != nil { + return err + } + case *Metadata[SnapshotType]: + if err := checkUniqueSignatures(*d); err != nil { + return err + } + case *Metadata[TimestampType]: + if err := checkUniqueSignatures(*d); err != nil { + return err + } + case *Metadata[TargetsType]: + if err := checkUniqueSignatures(*d); err != nil { + return err + } + } + // collect keys, keyIDs and threshold based on delegator type switch i := i.(type) { // Root delegator @@ -365,6 +385,7 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata for _, signature := range d.Signatures { if signature.KeyID == keyID { sign = signature + break } } payload, err = cjson.EncodeCanonical(d.Signed) @@ -375,6 +396,7 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata for _, signature := range d.Signatures { if signature.KeyID == keyID { sign = signature + break } } payload, err = cjson.EncodeCanonical(d.Signed) @@ -385,6 +407,7 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata for _, signature := range d.Signatures { if signature.KeyID == keyID { sign = signature + break } } payload, err = cjson.EncodeCanonical(d.Signed) @@ -395,6 +418,7 @@ func (meta *Metadata[T]) VerifyDelegate(delegatedRole string, delegatedMetadata for _, signature := range d.Signatures { if signature.KeyID == keyID { sign = signature + break } } payload, err = cjson.EncodeCanonical(d.Signed)