about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstore/store-api.cc8
-rw-r--r--src/libstore/store-api.hh3
-rw-r--r--src/nix/verify.cc46
3 files changed, 41 insertions, 16 deletions
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index b9939feda4..cc91ed2877 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -333,12 +333,18 @@ unsigned int ValidPathInfo::checkSignatures(const PublicKeys & publicKeys) const
 {
     unsigned int good = 0;
     for (auto & sig : sigs)
-        if (verifyDetached(fingerprint(), sig, publicKeys))
+        if (checkSignature(publicKeys, sig))
             good++;
     return good;
 }
 
 
+bool ValidPathInfo::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
+{
+    return verifyDetached(fingerprint(), sig, publicKeys);
+}
+
+
 }
 
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 4ea360b9d1..798054d166 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -127,6 +127,9 @@ struct ValidPathInfo
     /* Return the number of signatures on this .narinfo that were
        produced by one of the specified keys. */
     unsigned int checkSignatures(const PublicKeys & publicKeys) const;
+
+    /* Verify a single signature. */
+    bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
 };
 
 typedef list<ValidPathInfo> ValidPathInfos;
diff --git a/src/nix/verify.cc b/src/nix/verify.cc
index 0c05f450a4..9214d3b651 100644
--- a/src/nix/verify.cc
+++ b/src/nix/verify.cc
@@ -13,15 +13,17 @@ using namespace nix;
 struct MixVerify : virtual Args
 {
     bool noContents = false;
-    bool noSigs = false;
+    bool noTrust = false;
     Strings substituterUris;
+    size_t sigsNeeded;
 
     MixVerify()
     {
         mkFlag(0, "no-contents", "do not verify the contents of each store path", &noContents);
-        mkFlag(0, "no-sigs", "do not verify whether each store path has a valid signature", &noSigs);
+        mkFlag(0, "no-trust", "do not verify whether each store path is trusted", &noTrust);
         mkFlag('s', "substituter", {"store-uri"}, "use signatures from specified store", 1,
             [&](Strings ss) { substituterUris.push_back(ss.front()); });
+        mkIntFlag('n', "sigs-needed", "require that each path has at least N valid signatures", &sigsNeeded);
     }
 
     void verifyPaths(ref<Store> store, const Paths & storePaths)
@@ -85,28 +87,42 @@ struct MixVerify : virtual Args
 
                 }
 
-                if (!noSigs) {
+                if (!noTrust) {
 
                     bool good = false;
 
-                    if (info.ultimate)
+                    if (info.ultimate && !sigsNeeded)
                         good = true;
 
-                    if (!good && info.checkSignatures(publicKeys))
-                        good = true;
+                    else {
+
+                        StringSet sigsSeen;
+                        size_t actualSigsNeeded = sigsNeeded ? sigsNeeded : 1;
+                        size_t validSigs = 0;
+
+                        auto doSigs = [&](StringSet sigs) {
+                            for (auto sig : sigs) {
+                                if (sigsSeen.count(sig)) continue;
+                                sigsSeen.insert(sig);
+                                if (info.checkSignature(publicKeys, sig))
+                                    validSigs++;
+                            }
+                        };
+
+                        doSigs(info.sigs);
 
-                    if (!good) {
                         for (auto & store2 : substituters) {
-                            // FIXME: catch errors?
-                            if (!store2->isValidPath(storePath)) continue;
-                            auto info2 = store2->queryPathInfo(storePath);
-                            auto info3(info);
-                            info3.sigs = info2.sigs;
-                            if (info3.checkSignatures(publicKeys)) {
-                                good = true;
-                                break;
+                            if (validSigs >= actualSigsNeeded) break;
+                            try {
+                                if (!store2->isValidPath(storePath)) continue;
+                                doSigs(store2->queryPathInfo(storePath).sigs);
+                            } catch (Error & e) {
+                                printMsg(lvlError, format(ANSI_RED "error:" ANSI_NORMAL " %s") % e.what());
                             }
                         }
+
+                        if (validSigs >= actualSigsNeeded)
+                            good = true;
                     }
 
                     if (!good) {