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