2016-02-16 17:38:44 +02:00
|
|
|
#include "crypto.hh"
|
|
|
|
#include "util.hh"
|
2016-03-29 15:29:50 +03:00
|
|
|
#include "globals.hh"
|
2016-02-16 17:38:44 +02:00
|
|
|
|
|
|
|
#if HAVE_SODIUM
|
|
|
|
#include <sodium.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
|
|
|
static std::pair<std::string, std::string> split(const string & s)
|
|
|
|
{
|
|
|
|
size_t colon = s.find(':');
|
|
|
|
if (colon == std::string::npos || colon == 0)
|
|
|
|
return {"", ""};
|
|
|
|
return {std::string(s, 0, colon), std::string(s, colon + 1)};
|
|
|
|
}
|
|
|
|
|
|
|
|
Key::Key(const string & s)
|
|
|
|
{
|
|
|
|
auto ss = split(s);
|
|
|
|
|
|
|
|
name = ss.first;
|
|
|
|
key = ss.second;
|
|
|
|
|
|
|
|
if (name == "" || key == "")
|
|
|
|
throw Error("secret key is corrupt");
|
|
|
|
|
|
|
|
key = base64Decode(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
SecretKey::SecretKey(const string & s)
|
|
|
|
: Key(s)
|
|
|
|
{
|
|
|
|
#if HAVE_SODIUM
|
|
|
|
if (key.size() != crypto_sign_SECRETKEYBYTES)
|
|
|
|
throw Error("secret key is not valid");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-03-15 13:11:27 +02:00
|
|
|
#if !HAVE_SODIUM
|
2016-02-17 13:41:41 +02:00
|
|
|
[[noreturn]] static void noSodium()
|
|
|
|
{
|
|
|
|
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
|
|
|
|
}
|
2016-03-15 13:11:27 +02:00
|
|
|
#endif
|
2016-02-17 13:41:41 +02:00
|
|
|
|
2016-02-16 17:38:44 +02:00
|
|
|
std::string SecretKey::signDetached(const std::string & data) const
|
|
|
|
{
|
|
|
|
#if HAVE_SODIUM
|
|
|
|
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));
|
|
|
|
#else
|
2016-02-17 13:41:41 +02:00
|
|
|
noSodium();
|
2016-02-16 17:38:44 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-03-04 18:08:30 +02:00
|
|
|
PublicKey SecretKey::toPublicKey() const
|
|
|
|
{
|
|
|
|
#if HAVE_SODIUM
|
|
|
|
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));
|
|
|
|
#else
|
|
|
|
noSodium();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-02-16 17:38:44 +02:00
|
|
|
PublicKey::PublicKey(const string & s)
|
|
|
|
: Key(s)
|
|
|
|
{
|
|
|
|
#if HAVE_SODIUM
|
|
|
|
if (key.size() != crypto_sign_PUBLICKEYBYTES)
|
|
|
|
throw Error("public key is not valid");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool verifyDetached(const std::string & data, const std::string & sig,
|
|
|
|
const PublicKeys & publicKeys)
|
|
|
|
{
|
2016-02-17 13:41:41 +02:00
|
|
|
#if HAVE_SODIUM
|
2016-02-16 17:38:44 +02:00
|
|
|
auto ss = split(sig);
|
|
|
|
|
|
|
|
auto key = publicKeys.find(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;
|
2016-02-17 13:41:41 +02:00
|
|
|
#else
|
|
|
|
noSodium();
|
|
|
|
#endif
|
2016-02-16 17:38:44 +02:00
|
|
|
}
|
|
|
|
|
2016-03-29 15:29:50 +03:00
|
|
|
PublicKeys getDefaultPublicKeys()
|
|
|
|
{
|
|
|
|
PublicKeys publicKeys;
|
2016-04-07 16:07:00 +03:00
|
|
|
|
|
|
|
// FIXME: filter duplicates
|
|
|
|
|
Explicitly model all settings and fail on unrecognized ones
Previously, the Settings class allowed other code to query for string
properties, which led to a proliferation of code all over the place making
up new options without any sort of central registry of valid options. This
commit pulls all those options back into the central Settings class and
removes the public get() methods, to discourage future abuses like that.
Furthermore, because we know the full set of options ahead of time, we
now fail loudly if someone enters an unrecognized option, thus preventing
subtle typos. With some template fun, we could probably also dump the full
set of options (with documentation, defaults, etc.) to the command line,
but I'm not doing that yet here.
2017-02-22 05:50:18 +02:00
|
|
|
for (auto s : settings.binaryCachePublicKeys) {
|
2016-03-29 15:29:50 +03:00
|
|
|
PublicKey key(s);
|
|
|
|
publicKeys.emplace(key.name, key);
|
|
|
|
}
|
2016-04-07 16:07:00 +03:00
|
|
|
|
Explicitly model all settings and fail on unrecognized ones
Previously, the Settings class allowed other code to query for string
properties, which led to a proliferation of code all over the place making
up new options without any sort of central registry of valid options. This
commit pulls all those options back into the central Settings class and
removes the public get() methods, to discourage future abuses like that.
Furthermore, because we know the full set of options ahead of time, we
now fail loudly if someone enters an unrecognized option, thus preventing
subtle typos. With some template fun, we could probably also dump the full
set of options (with documentation, defaults, etc.) to the command line,
but I'm not doing that yet here.
2017-02-22 05:50:18 +02:00
|
|
|
for (auto secretKeyFile : settings.secretKeyFiles) {
|
2016-04-07 16:07:00 +03:00
|
|
|
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. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-29 15:29:50 +03:00
|
|
|
return publicKeys;
|
|
|
|
}
|
|
|
|
|
2016-02-16 17:38:44 +02:00
|
|
|
}
|