1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-02 17:41:48 +02:00

Signer infrastructure: Prep for #9076

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 <masterancpp@gmail.com>
This commit is contained in:
John Ericson 2024-01-03 15:02:20 -05:00
parent 315aade89d
commit 12bb8cdd38
24 changed files with 233 additions and 70 deletions

View file

@ -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;
@ -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);

View file

@ -1,7 +1,7 @@
#pragma once
///@file
#include "crypto.hh"
#include "signature/local-keys.hh"
#include "store-api.hh"
#include "log-store.hh"
@ -57,8 +57,7 @@ class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
{
private:
std::unique_ptr<SecretKey> secretKey;
std::unique_ptr<Signer> signer;
protected:

View file

@ -1,117 +0,0 @@
#include "crypto.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)
{
size_t colon = s.find(':');
if (colon == std::string::npos || colon == 0)
return {"", ""};
return {s.substr(0, colon), s.substr(colon + 1)};
}
Key::Key(std::string_view s)
{
auto ss = split(s);
name = ss.first;
key = ss.second;
if (name == "" || key == "")
throw Error("secret key is corrupt");
key = base64Decode(key);
}
std::string Key::to_string() const
{
return name + ":" + base64Encode(key);
}
SecretKey::SecretKey(std::string_view s)
: Key(s)
{
if (key.size() != crypto_sign_SECRETKEYBYTES)
throw Error("secret key is not valid");
}
std::string 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 + ":" + base64Encode(std::string((char *) sig, sigLen));
}
PublicKey SecretKey::toPublicKey() const
{
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
crypto_sign_ed25519_sk_to_pk(pk, (unsigned char *) key.data());
return PublicKey(name, std::string((char *) pk, crypto_sign_PUBLICKEYBYTES));
}
SecretKey SecretKey::generate(std::string_view name)
{
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
unsigned char sk[crypto_sign_SECRETKEYBYTES];
if (crypto_sign_keypair(pk, sk) != 0)
throw Error("key generation failed");
return SecretKey(name, std::string((char *) sk, crypto_sign_SECRETKEYBYTES));
}
PublicKey::PublicKey(std::string_view s)
: Key(s)
{
if (key.size() != crypto_sign_PUBLICKEYBYTES)
throw Error("public key is not valid");
}
bool verifyDetached(const std::string & data, const std::string & sig,
const PublicKeys & publicKeys)
{
auto ss = split(sig);
auto key = publicKeys.find(std::string(ss.first));
if (key == publicKeys.end()) return false;
auto sig2 = base64Decode(ss.second);
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;
}
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;
}
}

View file

@ -1,69 +0,0 @@
#pragma once
///@file
#include "types.hh"
#include <map>
namespace nix {
struct Key
{
std::string name;
std::string key;
/**
* Construct Key from a string in the format
* <name>:<key-in-base64>.
*/
Key(std::string_view s);
std::string to_string() const;
protected:
Key(std::string_view name, std::string && key)
: name(name), key(std::move(key)) { }
};
struct PublicKey;
struct SecretKey : Key
{
SecretKey(std::string_view s);
/**
* Return a detached signature of the given string.
*/
std::string signDetached(std::string_view s) const;
PublicKey toPublicKey() const;
static SecretKey generate(std::string_view name);
private:
SecretKey(std::string_view name, std::string && key)
: Key(name, std::move(key)) { }
};
struct PublicKey : Key
{
PublicKey(std::string_view data);
private:
PublicKey(std::string_view name, std::string && key)
: Key(name, std::move(key)) { }
friend struct SecretKey;
};
typedef std::map<std::string, PublicKey> PublicKeys;
/**
* @return true iff sig is a correct signature over data using one
* of the given public keys.
*/
bool verifyDetached(const std::string & data, const std::string & sig,
const PublicKeys & publicKeys);
PublicKeys getDefaultPublicKeys();
}

View file

@ -15,8 +15,6 @@
#include <nlohmann/json.hpp>
#include <sodium/core.h>
#ifdef __GLIBC__
# include <gnu/lib-names.h>
# include <nss.h>
@ -409,9 +407,6 @@ void initLibStore() {
initLibUtil();
if (sodium_init() == -1)
throw Error("could not initialise libsodium");
loadConfFile();
preloadNSS();

31
src/libstore/keys.cc Normal file
View file

@ -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
src/libstore/keys.hh Normal file
View file

@ -0,0 +1,10 @@
#pragma once
///@file
#include "signature/local-keys.hh"
namespace nix {
PublicKeys getDefaultPublicKeys();
}

View file

@ -14,6 +14,7 @@
#include "signals.hh"
#include "posix-fs-canonicalise.hh"
#include "posix-source-accessor.hh"
#include "keys.hh"
#include <iostream>
#include <algorithm>
@ -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);
}
}
@ -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);
}
}

View file

@ -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

View file

@ -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

View file

@ -1,7 +1,7 @@
#pragma once
///@file
#include "crypto.hh"
#include "signature/signer.hh"
#include "path.hh"
#include "hash.hh"
#include "content-address.hh"
@ -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

View file

@ -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)
@ -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

View file

@ -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 {
@ -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

View file

@ -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 {
@ -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;

View file

@ -1,4 +1,4 @@
#include "crypto.hh"
#include "signature/local-keys.hh"
#include "source-accessor.hh"
#include "globals.hh"
#include "derived-path.hh"