Skip to content

Commit

Permalink
Signer infrastructure: Prep for #9076
Browse files Browse the repository at this point in the history
This sets up infrastructure in libutil to allow for signing other than
by a secret key in memory. #9076 uses this to implement remote signing.

(Split from that PR to allow reviewing in smaller chunks.)

Co-Authored-By: Raito Bezarius <[email protected]>
  • Loading branch information
Ericson2314 and RaitoBezarius committed Jan 3, 2024
1 parent 315aade commit 12bb8cd
Show file tree
Hide file tree
Showing 24 changed files with 235 additions and 72 deletions.
1 change: 0 additions & 1 deletion perl/lib/Nix/Store.xs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include "realisation.hh"
#include "globals.hh"
#include "store-api.hh"
#include "crypto.hh"
#include "posix-source-accessor.hh"

#include <sodium.h>
Expand Down
5 changes: 3 additions & 2 deletions src/libstore/binary-cache-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ BinaryCacheStore::BinaryCacheStore(const Params & params)
, Store(params)
{
if (secretKeyFile != "")
secretKey = std::unique_ptr<SecretKey>(new SecretKey(readFile(secretKeyFile)));
signer = std::make_unique<LocalSigner>(
SecretKey { readFile(secretKeyFile) });

StringSink sink;
sink << narVersionMagic1;
Expand Down Expand Up @@ -274,7 +275,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
stats.narWriteCompressionTimeMs += duration;

/* Atomically write the NAR info file.*/
if (secretKey) narInfo->sign(*this, *secretKey);
if (signer) narInfo->sign(*this, *signer);

writeNarInfo(narInfo);

Expand Down
5 changes: 2 additions & 3 deletions src/libstore/binary-cache-store.hh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
///@file

#include "crypto.hh"
#include "signature/local-keys.hh"
#include "store-api.hh"
#include "log-store.hh"

Expand Down Expand Up @@ -57,8 +57,7 @@ class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
{

private:

std::unique_ptr<SecretKey> secretKey;
std::unique_ptr<Signer> signer;

protected:

Expand Down
5 changes: 0 additions & 5 deletions src/libstore/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

#include <nlohmann/json.hpp>

#include <sodium/core.h>

#ifdef __GLIBC__
# include <gnu/lib-names.h>
# include <nss.h>
Expand Down Expand Up @@ -409,9 +407,6 @@ void initLibStore() {

initLibUtil();

if (sodium_init() == -1)
throw Error("could not initialise libsodium");

loadConfFile();

preloadNSS();
Expand Down
31 changes: 31 additions & 0 deletions src/libstore/keys.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "file-system.hh"
#include "globals.hh"
#include "keys.hh"

namespace nix {

PublicKeys getDefaultPublicKeys()
{
PublicKeys publicKeys;

// FIXME: filter duplicates

for (auto s : settings.trustedPublicKeys.get()) {
PublicKey key(s);
publicKeys.emplace(key.name, key);
}

for (auto secretKeyFile : settings.secretKeyFiles.get()) {
try {
SecretKey secretKey(readFile(secretKeyFile));
publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
} catch (SysError & e) {
/* Ignore unreadable key files. That's normal in a
multi-user installation. */
}
}

return publicKeys;
}

}
10 changes: 10 additions & 0 deletions src/libstore/keys.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once
///@file

#include "signature/local-keys.hh"

namespace nix {

PublicKeys getDefaultPublicKeys();

}
7 changes: 5 additions & 2 deletions src/libstore/local-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "signals.hh"
#include "posix-fs-canonicalise.hh"
#include "posix-source-accessor.hh"
#include "keys.hh"

#include <iostream>
#include <algorithm>
Expand Down Expand Up @@ -1578,7 +1579,8 @@ void LocalStore::signRealisation(Realisation & realisation)

for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
realisation.sign(secretKey);
LocalSigner signer(std::move(secretKey));
realisation.sign(signer);
}
}

Expand All @@ -1590,7 +1592,8 @@ void LocalStore::signPathInfo(ValidPathInfo & info)

for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
info.sign(*this, secretKey);
LocalSigner signer(std::move(secretKey));
info.sign(*this, signer);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/libstore/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)

libstore_LIBS = libutil

libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) -pthread
ifdef HOST_LINUX
libstore_LDFLAGS += -ldl
endif
Expand Down
4 changes: 2 additions & 2 deletions src/libstore/path-info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ std::string ValidPathInfo::fingerprint(const Store & store) const
}


void ValidPathInfo::sign(const Store & store, const SecretKey & secretKey)
void ValidPathInfo::sign(const Store & store, const Signer & signer)
{
sigs.insert(secretKey.signDetached(fingerprint(store)));
sigs.insert(signer.signDetached(fingerprint(store)));
}

std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithReferences() const
Expand Down
4 changes: 2 additions & 2 deletions src/libstore/path-info.hh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
///@file

#include "crypto.hh"
#include "signature/signer.hh"
#include "path.hh"
#include "hash.hh"
#include "content-address.hh"
Expand Down Expand Up @@ -107,7 +107,7 @@ struct ValidPathInfo : UnkeyedValidPathInfo {
*/
std::string fingerprint(const Store & store) const;

void sign(const Store & store, const SecretKey & secretKey);
void sign(const Store & store, const Signer & signer);

/**
* @return The `ContentAddressWithReferences` that determines the
Expand Down
6 changes: 1 addition & 5 deletions src/libstore/path.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#include "store-dir-config.hh"

#include <sodium.h>

namespace nix {

static void checkName(std::string_view path, std::string_view name)
Expand Down Expand Up @@ -49,9 +47,7 @@ StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x");

StorePath StorePath::random(std::string_view name)
{
Hash hash(HashAlgorithm::SHA1);
randombytes_buf(hash.hash, hash.hashSize);
return StorePath(hash, name);
return StorePath(Hash::random(HashAlgorithm::SHA1), name);
}

StorePath StoreDirConfig::parseStorePath(std::string_view path) const
Expand Down
5 changes: 3 additions & 2 deletions src/libstore/realisation.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "realisation.hh"
#include "store-api.hh"
#include "closure.hh"
#include "signature/local-keys.hh"
#include <nlohmann/json.hpp>

namespace nix {
Expand Down Expand Up @@ -113,9 +114,9 @@ std::string Realisation::fingerprint() const
return serialized.dump();
}

void Realisation::sign(const SecretKey & secretKey)
void Realisation::sign(const Signer &signer)
{
signatures.insert(secretKey.signDetached(fingerprint()));
signatures.insert(signer.signDetached(fingerprint()));
}

bool Realisation::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
Expand Down
4 changes: 2 additions & 2 deletions src/libstore/realisation.hh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "derived-path.hh"
#include <nlohmann/json_fwd.hpp>
#include "comparator.hh"
#include "crypto.hh"
#include "signature/signer.hh"

namespace nix {

Expand Down Expand Up @@ -64,7 +64,7 @@ struct Realisation {
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);

std::string fingerprint() const;
void sign(const SecretKey &);
void sign(const Signer &);
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
size_t checkSignatures(const PublicKeys & publicKeys) const;

Expand Down
2 changes: 1 addition & 1 deletion src/libstore/store-api.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "crypto.hh"
#include "signature/local-keys.hh"
#include "source-accessor.hh"
#include "globals.hh"
#include "derived-path.hh"
Expand Down
9 changes: 9 additions & 0 deletions src/libutil/hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <sys/stat.h>
#include <fcntl.h>

#include <sodium.h>

namespace nix {

static size_t regularHashSize(HashAlgorithm type) {
Expand Down Expand Up @@ -261,6 +263,13 @@ Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
throw BadHash("hash '%s' has wrong length for hash algorithm '%s'", rest, printHashAlgo(this->algo));
}

Hash Hash::random(HashAlgorithm algo)
{
Hash hash(algo);
randombytes_buf(hash.hash, hash.hashSize);
return hash;
}

Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashAlgorithm> ha)
{
if (hashStr.empty()) {
Expand Down
6 changes: 5 additions & 1 deletion src/libutil/hash.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "serialise.hh"
#include "file-system.hh"


namespace nix {


Expand Down Expand Up @@ -143,6 +142,11 @@ public:
}

static Hash dummy;

/**
* @return a random hash with hash algorithm `algo`
*/
static Hash random(HashAlgorithm algo);
};

