1 #include "crypto.hh"
2 #include "util.hh"
3 #include "globals.hh"
4
5 #if HAVE_SODIUM
6 #include <sodium.h>
7 #endif
8
9 namespace nix {
10
split(const string & s)11 static std::pair<std::string, std::string> split(const string & s)
12 {
13 size_t colon = s.find(':');
14 if (colon == std::string::npos || colon == 0)
15 return {"", ""};
16 return {std::string(s, 0, colon), std::string(s, colon + 1)};
17 }
18
Key(const string & s)19 Key::Key(const string & s)
20 {
21 auto ss = split(s);
22
23 name = ss.first;
24 key = ss.second;
25
26 if (name == "" || key == "")
27 throw Error("secret key is corrupt");
28
29 key = base64Decode(key);
30 }
31
SecretKey(const string & s)32 SecretKey::SecretKey(const string & s)
33 : Key(s)
34 {
35 #if HAVE_SODIUM
36 if (key.size() != crypto_sign_SECRETKEYBYTES)
37 throw Error("secret key is not valid");
38 #endif
39 }
40
41 #if !HAVE_SODIUM
noSodium()42 [[noreturn]] static void noSodium()
43 {
44 throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
45 }
46 #endif
47
signDetached(const std::string & data) const48 std::string SecretKey::signDetached(const std::string & data) const
49 {
50 #if HAVE_SODIUM
51 unsigned char sig[crypto_sign_BYTES];
52 unsigned long long sigLen;
53 crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(),
54 (unsigned char *) key.data());
55 return name + ":" + base64Encode(std::string((char *) sig, sigLen));
56 #else
57 noSodium();
58 #endif
59 }
60
toPublicKey() const61 PublicKey SecretKey::toPublicKey() const
62 {
63 #if HAVE_SODIUM
64 unsigned char pk[crypto_sign_PUBLICKEYBYTES];
65 crypto_sign_ed25519_sk_to_pk(pk, (unsigned char *) key.data());
66 return PublicKey(name, std::string((char *) pk, crypto_sign_PUBLICKEYBYTES));
67 #else
68 noSodium();
69 #endif
70 }
71
PublicKey(const string & s)72 PublicKey::PublicKey(const string & s)
73 : Key(s)
74 {
75 #if HAVE_SODIUM
76 if (key.size() != crypto_sign_PUBLICKEYBYTES)
77 throw Error("public key is not valid");
78 #endif
79 }
80
verifyDetached(const std::string & data,const std::string & sig,const PublicKeys & publicKeys)81 bool verifyDetached(const std::string & data, const std::string & sig,
82 const PublicKeys & publicKeys)
83 {
84 #if HAVE_SODIUM
85 auto ss = split(sig);
86
87 auto key = publicKeys.find(ss.first);
88 if (key == publicKeys.end()) return false;
89
90 auto sig2 = base64Decode(ss.second);
91 if (sig2.size() != crypto_sign_BYTES)
92 throw Error("signature is not valid");
93
94 return crypto_sign_verify_detached((unsigned char *) sig2.data(),
95 (unsigned char *) data.data(), data.size(),
96 (unsigned char *) key->second.key.data()) == 0;
97 #else
98 noSodium();
99 #endif
100 }
101
getDefaultPublicKeys()102 PublicKeys getDefaultPublicKeys()
103 {
104 PublicKeys publicKeys;
105
106 // FIXME: filter duplicates
107
108 for (auto s : settings.trustedPublicKeys.get()) {
109 PublicKey key(s);
110 publicKeys.emplace(key.name, key);
111 }
112
113 for (auto secretKeyFile : settings.secretKeyFiles.get()) {
114 try {
115 SecretKey secretKey(readFile(secretKeyFile));
116 publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
117 } catch (SysError & e) {
118 /* Ignore unreadable key files. That's normal in a
119 multi-user installation. */
120 }
121 }
122
123 return publicKeys;
124 }
125
126 }
127