forked from lix-project/lix
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:
parent
315aade89d
commit
12bb8cdd38
|
@ -12,7 +12,6 @@
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "crypto.hh"
|
|
||||||
#include "posix-source-accessor.hh"
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
|
@ -28,7 +28,8 @@ BinaryCacheStore::BinaryCacheStore(const Params & params)
|
||||||
, Store(params)
|
, Store(params)
|
||||||
{
|
{
|
||||||
if (secretKeyFile != "")
|
if (secretKeyFile != "")
|
||||||
secretKey = std::unique_ptr<SecretKey>(new SecretKey(readFile(secretKeyFile)));
|
signer = std::make_unique<LocalSigner>(
|
||||||
|
SecretKey { readFile(secretKeyFile) });
|
||||||
|
|
||||||
StringSink sink;
|
StringSink sink;
|
||||||
sink << narVersionMagic1;
|
sink << narVersionMagic1;
|
||||||
|
@ -274,7 +275,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
|
||||||
stats.narWriteCompressionTimeMs += duration;
|
stats.narWriteCompressionTimeMs += duration;
|
||||||
|
|
||||||
/* Atomically write the NAR info file.*/
|
/* Atomically write the NAR info file.*/
|
||||||
if (secretKey) narInfo->sign(*this, *secretKey);
|
if (signer) narInfo->sign(*this, *signer);
|
||||||
|
|
||||||
writeNarInfo(narInfo);
|
writeNarInfo(narInfo);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include "crypto.hh"
|
#include "signature/local-keys.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "log-store.hh"
|
#include "log-store.hh"
|
||||||
|
|
||||||
|
@ -57,8 +57,7 @@ class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
|
||||||
{
|
{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<Signer> signer;
|
||||||
std::unique_ptr<SecretKey> secretKey;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include <sodium/core.h>
|
|
||||||
|
|
||||||
#ifdef __GLIBC__
|
#ifdef __GLIBC__
|
||||||
# include <gnu/lib-names.h>
|
# include <gnu/lib-names.h>
|
||||||
# include <nss.h>
|
# include <nss.h>
|
||||||
|
@ -409,9 +407,6 @@ void initLibStore() {
|
||||||
|
|
||||||
initLibUtil();
|
initLibUtil();
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
|
||||||
throw Error("could not initialise libsodium");
|
|
||||||
|
|
||||||
loadConfFile();
|
loadConfFile();
|
||||||
|
|
||||||
preloadNSS();
|
preloadNSS();
|
||||||
|
|
31
src/libstore/keys.cc
Normal file
31
src/libstore/keys.cc
Normal 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
10
src/libstore/keys.hh
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "signature/local-keys.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
PublicKeys getDefaultPublicKeys();
|
||||||
|
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "posix-fs-canonicalise.hh"
|
#include "posix-fs-canonicalise.hh"
|
||||||
#include "posix-source-accessor.hh"
|
#include "posix-source-accessor.hh"
|
||||||
|
#include "keys.hh"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -1578,7 +1579,8 @@ void LocalStore::signRealisation(Realisation & realisation)
|
||||||
|
|
||||||
for (auto & secretKeyFile : secretKeyFiles.get()) {
|
for (auto & secretKeyFile : secretKeyFiles.get()) {
|
||||||
SecretKey secretKey(readFile(secretKeyFile));
|
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()) {
|
for (auto & secretKeyFile : secretKeyFiles.get()) {
|
||||||
SecretKey secretKey(readFile(secretKeyFile));
|
SecretKey secretKey(readFile(secretKeyFile));
|
||||||
info.sign(*this, secretKey);
|
LocalSigner signer(std::move(secretKey));
|
||||||
|
info.sign(*this, signer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
||||||
|
|
||||||
libstore_LIBS = libutil
|
libstore_LIBS = libutil
|
||||||
|
|
||||||
libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) -pthread
|
||||||
ifdef HOST_LINUX
|
ifdef HOST_LINUX
|
||||||
libstore_LDFLAGS += -ldl
|
libstore_LDFLAGS += -ldl
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -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
|
std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithReferences() const
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include "crypto.hh"
|
#include "signature/signer.hh"
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
#include "content-address.hh"
|
#include "content-address.hh"
|
||||||
|
@ -107,7 +107,7 @@ struct ValidPathInfo : UnkeyedValidPathInfo {
|
||||||
*/
|
*/
|
||||||
std::string fingerprint(const Store & store) const;
|
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
|
* @return The `ContentAddressWithReferences` that determines the
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "store-dir-config.hh"
|
#include "store-dir-config.hh"
|
||||||
|
|
||||||
#include <sodium.h>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void checkName(std::string_view path, std::string_view name)
|
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)
|
StorePath StorePath::random(std::string_view name)
|
||||||
{
|
{
|
||||||
Hash hash(HashAlgorithm::SHA1);
|
return StorePath(Hash::random(HashAlgorithm::SHA1), name);
|
||||||
randombytes_buf(hash.hash, hash.hashSize);
|
|
||||||
return StorePath(hash, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
|
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "closure.hh"
|
#include "closure.hh"
|
||||||
|
#include "signature/local-keys.hh"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -113,9 +114,9 @@ std::string Realisation::fingerprint() const
|
||||||
return serialized.dump();
|
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
|
bool Realisation::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "derived-path.hh"
|
#include "derived-path.hh"
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
#include "comparator.hh"
|
#include "comparator.hh"
|
||||||
#include "crypto.hh"
|
#include "signature/signer.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ struct Realisation {
|
||||||
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);
|
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);
|
||||||
|
|
||||||
std::string fingerprint() const;
|
std::string fingerprint() const;
|
||||||
void sign(const SecretKey &);
|
void sign(const Signer &);
|
||||||
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
||||||
size_t checkSignatures(const PublicKeys & publicKeys) const;
|
size_t checkSignatures(const PublicKeys & publicKeys) const;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "crypto.hh"
|
#include "signature/local-keys.hh"
|
||||||
#include "source-accessor.hh"
|
#include "source-accessor.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "derived-path.hh"
|
#include "derived-path.hh"
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static size_t regularHashSize(HashAlgorithm type) {
|
static size_t regularHashSize(HashAlgorithm type) {
|
||||||
|
@ -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));
|
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)
|
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashAlgorithm> ha)
|
||||||
{
|
{
|
||||||
if (hashStr.empty()) {
|
if (hashStr.empty()) {
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,6 +142,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
static Hash dummy;
|
static Hash dummy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a random hash with hash algorithm `algo`
|
||||||
|
*/
|
||||||
|
static Hash random(HashAlgorithm algo);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,14 +4,17 @@ libutil_NAME = libnixutil
|
||||||
|
|
||||||
libutil_DIR := $(d)
|
libutil_DIR := $(d)
|
||||||
|
|
||||||
libutil_SOURCES := $(wildcard $(d)/*.cc)
|
libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc)
|
||||||
|
|
||||||
libutil_CXXFLAGS += -I src/libutil
|
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), \
|
$(foreach i, $(wildcard $(d)/args/*.hh), \
|
||||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/args, 0644)))
|
$(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)
|
ifeq ($(HAVE_LIBCPUID), 1)
|
||||||
libutil_LDFLAGS += -lcpuid
|
libutil_LDFLAGS += -lcpuid
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
#include "crypto.hh"
|
#include "signature/local-keys.hh"
|
||||||
|
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "globals.hh"
|
|
||||||
|
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
namespace nix {
|
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(':');
|
size_t colon = s.find(':');
|
||||||
if (colon == std::string::npos || colon == 0)
|
if (colon == std::string::npos || colon == 0)
|
||||||
|
@ -17,10 +16,10 @@ static std::pair<std::string_view, std::string_view> split(std::string_view s)
|
||||||
|
|
||||||
Key::Key(std::string_view s)
|
Key::Key(std::string_view s)
|
||||||
{
|
{
|
||||||
auto ss = split(s);
|
auto ss = BorrowedCryptoValue::parse(s);
|
||||||
|
|
||||||
name = ss.first;
|
name = ss.name;
|
||||||
key = ss.second;
|
key = ss.payload;
|
||||||
|
|
||||||
if (name == "" || key == "")
|
if (name == "" || key == "")
|
||||||
throw Error("secret key is corrupt");
|
throw Error("secret key is corrupt");
|
||||||
|
@ -73,45 +72,34 @@ PublicKey::PublicKey(std::string_view s)
|
||||||
throw Error("public key is not valid");
|
throw Error("public key is not valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verifyDetached(const std::string & data, const std::string & sig,
|
bool PublicKey::verifyDetached(std::string_view data, std::string_view sig) const
|
||||||
const PublicKeys & publicKeys)
|
|
||||||
{
|
{
|
||||||
auto ss = split(sig);
|
auto ss = BorrowedCryptoValue::parse(sig);
|
||||||
|
|
||||||
auto key = publicKeys.find(std::string(ss.first));
|
if (ss.name != std::string_view { name }) return false;
|
||||||
if (key == publicKeys.end()) return false;
|
|
||||||
|
|
||||||
auto sig2 = base64Decode(ss.second);
|
return verifyDetachedAnon(data, ss.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PublicKey::verifyDetachedAnon(std::string_view data, std::string_view sig) const
|
||||||
|
{
|
||||||
|
auto sig2 = base64Decode(sig);
|
||||||
if (sig2.size() != crypto_sign_BYTES)
|
if (sig2.size() != crypto_sign_BYTES)
|
||||||
throw Error("signature is not valid");
|
throw Error("signature is not valid");
|
||||||
|
|
||||||
return crypto_sign_verify_detached((unsigned char *) sig2.data(),
|
return crypto_sign_verify_detached((unsigned char *) sig2.data(),
|
||||||
(unsigned char *) data.data(), data.size(),
|
(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;
|
auto ss = BorrowedCryptoValue::parse(sig);
|
||||||
|
|
||||||
// FIXME: filter duplicates
|
auto key = publicKeys.find(std::string(ss.name));
|
||||||
|
if (key == publicKeys.end()) return false;
|
||||||
|
|
||||||
for (auto s : settings.trustedPublicKeys.get()) {
|
return key->second.verifyDetachedAnon(data, ss.payload);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,25 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Except where otherwise noted, Nix serializes keys and signatures in
|
||||||
|
* the form:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* <name>:<key/signature-in-Base64>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
struct BorrowedCryptoValue {
|
||||||
|
std::string_view name;
|
||||||
|
std::string_view payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This splits on the colon, the user can then separated decode the
|
||||||
|
* Base64 payload separately.
|
||||||
|
*/
|
||||||
|
static BorrowedCryptoValue parse(std::string_view);
|
||||||
|
};
|
||||||
|
|
||||||
struct Key
|
struct Key
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -49,21 +68,36 @@ struct PublicKey : Key
|
||||||
{
|
{
|
||||||
PublicKey(std::string_view data);
|
PublicKey(std::string_view data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
bool verifyDetachedAnon(std::string_view data, std::string_view sigs) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PublicKey(std::string_view name, std::string && key)
|
PublicKey(std::string_view name, std::string && key)
|
||||||
: Key(name, std::move(key)) { }
|
: Key(name, std::move(key)) { }
|
||||||
friend struct SecretKey;
|
friend struct SecretKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map from key names to public keys
|
||||||
|
*/
|
||||||
typedef std::map<std::string, PublicKey> PublicKeys;
|
typedef std::map<std::string, PublicKey> PublicKeys;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true iff ‘sig’ is a correct signature over ‘data’ using one
|
* @return true iff ‘sig’ is a correct signature over ‘data’ using one
|
||||||
* of the given public keys.
|
* of the given public keys.
|
||||||
*/
|
*/
|
||||||
bool verifyDetached(const std::string & data, const std::string & sig,
|
bool verifyDetached(std::string_view data, std::string_view sig, const PublicKeys & publicKeys);
|
||||||
const PublicKeys & publicKeys);
|
|
||||||
|
|
||||||
PublicKeys getDefaultPublicKeys();
|
|
||||||
|
|
||||||
}
|
}
|
23
src/libutil/signature/signer.cc
Normal file
23
src/libutil/signature/signer.cc
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include "signature/signer.hh"
|
||||||
|
#include "error.hh"
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
LocalSigner::LocalSigner(SecretKey && privateKey)
|
||||||
|
: privateKey(privateKey)
|
||||||
|
, publicKey(privateKey.toPublicKey())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::string LocalSigner::signDetached(std::string_view s) const
|
||||||
|
{
|
||||||
|
return privateKey.signDetached(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PublicKey & LocalSigner::getPublicKey()
|
||||||
|
{
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
src/libutil/signature/signer.hh
Normal file
61
src/libutil/signature/signer.hh
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "signature/local-keys.hh"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract signer
|
||||||
|
*
|
||||||
|
* Derive from this class to implement a custom signature scheme.
|
||||||
|
*
|
||||||
|
* It is only necessary to implement signature of bytes and provide a
|
||||||
|
* public key.
|
||||||
|
*/
|
||||||
|
struct Signer
|
||||||
|
{
|
||||||
|
virtual ~Signer() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign the given data, creating a (detached) signature.
|
||||||
|
*
|
||||||
|
* @param data data to be signed.
|
||||||
|
*
|
||||||
|
* @return the [detached
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View the public key associated with this `Signer`.
|
||||||
|
*/
|
||||||
|
virtual const PublicKey & getPublicKey() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Signers = std::map<std::string, Signer*>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local signer
|
||||||
|
*
|
||||||
|
* The private key is held in this machine's RAM
|
||||||
|
*/
|
||||||
|
struct LocalSigner : Signer
|
||||||
|
{
|
||||||
|
LocalSigner(SecretKey && privateKey);
|
||||||
|
|
||||||
|
std::string signDetached(std::string_view s) const override;
|
||||||
|
|
||||||
|
const PublicKey & getPublicKey() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
SecretKey privateKey;
|
||||||
|
PublicKey publicKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -28,6 +29,9 @@ void initLibUtil() {
|
||||||
}
|
}
|
||||||
// This is not actually the main point of this check, but let's make sure anyway:
|
// This is not actually the main point of this check, but let's make sure anyway:
|
||||||
assert(caught);
|
assert(caught);
|
||||||
|
|
||||||
|
if (sodium_init() == -1)
|
||||||
|
throw Error("could not initialise libsodium");
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -112,7 +112,7 @@ struct CmdSign : StorePathsCommand
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "sign store paths";
|
return "sign store paths with a local key";
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(ref<Store> store, StorePaths && storePaths) override
|
void run(ref<Store> store, StorePaths && storePaths) override
|
||||||
|
@ -121,6 +121,7 @@ struct CmdSign : StorePathsCommand
|
||||||
throw UsageError("you must specify a secret key file using '-k'");
|
throw UsageError("you must specify a secret key file using '-k'");
|
||||||
|
|
||||||
SecretKey secretKey(readFile(secretKeyFile));
|
SecretKey secretKey(readFile(secretKeyFile));
|
||||||
|
LocalSigner signer(std::move(secretKey));
|
||||||
|
|
||||||
size_t added{0};
|
size_t added{0};
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ struct CmdSign : StorePathsCommand
|
||||||
|
|
||||||
auto info2(*info);
|
auto info2(*info);
|
||||||
info2.sigs.clear();
|
info2.sigs.clear();
|
||||||
info2.sign(*store, secretKey);
|
info2.sign(*store, signer);
|
||||||
assert(!info2.sigs.empty());
|
assert(!info2.sigs.empty());
|
||||||
|
|
||||||
if (!info->sigs.count(*info2.sigs.begin())) {
|
if (!info->sigs.count(*info2.sigs.begin())) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
|
#include "keys.hh"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue