1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
#include "libstore/crypto.hh"
#include <absl/strings/escaping.h>
#include "libstore/globals.hh"
#include "libutil/util.hh"
#if HAVE_SODIUM
#include <sodium.h>
#endif
namespace nix {
// TODO(riking): convert to string_view to reduce allocations
static std::pair<std::string, std::string> split(const std::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 std::string& s) {
auto ss = split(s);
name = ss.first;
std::string keyb64 = ss.second;
if (name.empty() || keyb64.empty()) {
throw Error("secret key is corrupt");
}
if (!absl::Base64Unescape(keyb64, &key)) {
// TODO(grfn): replace this with StatusOr
throw Error("Invalid Base64");
}
}
SecretKey::SecretKey(const std::string& s) : Key(s) {
#if HAVE_SODIUM
if (key.size() != crypto_sign_SECRETKEYBYTES) {
throw Error("secret key is not valid");
}
#endif
}
#if !HAVE_SODIUM
[[noreturn]] static void noSodium() {
throw Error(
"Nix was not compiled with libsodium, required for signed binary cache "
"support");
}
#endif
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 + ":" +
absl::Base64Escape(std::string(reinterpret_cast<char*>(sig), sigLen));
#else
noSodium();
#endif
}
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(reinterpret_cast<char*>(pk),
crypto_sign_PUBLICKEYBYTES));
#else
noSodium();
#endif
}
PublicKey::PublicKey(const std::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) {
#if HAVE_SODIUM
auto ss = split(sig);
auto key = publicKeys.find(ss.first);
if (key == publicKeys.end()) {
return false;
}
std::string sig2;
if (!absl::Base64Unescape(ss.second, &sig2)) {
// TODO(grfn): replace this with StatusOr
throw Error("Invalid Base64");
}
if (sig2.size() != crypto_sign_BYTES) {
throw Error("signature is not valid");
}
return crypto_sign_verify_detached(
reinterpret_cast<unsigned char*>(sig2.data()),
(unsigned char*)data.data(), data.size(),
(unsigned char*)key->second.key.data()) == 0;
#else
noSodium();
#endif
}
PublicKeys getDefaultPublicKeys() {
PublicKeys publicKeys;
// FIXME: filter duplicates
for (const auto& s : settings.trustedPublicKeys.get()) {
PublicKey key(s);
publicKeys.emplace(key.name, key);
}
for (const 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;
}
} // namespace nix
|