diff --git a/docs/validators/notaryv1.md b/docs/validators/notaryv1.md
index ef7d73eec..083678735 100644
--- a/docs/validators/notaryv1.md
+++ b/docs/validators/notaryv1.md
@@ -182,7 +182,7 @@ For more information on TUF roles, please refer to [TUF's documentation](https:/
| `name` | - | :heavy_check_mark: | See [basics](../basics.md#validators). |
| `type` | - | :heavy_check_mark: | `notaryv1`; the validator type must be set to `notaryv1`. |
| `host` | - | :heavy_check_mark: | URL of the Notary instance, in which the signatures reside, e.g. `notary.docker.io`. |
-| `trustRoots[*].name` | - | :heavy_check_mark: | See [basics](../basics.md#validators). Name of validator for reference in policy section. |
+| `trustRoots[*].name` | - | :heavy_check_mark: | See [basics](../basics.md#validators). Setting the name of trust root to "*" implements a logical `and` and enables multiple signature verification under any trust root in the validator. |
| `trustRoots[*].key` | - | :heavy_check_mark: | See [basics](../basics.md#validators). TUF public root key. |
| `auth` | - | - | Authentication credentials for the Notary server in case the trust data is not public. |
| `auth.secretName` | - | - | (Preferred over `username` + `password` combination.) Name of a Kubernetes secret that must exist in Connaisseur namespace beforehand. Create a file `auth.yaml` containing:
`username: `
`password: `
Run `kubectl create secret generic --from-file auth.yaml -n connaisseur` to create the secret.|
diff --git a/internal/validator/notaryv1/notaryserver/repo.go b/internal/validator/notaryv1/notaryserver/repo.go
index 7b3314b05..0128283f9 100644
--- a/internal/validator/notaryv1/notaryserver/repo.go
+++ b/internal/validator/notaryv1/notaryserver/repo.go
@@ -101,27 +101,23 @@ func (r *Repo) validateRoot(keys []data.PublicKey) error {
return fmt.Errorf("no signatures found for root")
}
- // verify signatures
- // it's not expected to have multiple signatures on the root, but we'll check them all
- // with all selected keys.
- for _, sig := range r.Root.Signatures {
- // reassign variable sig to inner variant to prevent possible implicit memory aliasing
- sig := sig
- errors := []error{}
-
- // for each signature, verify that at least one keys validates the signature
- for _, key := range keys {
+ // for each key, verify that at least one signature is valid
+ for _, key := range keys {
+ verified := false
+
+ for _, sig := range r.Root.Signatures {
+ sig := sig
logrus.Debugf("verifying root signature with key %s", key.ID())
+
if err = signed.VerifySignature(msg, &sig, key); err == nil && sig.IsValid {
logrus.Debugf("root signature verified with key %s", key.ID())
+ verified = true
break
}
- errors = append(errors, err)
- logrus.Debugf("root signature failed with key %s", key.ID())
}
- if err != nil || !sig.IsValid {
- return fmt.Errorf("error validating root signature: %+q", errors)
+ if !verified {
+ return fmt.Errorf("error validating root signature with key %s: %s", key.ID(), err)
}
}
diff --git a/internal/validator/notaryv1/notaryserver/repo_test.go b/internal/validator/notaryv1/notaryserver/repo_test.go
index f74cfc9d3..b88a27d43 100644
--- a/internal/validator/notaryv1/notaryserver/repo_test.go
+++ b/internal/validator/notaryv1/notaryserver/repo_test.go
@@ -147,29 +147,29 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaqgujac1VCdaGHKQMKoDn6/deWJ
"root trust data expired",
},
{ // 4
- "sample-image/root",
+ "07_root_multiple_signatures",
[]string{
`-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtR5kwrDK22SyCu7WMF8tCjVgeORA
-S2PWacRcBN/VQdVK4PVk1w4pMWlz9AHQthDGl+W2k3elHkPbR+gNkK2PCA==
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaqgujac1VCdaGHKQMKoDn6/deWJ
+8cCzsnDqGDgjPBayhJCQiI/qN+iBWEUPM7BkrCyDS878h+qd/MdZS22XwA==
-----END PUBLIC KEY-----`,
`-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvtc/qpHtx7iUUj+rRHR99a8mnGni
-qiGkmUb9YpWWTS4YwlvwdmMDiGzcsHiDOYz6f88u2hCRF5GUCvyiZAKrsA==
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnsF6hghdCI5MUEEge8PqtSe1HB3O
+88lEztfMO+LaUBCyX1aoeB36MrkqeM4zrWe2UUSxFuL6y/+qiPQQQ/n7Ww==
-----END PUBLIC KEY-----`,
},
"",
},
{ // 5
- "sample-image/root",
+ "07_root_multiple_signatures",
[]string{
`-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvtc/qpHtx7iUUj+rRHR99a8mnGni
-qiGkmUb9YpWWTS4YwlvwdmMDiGzcsHiDOYz6f88u2hCRF5GUCvyiZAKrsA==
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnsF6hghdCI5MUEEge8PqtSe1HB3O
+88lEztfMO+LaUBCyX1aoeB36MrkqeM4zrWe2UUSxFuL6y/+qiPQQQ/n7Ww==
-----END PUBLIC KEY-----`,
`-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtR5kwrDK22SyCu7WMF8tCjVgeORA
-S2PWacRcBN/VQdVK4PVk1w4pMWlz9AHQthDGl+W2k3elHkPbR+gNkK2PCA==
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaqgujac1VCdaGHKQMKoDn6/deWJ
+8cCzsnDqGDgjPBayhJCQiI/qN+iBWEUPM7BkrCyDS878h+qd/MdZS22XwA==
-----END PUBLIC KEY-----`,
},
"",
diff --git a/test/testdata/notaryv1/trust_data/07_root_multiple_signatures.json b/test/testdata/notaryv1/trust_data/07_root_multiple_signatures.json
new file mode 100644
index 000000000..d62c90af4
--- /dev/null
+++ b/test/testdata/notaryv1/trust_data/07_root_multiple_signatures.json
@@ -0,0 +1,76 @@
+{
+ "signed": {
+ "_type": "Root",
+ "consistent_snapshot": false,
+ "expires": "2999-08-26T14:12:13.21926558+02:00",
+ "keys": {
+ "52e8085cd75be93c5062130e08663a83a20f2eb9baf31f28eef4735e50f3a407": {
+ "keytype": "ecdsa-x509",
+ "keyval": {
+ "private": null,
+ "public": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ5akNDQVcrZ0F3SUJBZ0lVZnNnNXJ2THhSNkE1TUxLdFgrUE5lN0FoUDhJd0NnWUlLb1pJemowRUF3SXcKT1RFWk1CY0dBMVVFQXd3UWMyVmpkWEpsYzNsemRHVnRjeTVrWlRFTE1Ba0dBMVVFQmhNQ1JFVXhEekFOQmdOVgpCQWNNQmtKbGNteHBiakFnRncweU16QTRNVEV3T1RRM01EQmFHQTh5T1RrNE1EUXlNVEE1TkRjd01Gb3dPVEVaCk1CY0dBMVVFQXd3UWMyVmpkWEpsYzNsemRHVnRjeTVrWlRFTE1Ba0dBMVVFQmhNQ1JFVXhEekFOQmdOVkJBY00KQmtKbGNteHBiakJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRkdxb0xvMm5OVlFuV2hoeWtEQwpxQTUrdjNYbGlmSEFzN0p3NmhnNEl6d1dzb1NRa0lpUDZqZm9nVmhGRHpPd1pLd3NnMHZPL0lmcW5mekhXVXR0Cmw4Q2pVekJSTUIwR0ExVWREZ1FXQkJUUTNKaTFlbUU4K2wweGVLMzIvNHR0eUF6WU1qQWZCZ05WSFNNRUdEQVcKZ0JUUTNKaTFlbUU4K2wweGVLMzIvNHR0eUF6WU1qQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01Bb0dDQ3FHU000OQpCQU1DQTBrQU1FWUNJUUMvSVQ3VUNxOTBHbExiV2N6SWNwN3Nod0F0WFE3Nlg3R1FHR2lNbmpCb1R3SWhBUGw3Cll5b0JtOGliSzU2cFRycGkraS9CUXlRbEtTT1pSMXZCZWxmVFJnanoKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
+ }
+ },
+ "56ec6c5e5d48b72a26289a3230e78ffc4805f7ef74debb8fe2ad567166f51bb8": {
+ "keytype": "ecdsa",
+ "keyval": {
+ "private": null,
+ "public": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADZhwhf3fEcDYMRZZFGfQL7cRcbWMqKmaygKpi0RAdw/3cmTjwm8/c71FTbPqPTnbUKFBVDBpxCpsx8SiIcuFg=="
+ }
+ },
+ "7dc6224e24d6ce2b4260f138e4eff12645aa675b727722aebf86a3421530d886": {
+ "keytype": "ecdsa",
+ "keyval": {
+ "private": null,
+ "public": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEugAZDGWouB+x8W2J3dnOFMez4vijx4PgtREVwbp3NZvLYZSWd5NJwkBioDWiZITIpWVNQSLosqDNJ1xgSAF7bg=="
+ }
+ },
+ "d0cef4ea6349fcfbb961a917342a8c218126ebc7eba7ca4351812200207f4f18": {
+ "keytype": "ecdsa",
+ "keyval": {
+ "private": null,
+ "public": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnsF6hghdCI5MUEEge8PqtSe1HB3O88lEztfMO+LaUBCyX1aoeB36MrkqeM4zrWe2UUSxFuL6y/+qiPQQQ/n7Ww=="
+ }
+ }
+ },
+ "roles": {
+ "root": {
+ "keyids": [
+ "52e8085cd75be93c5062130e08663a83a20f2eb9baf31f28eef4735e50f3a407"
+ ],
+ "threshold": 1
+ },
+ "snapshot": {
+ "keyids": [
+ "56ec6c5e5d48b72a26289a3230e78ffc4805f7ef74debb8fe2ad567166f51bb8"
+ ],
+ "threshold": 1
+ },
+ "targets": {
+ "keyids": [
+ "d0cef4ea6349fcfbb961a917342a8c218126ebc7eba7ca4351812200207f4f18"
+ ],
+ "threshold": 1
+ },
+ "timestamp": {
+ "keyids": [
+ "7dc6224e24d6ce2b4260f138e4eff12645aa675b727722aebf86a3421530d886"
+ ],
+ "threshold": 1
+ }
+ },
+ "version": 1
+ },
+ "signatures": [
+ {
+ "keyid": "52e8085cd75be93c5062130e08663a83a20f2eb9baf31f28eef4735e50f3a407",
+ "method": "ecdsa",
+ "sig": "1NVDZSAph52T4lljxo5HXpmeiK76CM8B3pb1exJdnnqCcxR9z2wQgHR1P6PQOK7ITRcnRePjjmD4uyRBOHoJIw=="
+ },
+ {
+ "keyid": "d0cef4ea6349fcfbb961a917342a8c218126ebc7eba7ca4351812200207f4f18",
+ "method": "ecdsa",
+ "sig": "bHbVeHquTdZINHXvMl2rRqQY2uYQ/h4U5VesuHT3M7CkV7HrUaU/9pbsEBAR2wQSF0+Q9dvWRgUFVGDw2LvgSg=="
+ }
+ ]
+}