/**
Expand Down
7 changes: 5 additions & 2 deletions src/libutil/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ libutil_NAME = libnixutil

libutil_DIR := $(d)

libutil_SOURCES := $(wildcard $(d)/*.cc)
libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc)

libutil_CXXFLAGS += -I src/libutil

libutil_LDFLAGS += -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
libutil_LDFLAGS += -pthread $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context

$(foreach i, $(wildcard $(d)/args/*.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix/args, 0644)))
$(foreach i, $(wildcard $(d)/signature/*.hh), \
$(eval $(call install-file-in, $(i), $(includedir)/nix/signature, 0644)))


ifeq ($(HAVE_LIBCPUID), 1)
libutil_LDFLAGS += -lcpuid
Expand Down
58 changes: 23 additions & 35 deletions src/libstore/crypto.cc → src/libutil/signature/local-keys.cc
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#include "crypto.hh"
#include "signature/local-keys.hh"

#include "file-system.hh"
#include "util.hh"
#include "globals.hh"

#include <sodium.h>

namespace nix {

static std::pair<std::string_view, std::string_view> split(std::string_view s)
BorrowedCryptoValue BorrowedCryptoValue::parse(std::string_view s)
{
size_t colon = s.find(':');
if (colon == std::string::npos || colon == 0)
Expand All @@ -17,10 +16,10 @@ static std::pair<std::string_view, std::string_view> split(std::string_view s)

Key::Key(std::string_view s)
{
auto ss = split(s);
auto ss = BorrowedCryptoValue::parse(s);

name = ss.first;
key = ss.second;
name = ss.name;
key = ss.payload;

if (name == "" || key == "")
throw Error("secret key is corrupt");
Expand Down Expand Up @@ -73,45 +72,34 @@ PublicKey::PublicKey(std::string_view s)
throw Error("public key is not valid");
}

bool verifyDetached(const std::string & data, const std::string & sig,
const PublicKeys & publicKeys)
bool PublicKey::verifyDetached(std::string_view data, std::string_view sig) const
{
auto ss = split(sig);
auto ss = BorrowedCryptoValue::parse(sig);

auto key = publicKeys.find(std::string(ss.first));
if (key == publicKeys.end()) return false;
if (ss.name != std::string_view { name }) return false;

return verifyDetachedAnon(data, ss.payload);
}

auto sig2 = base64Decode(ss.second);
bool PublicKey::verifyDetachedAnon(std::string_view data, std::string_view sig) const
{
auto sig2 = base64Decode(sig);
if (sig2.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->second.key.data()) == 0;
(unsigned char *) key.data()) == 0;
}

PublicKeys getDefaultPublicKeys()
bool verifyDetached(std::string_view data, std::string_view sig, const PublicKeys & publicKeys)
{
PublicKeys publicKeys;

// FIXME: filter duplicates

for (auto s : settings.trustedPublicKeys.get()) {
PublicKey key(s);
publicKeys.emplace(key.name, key);
}

for (auto secretKeyFile : settings.secretKeyFiles.get()) {
try {
SecretKey secretKey(readFile(secretKeyFile));
publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
} catch (SysError & e) {
/* Ignore unreadable key files. That's normal in a
multi-user installation. */
}
}

return publicKeys;
auto ss = BorrowedCryptoValue::parse(sig);

auto key = publicKeys.find(std::string(ss.name));
if (key == publicKeys.end()) return false;

return key->second.verifyDetachedAnon(data, ss.payload);
}

}
Loading

0 comments on commit 12bb8cd

Please sign in to comment.