diff --git a/src/libstore-tests/common-protocol.cc b/src/libstore-tests/common-protocol.cc index da4d704c130..f318c8ec631 100644 --- a/src/libstore-tests/common-protocol.cc +++ b/src/libstore-tests/common-protocol.cc @@ -140,8 +140,8 @@ CHARACTERIZATION_TEST( .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, .signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }, }, { @@ -160,8 +160,8 @@ READ_CHARACTERIZATION_TEST( .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, .signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }, }, { diff --git a/src/libstore-tests/nar-info.cc b/src/libstore-tests/nar-info.cc index cc9707218e4..e71575d2d44 100644 --- a/src/libstore-tests/nar-info.cc +++ b/src/libstore-tests/nar-info.cc @@ -60,8 +60,8 @@ static NarInfo makeNarInfo(const Store & store, bool includeImpureInfo) info.registrationTime = 23423; info.ultimate = true; info.sigs = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }; info.url = "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz"; diff --git a/src/libstore-tests/path-info.cc b/src/libstore-tests/path-info.cc index 795ad2f5388..05bcf2c117d 100644 --- a/src/libstore-tests/path-info.cc +++ b/src/libstore-tests/path-info.cc @@ -67,8 +67,8 @@ static ValidPathInfo makeFullKeyed(const Store & store, bool includeImpureInfo) info.registrationTime = 23423; info.ultimate = true; info.sigs = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }; } return info; diff --git a/src/libstore-tests/realisation.cc b/src/libstore-tests/realisation.cc index ec94a6ac4a2..d2d7df80f59 100644 --- a/src/libstore-tests/realisation.cc +++ b/src/libstore-tests/realisation.cc @@ -71,7 +71,7 @@ INSTANTIATE_TEST_SUITE_P( auto r = simple; // FIXME actually sign properly r.signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, }; return r; }(), diff --git a/src/libstore-tests/serve-protocol.cc b/src/libstore-tests/serve-protocol.cc index 3a60f8b31da..4be4b006015 100644 --- a/src/libstore-tests/serve-protocol.cc +++ b/src/libstore-tests/serve-protocol.cc @@ -111,8 +111,8 @@ VERSIONED_CHARACTERIZATION_TEST( .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, .signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }, }, { @@ -133,8 +133,8 @@ VERSIONED_READ_CHARACTERIZATION_TEST( .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, .signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }, }, { @@ -329,8 +329,8 @@ VERSIONED_CHARACTERIZATION_TEST( info.narSize = 34878; info.sigs = { - "fake-sig-1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "fake-sig-2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "fake-sig-1", .sig = std::string(64, '\0')}, + Signature{.keyName = "fake-sig-2", .sig = std::string(64, '\0')}, }, static_cast(std::move(info)); }), diff --git a/src/libstore-tests/worker-protocol.cc b/src/libstore-tests/worker-protocol.cc index 6175b6ccba8..ac4aafe7bf3 100644 --- a/src/libstore-tests/worker-protocol.cc +++ b/src/libstore-tests/worker-protocol.cc @@ -164,8 +164,8 @@ VERSIONED_CHARACTERIZATION_TEST( .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, .signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }, }, { @@ -186,8 +186,8 @@ VERSIONED_READ_CHARACTERIZATION_TEST( .outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"}, .signatures = { - "asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "asdf", .sig = std::string(64, '\0')}, + Signature{.keyName = "qwer", .sig = std::string(64, '\0')}, }, }, { @@ -549,8 +549,8 @@ VERSIONED_CHARACTERIZATION_TEST( info.narSize = 34878; info.sigs = { - "fake-sig-1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "fake-sig-2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + Signature{.keyName = "fake-sig-1", .sig = std::string(64, '\0')}, + Signature{.keyName = "fake-sig-2", .sig = std::string(64, '\0')}, }, info; }), diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index b5a47c6ce0f..c5109be9cbb 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -565,7 +565,7 @@ std::shared_ptr BinaryCacheStore::getFSAccessor(const StorePath return getRemoteFSAccessor(requireValidPath)->accessObject(storePath); } -void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSet & sigs) +void BinaryCacheStore::addSignatures(const StorePath & storePath, const std::set & sigs) { /* Note: this is inherently racy since there is no locking on binary caches. In particular, with S3 this unreliable, even diff --git a/src/libstore/common-protocol.cc b/src/libstore/common-protocol.cc index b069c949823..77c1ba3ca8c 100644 --- a/src/libstore/common-protocol.cc +++ b/src/libstore/common-protocol.cc @@ -6,6 +6,7 @@ #include "nix/store/common-protocol-impl.hh" #include "nix/util/archive.hh" #include "nix/store/derivations.hh" +#include "nix/util/signature/local-keys.hh" #include @@ -99,4 +100,15 @@ void CommonProto::Serialise>::write( conn.to << (caOpt ? renderContentAddress(*caOpt) : ""); } +Signature CommonProto::Serialise::read(const StoreDirConfig & store, CommonProto::ReadConn conn) +{ + return Signature::parse(readString(conn.from)); +} + +void CommonProto::Serialise::write( + const StoreDirConfig & store, CommonProto::WriteConn conn, const Signature & sig) +{ + conn.to << sig.to_string(); +} + } // namespace nix diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 630ebb68b91..f43a60bcd69 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -861,7 +861,7 @@ static void performOp( case WorkerProto::Op::AddSignatures: { auto path = WorkerProto::Serialise::read(*store, rconn); - StringSet sigs = readStrings(conn.from); + auto sigs = WorkerProto::Serialise>::read(*store, rconn); logger->startWork(); store->addSignatures(path, sigs); logger->stopWork(); @@ -886,7 +886,7 @@ static void performOp( info.deriver = std::move(deriver); info.references = WorkerProto::Serialise::read(*store, rconn); conn.from >> info.registrationTime >> info.narSize >> info.ultimate; - info.sigs = readStrings(conn.from); + info.sigs = WorkerProto::Serialise>::read(*store, rconn); info.ca = ContentAddress::parseOpt(readString(conn.from)); conn.from >> repair >> dontCheckSigs; if (!trusted && dontCheckSigs) diff --git a/src/libstore/include/nix/store/binary-cache-store.hh b/src/libstore/include/nix/store/binary-cache-store.hh index 80c0bc9d0cb..e7ff7905231 100644 --- a/src/libstore/include/nix/store/binary-cache-store.hh +++ b/src/libstore/include/nix/store/binary-cache-store.hh @@ -205,7 +205,7 @@ public: std::shared_ptr getFSAccessor(const StorePath &, bool requireValidPath = true) override; - void addSignatures(const StorePath & storePath, const StringSet & sigs) override; + void addSignatures(const StorePath & storePath, const std::set & sigs) override; std::optional getBuildLogExact(const StorePath & path) override; diff --git a/src/libstore/include/nix/store/common-protocol.hh b/src/libstore/include/nix/store/common-protocol.hh index c1d22fa6c54..f0707b76c75 100644 --- a/src/libstore/include/nix/store/common-protocol.hh +++ b/src/libstore/include/nix/store/common-protocol.hh @@ -13,6 +13,7 @@ class StorePath; struct ContentAddress; struct DrvOutput; struct Realisation; +struct Signature; /** * Shared serializers between the worker protocol, serve protocol, and a @@ -72,6 +73,8 @@ template<> DECLARE_COMMON_SERIALISER(DrvOutput); template<> DECLARE_COMMON_SERIALISER(Realisation); +template<> +DECLARE_COMMON_SERIALISER(Signature); #define COMMA_ , template diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh index adbf2b26efe..5460c6e786a 100644 --- a/src/libstore/include/nix/store/local-store.hh +++ b/src/libstore/include/nix/store/local-store.hh @@ -368,7 +368,7 @@ public: void vacuumDB(); - void addSignatures(const StorePath & storePath, const StringSet & sigs) override; + void addSignatures(const StorePath & storePath, const std::set & sigs) override; /** * If free disk space in /nix/store if below minFree, delete diff --git a/src/libstore/include/nix/store/path-info.hh b/src/libstore/include/nix/store/path-info.hh index dbcd933f426..44840b0e5e4 100644 --- a/src/libstore/include/nix/store/path-info.hh +++ b/src/libstore/include/nix/store/path-info.hh @@ -102,7 +102,7 @@ struct UnkeyedValidPathInfo */ bool ultimate = false; - StringSet sigs; // note: not necessarily verified + std::set sigs; /** * If non-empty, an assertion that the path is content-addressed, @@ -200,7 +200,7 @@ struct ValidPathInfo : virtual UnkeyedValidPathInfo /** * Verify a single signature. */ - bool checkSignature(const StoreDirConfig & store, const PublicKeys & publicKeys, const std::string & sig) const; + bool checkSignature(const StoreDirConfig & store, const PublicKeys & publicKeys, const Signature & sig) const; /** * References as store path basenames, including a self reference if it has one. diff --git a/src/libstore/include/nix/store/realisation.hh b/src/libstore/include/nix/store/realisation.hh index 3691c14fb79..55597acb7c0 100644 --- a/src/libstore/include/nix/store/realisation.hh +++ b/src/libstore/include/nix/store/realisation.hh @@ -54,13 +54,13 @@ struct UnkeyedRealisation { StorePath outPath; - StringSet signatures; + std::set signatures; std::string fingerprint(const DrvOutput & key) const; void sign(const DrvOutput & key, const Signer &); - bool checkSignature(const DrvOutput & key, const PublicKeys & publicKeys, const std::string & sig) const; + bool checkSignature(const DrvOutput & key, const PublicKeys & publicKeys, const Signature & sig) const; size_t checkSignatures(const DrvOutput & key, const PublicKeys & publicKeys) const; diff --git a/src/libstore/include/nix/store/remote-store.hh b/src/libstore/include/nix/store/remote-store.hh index 33f518fe970..73de1795def 100644 --- a/src/libstore/include/nix/store/remote-store.hh +++ b/src/libstore/include/nix/store/remote-store.hh @@ -140,7 +140,7 @@ struct RemoteStore : public virtual Store, public virtual GcStore, public virtua unsupported("repairPath"); } - void addSignatures(const StorePath & storePath, const StringSet & sigs) override; + void addSignatures(const StorePath & storePath, const std::set & sigs) override; MissingPaths queryMissing(const std::vector & targets) override; diff --git a/src/libstore/include/nix/store/store-api.hh b/src/libstore/include/nix/store/store-api.hh index c4c9bc93051..2fb8a42605d 100644 --- a/src/libstore/include/nix/store/store-api.hh +++ b/src/libstore/include/nix/store/store-api.hh @@ -754,7 +754,7 @@ public: * Add signatures to the specified store path. The signatures are * not verified. */ - virtual void addSignatures(const StorePath & storePath, const StringSet & sigs) + virtual void addSignatures(const StorePath & storePath, const std::set & sigs) { unsupported("addSignatures"); } diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 3b466c9bb8b..1e89f991527 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -153,7 +153,9 @@ void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source, Rep << (info.deriver ? printStorePath(*info.deriver) : "") << info.narHash.to_string(HashFormat::Base16, false); ServeProto::write(*this, *conn, info.references); - conn->to << info.registrationTime << info.narSize << info.ultimate << info.sigs << renderContentAddress(info.ca); + conn->to << info.registrationTime << info.narSize << info.ultimate; + ServeProto::write(*this, *conn, info.sigs); + conn->to << renderContentAddress(info.ca); try { copyNAR(source, conn->to); } catch (...) { diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 4b92d752291..8ed072378da 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -619,7 +619,8 @@ void LocalStore::registerDrvOutput(const Realisation & info) auto combinedSignatures = oldR->signatures; combinedSignatures.insert(info.signatures.begin(), info.signatures.end()); state->stmts->UpdateRealisedOutput - .use()(concatStringsSep(" ", combinedSignatures))(info.id.strHash())(info.id.outputName) + .use()(concatStringsSep(" ", Signature::toStrings(combinedSignatures)))(info.id.strHash())( + info.id.outputName) .exec(); } else { throw Error( @@ -634,7 +635,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) } else { state->stmts->RegisterRealisedOutput .use()(info.id.strHash())(info.id.outputName)(printStorePath(info.outPath))( - concatStringsSep(" ", info.signatures)) + concatStringsSep(" ", Signature::toStrings(info.signatures))) .exec(); } }); @@ -659,7 +660,8 @@ uint64_t LocalStore::addValidPath(State & state, const ValidPathInfo & info) info.registrationTime == 0 ? time(0) : info.registrationTime)( info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver)(info.narSize, info.narSize != 0)(info.ultimate ? 1 : 0, info.ultimate)( - concatStringsSep(" ", info.sigs), !info.sigs.empty())(renderContentAddress(info.ca), (bool) info.ca) + concatStringsSep(" ", Signature::toStrings(info.sigs)), + !info.sigs.empty())(renderContentAddress(info.ca), (bool) info.ca) .exec(); uint64_t id = state.db.getLastInsertedRowId(); @@ -737,7 +739,7 @@ std::shared_ptr LocalStore::queryPathInfoInternal(State & s s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 6); if (s) - info->sigs = tokenizeString(s, " "); + info->sigs = Signature::parseMany(tokenizeString(s, " ")); s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 7); if (s) @@ -757,7 +759,8 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) { state.stmts->UpdatePathInfo .use()(info.narSize, info.narSize != 0)(info.narHash.to_string(HashFormat::Base16, true))( - info.ultimate ? 1 : 0, info.ultimate)(concatStringsSep(" ", info.sigs), !info.sigs.empty())( + info.ultimate ? 1 : 0, + info.ultimate)(concatStringsSep(" ", Signature::toStrings(info.sigs)), !info.sigs.empty())( renderContentAddress(info.ca), (bool) info.ca)(printStorePath(info.path)) .exec(); } @@ -1484,7 +1487,7 @@ void LocalStore::vacuumDB() _state->lock()->db.exec("vacuum"); } -void LocalStore::addSignatures(const StorePath & storePath, const StringSet & sigs) +void LocalStore::addSignatures(const StorePath & storePath, const std::set & sigs) { retrySQLite([&]() { auto state(_state->lock()); @@ -1515,7 +1518,7 @@ LocalStore::queryRealisationCore_(LocalStore::State & state, const DrvOutput & i {realisationDbId, UnkeyedRealisation{ .outPath = outputPath, - .signatures = signatures, + .signatures = Signature::parseMany(signatures), }}}; } diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index c32c6cd2b31..bf7ab4bf933 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -277,7 +277,7 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache if (!queryNAR.isNull(9)) narInfo->deriver = StorePath(queryNAR.getStr(9)); for (auto & sig : tokenizeString(queryNAR.getStr(10), " ")) - narInfo->sigs.insert(sig); + narInfo->sigs.insert(Signature::parse(sig)); narInfo->ca = ContentAddress::parseOpt(queryNAR.getStr(11)); return {oValid, narInfo}; @@ -337,8 +337,9 @@ class NarInfoDiskCacheImpl : public NarInfoDiskCache narInfo && narInfo->fileHash)( narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize)(info->narHash.to_string( HashFormat::Nix32, true))(info->narSize)(concatStringsSep(" ", info->shortRefs()))( - info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver)( - concatStringsSep(" ", info->sigs))(renderContentAddress(info->ca))(time(0)) + info->deriver ? std::string(info->deriver->to_string()) : "", + (bool) info->deriver)(concatStringsSep(" ", Signature::toStrings(info->sigs)))( + renderContentAddress(info->ca))(time(0)) .exec(); } else { diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 27ed5a76143..b912467d8a8 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -78,7 +78,7 @@ NarInfo::NarInfo(const StoreDirConfig & store, const std::string & s, const std: if (value != "unknown-deriver") deriver = StorePath(value); } else if (name == "Sig") - sigs.insert(value); + sigs.insert(Signature::parse(value)); else if (name == "CA") { if (ca) throw corrupt("extra CA"); @@ -124,7 +124,7 @@ std::string NarInfo::to_string(const StoreDirConfig & store) const res += "Deriver: " + std::string(deriver->to_string()) + "\n"; for (const auto & sig : sigs) - res += "Sig: " + sig + "\n"; + res += "Sig: " + sig.to_string() + "\n"; if (ca) res += "CA: " + renderContentAddress(*ca) + "\n"; diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index ebab52cec81..1c631cf147f 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -129,7 +129,7 @@ size_t ValidPathInfo::checkSignatures(const StoreDirConfig & store, const Public } bool ValidPathInfo::checkSignature( - const StoreDirConfig & store, const PublicKeys & publicKeys, const std::string & sig) const + const StoreDirConfig & store, const PublicKeys & publicKeys, const Signature & sig) const { return verifyDetached(fingerprint(store), sig, publicKeys); } @@ -211,9 +211,7 @@ UnkeyedValidPathInfo::toJSON(const StoreDirConfig * store, bool includeImpureInf jsonObject["ultimate"] = ultimate; - auto & sigsObj = jsonObject["signatures"] = json::array(); - for (auto & sig : sigs) - sigsObj.push_back(sig); + jsonObject["signatures"] = sigs; } return jsonObject; @@ -287,7 +285,7 @@ UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON(const StoreDirConfig * store res.ultimate = getBoolean(*rawUltimate); if (auto * rawSignatures = optionalValueAt(json, "signatures")) - res.sigs = getStringSet(*rawSignatures); + res.sigs = *rawSignatures; return res; } diff --git a/src/libstore/realisation.cc b/src/libstore/realisation.cc index 2075b39e6fb..433d67f26ba 100644 --- a/src/libstore/realisation.cc +++ b/src/libstore/realisation.cc @@ -38,7 +38,7 @@ void UnkeyedRealisation::sign(const DrvOutput & key, const Signer & signer) } bool UnkeyedRealisation::checkSignature( - const DrvOutput & key, const PublicKeys & publicKeys, const std::string & sig) const + const DrvOutput & key, const PublicKeys & publicKeys, const Signature & sig) const { return verifyDetached(fingerprint(key), sig, publicKeys); } @@ -86,13 +86,13 @@ UnkeyedRealisation adl_serializer::from_json(const json & js { auto json = getObject(json0); - StringSet signatures; - if (auto signaturesOpt = optionalValueAt(json, "signatures")) - signatures = *signaturesOpt; - return UnkeyedRealisation{ .outPath = valueAt(json, "outPath"), - .signatures = signatures, + .signatures = [&] -> std::set { + if (auto signaturesOpt = optionalValueAt(json, "signatures")) + return *signaturesOpt; + return {}; + }(), }; } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 22af53ae882..137f8edeecd 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -437,8 +437,9 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, Repair WorkerProto::write(*this, *conn, info.deriver); conn->to << info.narHash.to_string(HashFormat::Base16, false); WorkerProto::write(*this, *conn, info.references); - conn->to << info.registrationTime << info.narSize << info.ultimate << info.sigs << renderContentAddress(info.ca) - << repair << !checkSigs; + conn->to << info.registrationTime << info.narSize << info.ultimate; + WorkerProto::write(*this, *conn, info.sigs); + conn->to << renderContentAddress(info.ca) << repair << !checkSigs; if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 23) { conn.withFramedSink([&](Sink & sink) { copyNAR(source, sink); }); @@ -733,12 +734,12 @@ bool RemoteStore::verifyStore(bool checkContents, RepairFlag repair) return readInt(conn->from); } -void RemoteStore::addSignatures(const StorePath & storePath, const StringSet & sigs) +void RemoteStore::addSignatures(const StorePath & storePath, const std::set & sigs) { auto conn(getConnection()); conn->to << WorkerProto::Op::AddSignatures; WorkerProto::write(*this, *conn, storePath); - conn->to << sigs; + WorkerProto::write(*this, *conn, sigs); conn.processStderr(); readInt(conn->from); } diff --git a/src/libstore/restricted-store.cc b/src/libstore/restricted-store.cc index b4d696a536b..c0bd273322f 100644 --- a/src/libstore/restricted-store.cc +++ b/src/libstore/restricted-store.cc @@ -134,7 +134,7 @@ struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStor void collectGarbage(const GCOptions & options, GCResults & results) override {} - void addSignatures(const StorePath & storePath, const StringSet & sigs) override + void addSignatures(const StorePath & storePath, const std::set & sigs) override { unsupported("addSignatures"); } diff --git a/src/libstore/serve-protocol.cc b/src/libstore/serve-protocol.cc index e6c9eabd22c..b44d7a959dc 100644 --- a/src/libstore/serve-protocol.cc +++ b/src/libstore/serve-protocol.cc @@ -93,7 +93,7 @@ UnkeyedValidPathInfo ServeProto::Serialise::read(const Sto if (!s.empty()) info.narHash = Hash::parseAnyPrefixed(s); info.ca = ContentAddress::parseOpt(readString(conn.from)); - info.sigs = readStrings(conn.from); + info.sigs = ServeProto::Serialise>::read(store, conn); } return info; @@ -108,8 +108,10 @@ void ServeProto::Serialise::write( // !!! Maybe we want compression? conn.to << info.narSize // downloadSize, lie a little << info.narSize; - if (GET_PROTOCOL_MINOR(conn.version) >= 4) - conn.to << info.narHash.to_string(HashFormat::Nix32, true) << renderContentAddress(info.ca) << info.sigs; + if (GET_PROTOCOL_MINOR(conn.version) >= 4) { + conn.to << info.narHash.to_string(HashFormat::Nix32, true) << renderContentAddress(info.ca); + ServeProto::write(store, conn, info.sigs); + } } ServeProto::BuildOptions diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index 2788222c0d7..83eb6691e8d 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -300,7 +300,7 @@ UnkeyedValidPathInfo WorkerProto::Serialise::read(const St conn.from >> info.registrationTime >> info.narSize; if (GET_PROTOCOL_MINOR(conn.version) >= 16) { conn.from >> info.ultimate; - info.sigs = readStrings(conn.from); + info.sigs = WorkerProto::Serialise>::read(store, conn); info.ca = ContentAddress::parseOpt(readString(conn.from)); } return info; @@ -314,7 +314,9 @@ void WorkerProto::Serialise::write( WorkerProto::write(store, conn, pathInfo.references); conn.to << pathInfo.registrationTime << pathInfo.narSize; if (GET_PROTOCOL_MINOR(conn.version) >= 16) { - conn.to << pathInfo.ultimate << pathInfo.sigs << renderContentAddress(pathInfo.ca); + conn.to << pathInfo.ultimate; + WorkerProto::write(store, conn, pathInfo.sigs); + conn.to << renderContentAddress(pathInfo.ca); } } diff --git a/src/libutil/include/nix/util/signature/local-keys.hh b/src/libutil/include/nix/util/signature/local-keys.hh index 1c0579ce9ec..789fb831f0f 100644 --- a/src/libutil/include/nix/util/signature/local-keys.hh +++ b/src/libutil/include/nix/util/signature/local-keys.hh @@ -1,30 +1,44 @@ #pragma once ///@file -#include "nix/util/types.hh" +#include "nix/util/json-impls.hh" #include namespace nix { /** - * Except where otherwise noted, Nix serializes keys and signatures in - * the form: + * A cryptographic signature along with the name of the key that produced it. * - * ``` - * : - * ``` + * Serialized as `:`. */ -struct BorrowedCryptoValue +struct Signature { - std::string_view name; - std::string_view payload; + std::string keyName; /** - * This splits on the colon, the user can then separated decode the - * Base64 payload separately. + * The raw decoded signature bytes. */ - static BorrowedCryptoValue parse(std::string_view); + std::string sig; + + /** + * Parse a signature in the format `:`. + */ + static Signature parse(std::string_view); + + /** + * Parse multiple signatures from a container of strings. + * + * Each string must be in the format `:`. + */ + template + static std::set parseMany(const Container & sigStrs); + + std::string to_string() const; + + static Strings toStrings(const std::set & sigs); + + auto operator<=>(const Signature &) const = default; }; struct Key @@ -61,7 +75,7 @@ struct SecretKey : Key /** * Return a detached signature of the given string. */ - std::string signDetached(std::string_view s) const; + Signature signDetached(std::string_view s) const; PublicKey toPublicKey() const; @@ -82,16 +96,15 @@ struct PublicKey : Key * @return true iff `sig` and this key's names match, and `sig` is a * correct signature over `data` using the given public key. */ - bool verifyDetached(std::string_view data, std::string_view sigs) const; + bool verifyDetached(std::string_view data, const Signature & sig) const; /** * @return true iff `sig` is a correct signature over `data` using the * given public key. * - * @param just the Base64 signature itself, not a colon-separated pair of a - * public key name and signature. + * @param sig the raw signature bytes (not Base64 encoded). */ - bool verifyDetachedAnon(std::string_view data, std::string_view sigs) const; + bool verifyDetachedAnon(std::string_view data, const Signature & sig) const; private: PublicKey(std::string_view name, std::string && key) @@ -110,6 +123,8 @@ typedef std::map PublicKeys; * @return true iff ‘sig’ is a correct signature over ‘data’ using one * of the given public keys. */ -bool verifyDetached(std::string_view data, std::string_view sig, const PublicKeys & publicKeys); +bool verifyDetached(std::string_view data, const Signature & sig, const PublicKeys & publicKeys); } // namespace nix + +JSON_IMPL(nix::Signature) diff --git a/src/libutil/include/nix/util/signature/signer.hh b/src/libutil/include/nix/util/signature/signer.hh index 074c0c6e596..d03dbe97546 100644 --- a/src/libutil/include/nix/util/signature/signer.hh +++ b/src/libutil/include/nix/util/signature/signer.hh @@ -29,7 +29,7 @@ struct Signer * signature](https://en.wikipedia.org/wiki/Detached_signature), * i.e. just the signature itself without a copy of the signed data. */ - virtual std::string signDetached(std::string_view data) const = 0; + virtual Signature signDetached(std::string_view data) const = 0; /** * View the public key associated with this `Signer`. @@ -48,7 +48,7 @@ struct LocalSigner : Signer { LocalSigner(SecretKey && privateKey); - std::string signDetached(std::string_view s) const override; + Signature signDetached(std::string_view s) const override; const PublicKey & getPublicKey() override; diff --git a/src/libutil/signature/local-keys.cc b/src/libutil/signature/local-keys.cc index 7dcd92c72ae..51f94cee006 100644 --- a/src/libutil/signature/local-keys.cc +++ b/src/libutil/signature/local-keys.cc @@ -1,36 +1,96 @@ -#include "nix/util/signature/local-keys.hh" +#include +#include +#include -#include "nix/util/file-system.hh" #include "nix/util/base-n.hh" +#include "nix/util/signature/local-keys.hh" +#include "nix/util/json-utils.hh" #include "nix/util/util.hh" -#include namespace nix { -BorrowedCryptoValue BorrowedCryptoValue::parse(std::string_view s) +namespace { + +/** + * Parse a colon-separated string where the second part is Base64-encoded. + * + * @param s The string to parse in the format `:`. + * @param typeName Name of the type being parsed (for error messages). + * @return A pair of (name, decoded-data). + */ +std::pair parseColonBase64(std::string_view s, std::string_view typeName) { size_t colon = s.find(':'); if (colon == std::string::npos || colon == 0) - return {"", ""}; - return {s.substr(0, colon), s.substr(colon + 1)}; + throw FormatError("%s is corrupt", typeName); + + auto name = std::string(s.substr(0, colon)); + auto data = base64::decode(s.substr(colon + 1)); + + if (name.empty() || data.empty()) + throw FormatError("%s is corrupt", typeName); + + return {std::move(name), std::move(data)}; } -Key::Key(std::string_view s, bool sensitiveValue) +/** + * Serialize a name and data to a colon-separated string with Base64 encoding. + * + * @param name The name part. + * @param data The raw data to be Base64-encoded. + * @return A string in the format `:`. + */ +std::string serializeColonBase64(std::string_view name, std::string_view data) +{ + return std::string(name) + ":" + base64::encode(std::as_bytes(std::span{data.data(), data.size()})); +} + +} // anonymous namespace + +Signature Signature::parse(std::string_view s) +{ + auto [keyName, sig] = parseColonBase64(s, "signature"); + return Signature{ + .keyName = std::move(keyName), + .sig = std::move(sig), + }; +} + +std::string Signature::to_string() const { - auto ss = BorrowedCryptoValue::parse(s); + return serializeColonBase64(keyName, sig); +} - name = ss.name; - key = ss.payload; +template +std::set Signature::parseMany(const Container & sigStrs) +{ + auto parsed = sigStrs | std::views::transform([](const auto & s) { return Signature::parse(s); }); + return std::set(parsed.begin(), parsed.end()); +} - try { - if (name == "" || key == "") - throw FormatError("key is corrupt"); +template std::set Signature::parseMany(const Strings &); +template std::set Signature::parseMany(const StringSet &); - key = base64::decode(key); +Strings Signature::toStrings(const std::set & sigs) +{ + Strings res; + for (const auto & sig : sigs) { + res.push_back(sig.to_string()); + } + + return res; +} + +Key::Key(std::string_view s, bool sensitiveValue) +{ + try { + auto [parsedName, parsedKey] = parseColonBase64(s, "key"); + name = std::move(parsedName); + key = std::move(parsedKey); } catch (Error & e) { std::string extra; if (!sensitiveValue) - extra = fmt(" with raw value '%s'", key); + extra = fmt(" with raw value '%s'", s); e.addTrace({}, "while decoding key named '%s'%s", name, extra); throw; } @@ -38,7 +98,7 @@ Key::Key(std::string_view s, bool sensitiveValue) std::string Key::to_string() const { - return name + ":" + base64::encode(std::as_bytes(std::span{key})); + return serializeColonBase64(name, key); } SecretKey::SecretKey(std::string_view s) @@ -48,12 +108,15 @@ SecretKey::SecretKey(std::string_view s) throw Error("secret key is not valid"); } -std::string SecretKey::signDetached(std::string_view data) const +Signature SecretKey::signDetached(std::string_view data) const { unsigned char sig[crypto_sign_BYTES]; unsigned long long sigLen; crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(), (unsigned char *) key.data()); - return name + ":" + base64::encode(std::as_bytes(std::span(sig, sigLen))); + return Signature{ + .keyName = name, + .sig = std::string((char *) sig, sigLen), + }; } PublicKey SecretKey::toPublicKey() const @@ -80,41 +143,47 @@ PublicKey::PublicKey(std::string_view s) throw Error("public key is not valid"); } -bool PublicKey::verifyDetached(std::string_view data, std::string_view sig) const +bool PublicKey::verifyDetached(std::string_view data, const Signature & sig) const { - auto ss = BorrowedCryptoValue::parse(sig); - - if (ss.name != std::string_view{name}) + if (sig.keyName != name) return false; - return verifyDetachedAnon(data, ss.payload); + return verifyDetachedAnon(data, sig); } -bool PublicKey::verifyDetachedAnon(std::string_view data, std::string_view sig) const +bool PublicKey::verifyDetachedAnon(std::string_view data, const Signature & sig) const { - std::string sig2; - try { - sig2 = base64::decode(sig); - } catch (Error & e) { - e.addTrace({}, "while decoding signature '%s'", sig); - } - if (sig2.size() != crypto_sign_BYTES) + if (sig.sig.size() != crypto_sign_BYTES) throw Error("signature is not valid"); return crypto_sign_verify_detached( - (unsigned char *) sig2.data(), (unsigned char *) data.data(), data.size(), (unsigned char *) key.data()) + (unsigned char *) sig.sig.data(), + (unsigned char *) data.data(), + data.size(), + (unsigned char *) key.data()) == 0; } -bool verifyDetached(std::string_view data, std::string_view sig, const PublicKeys & publicKeys) +bool verifyDetached(std::string_view data, const Signature & sig, const PublicKeys & publicKeys) { - auto ss = BorrowedCryptoValue::parse(sig); - - auto key = publicKeys.find(std::string(ss.name)); + auto key = publicKeys.find(sig.keyName); if (key == publicKeys.end()) return false; - return key->second.verifyDetachedAnon(data, ss.payload); + return key->second.verifyDetachedAnon(data, sig); } } // namespace nix + +namespace nlohmann { +void adl_serializer::to_json(json & j, const Signature & s) +{ + j = s.to_string(); +} + +Signature adl_serializer::from_json(const json & j) +{ + return Signature::parse(getString(j)); +} + +} // namespace nlohmann diff --git a/src/libutil/signature/signer.cc b/src/libutil/signature/signer.cc index 9f6f663e92c..fff03fc30db 100644 --- a/src/libutil/signature/signer.cc +++ b/src/libutil/signature/signer.cc @@ -11,7 +11,7 @@ LocalSigner::LocalSigner(SecretKey && privateKey) { } -std::string LocalSigner::signDetached(std::string_view s) const +Signature LocalSigner::signDetached(std::string_view s) const { return privateKey.signDetached(s); } diff --git a/src/nix/nix-store/nix-store.cc b/src/nix/nix-store/nix-store.cc index a2c0aaf3ff8..4e57723634a 100644 --- a/src/nix/nix-store/nix-store.cc +++ b/src/nix/nix-store/nix-store.cc @@ -1066,7 +1066,7 @@ static void opServe(Strings opFlags, Strings opArgs) info.deriver = store->parseStorePath(deriver); info.references = ServeProto::Serialise::read(*store, rconn); in >> info.registrationTime >> info.narSize >> info.ultimate; - info.sigs = readStrings(in); + info.sigs = ServeProto::Serialise>::read(*store, rconn); info.ca = ContentAddress::parseOpt(readString(in)); if (info.narSize == 0) diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 6bffe2424e4..3e36197b958 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -227,7 +227,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca)); for (auto & sig : info->sigs) - ss.push_back(sig); + ss.push_back(sig.to_string()); str << concatStringsSep(" ", ss); } diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index e82f0d284b9..2d864a5fcc2 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -61,7 +61,7 @@ struct CmdCopySigs : StorePathsCommand auto info = store->queryPathInfo(storePath); - StringSet newSigs; + std::set newSigs; for (auto & store2 : substituters) { try { diff --git a/src/nix/verify.cc b/src/nix/verify.cc index 309d19a1d4e..51e4f697b4d 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -123,11 +123,11 @@ struct CmdVerify : StorePathsCommand else { - StringSet sigsSeen; + std::set sigsSeen; size_t actualSigsNeeded = std::max(sigsNeeded, (size_t) 1); size_t validSigs = 0; - auto doSigs = [&](StringSet sigs) { + auto doSigs = [&](std::set sigs) { for (const auto & sig : sigs) { if (!sigsSeen.insert(sig).second) continue; diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index 93e9f0f9541..0c158218506 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -156,7 +156,7 @@ StoreWrapper::queryPathInfo(char * path, int base32) XPUSHs(sv_2mortal(newRV((SV *) refs))); AV * sigs = newAV(); for (auto & i : info->sigs) - av_push(sigs, newSVpv(i.c_str(), 0)); + av_push(sigs, newSVpv(i.to_string().c_str(), 0)); XPUSHs(sv_2mortal(newRV((SV *) sigs))); } catch (Error & e) { croak("%s", e.what()); @@ -301,7 +301,7 @@ SV * convertHash(char * algo, char * s, int toBase32) SV * signString(char * secretKey_, char * msg) PPCODE: try { - auto sig = SecretKey(secretKey_).signDetached(msg); + auto sig = SecretKey(secretKey_).signDetached(msg).to_string(); XPUSHs(sv_2mortal(newSVpv(sig.c_str(), sig.size()))); } catch (Error & e) { croak("%s", e.what());