1 /*
2  *  Copyright (C) 2014-2019 Savoir-faire Linux Inc.
3  *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 #include "crypto.h"
20 #include "rng.h"
21 
22 extern "C" {
23 #include <gnutls/gnutls.h>
24 #include <gnutls/abstract.h>
25 #include <gnutls/x509.h>
26 #include <nettle/gcm.h>
27 #include <nettle/aes.h>
28 
29 #include <argon2.h>
30 }
31 
32 #include <random>
33 #include <sstream>
34 #include <fstream>
35 #include <stdexcept>
36 #include <cassert>
37 
38 #ifdef _WIN32
39 static std::uniform_int_distribution<int> rand_byte{ 0, std::numeric_limits<uint8_t>::max() };
40 #else
41 static std::uniform_int_distribution<uint8_t> rand_byte;
42 #endif
43 
44 // support for GnuTLS < 3.4.
45 #if GNUTLS_VERSION_NUMBER < 0x030400
46 #define GNUTLS_PKCS_PKCS12_3DES GNUTLS_PKCS_USE_PKCS12_3DES
47 #define GNUTLS_PKCS_PKCS12_ARCFOUR GNUTLS_PKCS_USE_PKCS12_ARCFOUR
48 #define GNUTLS_PKCS_PKCS12_RC2_40 GNUTLS_PKCS_USE_PKCS12_RC2_40
49 #define GNUTLS_PKCS_PBES2_3DES GNUTLS_PKCS_USE_PBES2_3DES
50 #define GNUTLS_PKCS_PBES2_AES_128 GNUTLS_PKCS_USE_PBES2_AES_128
51 #define GNUTLS_PKCS_PBES2_AES_192 GNUTLS_PKCS_USE_PBES2_AES_192
52 #define GNUTLS_PKCS_PBES2_AES_256 GNUTLS_PKCS_USE_PBES2_AES_256
53 #endif
54 
55 namespace dht {
56 namespace crypto {
57 
58 static constexpr std::array<size_t, 3> AES_LENGTHS {{128/8, 192/8, 256/8}};
59 static constexpr size_t PASSWORD_SALT_LENGTH {16};
60 
gnutlsHashAlgo(size_t min_res)61 constexpr gnutls_digest_algorithm_t gnutlsHashAlgo(size_t min_res) {
62     return (min_res > 256/8) ? GNUTLS_DIG_SHA512 : (
63            (min_res > 160/8) ? GNUTLS_DIG_SHA256 : (
64                                GNUTLS_DIG_SHA1));
65 }
66 
gnutlsHashSize(int algo)67 constexpr size_t gnutlsHashSize(int algo) {
68     return (algo == GNUTLS_DIG_SHA512) ? 512/8 : (
69            (algo == GNUTLS_DIG_SHA256) ? 256/8 : (
70            (algo == GNUTLS_DIG_SHA1)   ? 160/8 : 0 ));
71 }
72 
aesKeySize(size_t max)73 size_t aesKeySize(size_t max)
74 {
75     size_t aes_key_len = 0;
76     for (size_t s : AES_LENGTHS) {
77         if (s <= max) aes_key_len = s;
78         else break;
79     }
80     return aes_key_len;
81 }
82 
aesKeySizeGood(size_t key_size)83 bool aesKeySizeGood(size_t key_size)
84 {
85     for (auto& i : AES_LENGTHS)
86         if (key_size == i)
87             return true;
88     return false;
89 }
90 
91 #ifndef GCM_DIGEST_SIZE
92 #define GCM_DIGEST_SIZE GCM_BLOCK_SIZE
93 #endif
94 
aesEncrypt(const Blob & data,const Blob & key)95 Blob aesEncrypt(const Blob& data, const Blob& key)
96 {
97     if (not aesKeySizeGood(key.size()))
98         throw DecryptError("Wrong key size");
99 
100     Blob ret(data.size() + GCM_IV_SIZE + GCM_DIGEST_SIZE);
101     {
102         crypto::random_device rdev;
103         std::generate_n(ret.begin(), GCM_IV_SIZE, std::bind(rand_byte, std::ref(rdev)));
104     }
105     struct gcm_aes_ctx aes;
106     gcm_aes_set_key(&aes, key.size(), key.data());
107     gcm_aes_set_iv(&aes, GCM_IV_SIZE, ret.data());
108     gcm_aes_update(&aes, data.size(), data.data());
109 
110     gcm_aes_encrypt(&aes, data.size(), ret.data() + GCM_IV_SIZE, data.data());
111     gcm_aes_digest(&aes, GCM_DIGEST_SIZE, ret.data() + GCM_IV_SIZE + data.size());
112     return ret;
113 }
114 
aesEncrypt(const Blob & data,const std::string & password)115 Blob aesEncrypt(const Blob& data, const std::string& password)
116 {
117     Blob salt;
118     Blob key = stretchKey(password, salt, 256 / 8);
119     Blob encrypted = aesEncrypt(data, key);
120     encrypted.insert(encrypted.begin(), salt.begin(), salt.end());
121     return encrypted;
122 }
123 
aesDecrypt(const Blob & data,const Blob & key)124 Blob aesDecrypt(const Blob& data, const Blob& key)
125 {
126     if (not aesKeySizeGood(key.size()))
127         throw DecryptError("Wrong key size");
128 
129     if (data.size() <= GCM_IV_SIZE + GCM_DIGEST_SIZE)
130         throw DecryptError("Wrong data size");
131 
132     std::array<uint8_t, GCM_DIGEST_SIZE> digest;
133 
134     struct gcm_aes_ctx aes;
135     gcm_aes_set_key(&aes, key.size(), key.data());
136     gcm_aes_set_iv(&aes, GCM_IV_SIZE, data.data());
137 
138     size_t data_sz = data.size() - GCM_IV_SIZE - GCM_DIGEST_SIZE;
139     Blob ret(data_sz);
140     //gcm_aes_update(&aes, data_sz, data.data() + GCM_IV_SIZE);
141     gcm_aes_decrypt(&aes, data_sz, ret.data(), data.data() + GCM_IV_SIZE);
142     //gcm_aes_digest(aes, GCM_DIGEST_SIZE, digest.data());
143 
144     // TODO compute the proper digest directly from the decryption pass
145     Blob ret_tmp(data_sz);
146     struct gcm_aes_ctx aes_d;
147     gcm_aes_set_key(&aes_d, key.size(), key.data());
148     gcm_aes_set_iv(&aes_d, GCM_IV_SIZE, data.data());
149     gcm_aes_update(&aes_d, ret.size(), ret.data());
150     gcm_aes_encrypt(&aes_d, ret.size(), ret_tmp.data(), ret.data());
151     gcm_aes_digest(&aes_d, GCM_DIGEST_SIZE, digest.data());
152 
153     if (not std::equal(digest.begin(), digest.end(), data.end() - GCM_DIGEST_SIZE))
154         throw DecryptError("Can't decrypt data");
155 
156     return ret;
157 }
158 
aesDecrypt(const Blob & data,const std::string & password)159 Blob aesDecrypt(const Blob& data, const std::string& password)
160 {
161     if (data.size() <= PASSWORD_SALT_LENGTH)
162         throw DecryptError("Wrong data size");
163     Blob salt {data.begin(), data.begin()+PASSWORD_SALT_LENGTH};
164     Blob key = stretchKey(password, salt, 256/8);
165     Blob encrypted {data.begin()+PASSWORD_SALT_LENGTH, data.end()};
166     return aesDecrypt(encrypted, key);
167 }
168 
stretchKey(const std::string & password,Blob & salt,size_t key_length)169 Blob stretchKey(const std::string& password, Blob& salt, size_t key_length)
170 {
171     if (salt.empty()) {
172         salt.resize(PASSWORD_SALT_LENGTH);
173         crypto::random_device rdev;
174         std::generate_n(salt.begin(), salt.size(), std::bind(rand_byte, std::ref(rdev)));
175     }
176     Blob res;
177     res.resize(32);
178     auto ret = argon2i_hash_raw(16, 64*1024, 1, password.data(), password.size(), salt.data(), salt.size(), res.data(), res.size());
179     if (ret != ARGON2_OK)
180         throw CryptoException("Can't compute argon2i !");
181     return hash(res, key_length);
182 }
183 
hash(const Blob & data,size_t hash_len)184 Blob hash(const Blob& data, size_t hash_len)
185 {
186     auto algo = gnutlsHashAlgo(hash_len);
187     size_t res_size = gnutlsHashSize(algo);
188     Blob res;
189     res.resize(res_size);
190     const gnutls_datum_t gdat {(uint8_t*)data.data(), (unsigned)data.size()};
191     if (auto err = gnutls_fingerprint(algo, &gdat, res.data(), &res_size))
192         throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
193     res.resize(std::min(hash_len, res_size));
194     return res;
195 }
196 
hash(const uint8_t * data,size_t data_length,uint8_t * hash,size_t hash_length)197 void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length)
198 {
199     auto algo = gnutlsHashAlgo(hash_length);
200     size_t res_size = hash_length;
201     const gnutls_datum_t gdat {(uint8_t*)data, (unsigned)data_length};
202     if (auto err = gnutls_fingerprint(algo, &gdat, hash, &res_size))
203         throw CryptoException(std::string("Can't compute hash: ") + gnutls_strerror(err));
204 }
205 
PrivateKey()206 PrivateKey::PrivateKey()
207 {}
208 
PrivateKey(gnutls_x509_privkey_t k)209 PrivateKey::PrivateKey(gnutls_x509_privkey_t k) : x509_key(k)
210 {
211     gnutls_privkey_init(&key);
212     if (gnutls_privkey_import_x509(key, k, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
213         key = nullptr;
214         throw CryptoException("Can't load generic private key !");
215     }
216 }
217 
PrivateKey(const Blob & import,const std::string & password)218 PrivateKey::PrivateKey(const Blob& import, const std::string& password)
219 {
220     int err = gnutls_x509_privkey_init(&x509_key);
221     if (err != GNUTLS_E_SUCCESS)
222         throw CryptoException("Can't initialize private key !");
223 
224     const gnutls_datum_t dt {(uint8_t*)import.data(), static_cast<unsigned>(import.size())};
225     const char* password_ptr = password.empty() ? nullptr : password.c_str();
226     int flags = password.empty() ? GNUTLS_PKCS_PLAIN
227                 : ( GNUTLS_PKCS_PBES2_AES_128 | GNUTLS_PKCS_PBES2_AES_192  | GNUTLS_PKCS_PBES2_AES_256
228                   | GNUTLS_PKCS_PKCS12_3DES   | GNUTLS_PKCS_PKCS12_ARCFOUR | GNUTLS_PKCS_PKCS12_RC2_40);
229 
230     err = gnutls_x509_privkey_import2(x509_key, &dt, GNUTLS_X509_FMT_PEM, password_ptr, flags);
231     if (err != GNUTLS_E_SUCCESS) {
232         int err_der = gnutls_x509_privkey_import2(x509_key, &dt, GNUTLS_X509_FMT_DER, password_ptr, flags);
233         if (err_der != GNUTLS_E_SUCCESS) {
234             gnutls_x509_privkey_deinit(x509_key);
235             if (err == GNUTLS_E_DECRYPTION_FAILED or err_der == GNUTLS_E_DECRYPTION_FAILED)
236                 throw DecryptError("Can't decrypt private key");
237             else
238                 throw CryptoException(std::string("Can't load private key: PEM: ") + gnutls_strerror(err)
239                                                                        + " DER: "  + gnutls_strerror(err_der));
240         }
241     }
242 
243     gnutls_privkey_init(&key);
244     if (gnutls_privkey_import_x509(key, x509_key, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
245         throw CryptoException("Can't load generic private key !");
246     }
247 }
248 
PrivateKey(PrivateKey && o)249 PrivateKey::PrivateKey(PrivateKey&& o) noexcept : key(o.key), x509_key(o.x509_key)
250 {
251     o.key = nullptr;
252     o.x509_key = nullptr;
253 }
254 
~PrivateKey()255 PrivateKey::~PrivateKey()
256 {
257     if (key) {
258         gnutls_privkey_deinit(key);
259         key = nullptr;
260     }
261     if (x509_key) {
262         gnutls_x509_privkey_deinit(x509_key);
263         x509_key = nullptr;
264     }
265 }
266 
267 PrivateKey&
operator =(PrivateKey && o)268 PrivateKey::operator=(PrivateKey&& o) noexcept
269 {
270     if (key) {
271         gnutls_privkey_deinit(key);
272         key = nullptr;
273     }
274     if (x509_key) {
275         gnutls_x509_privkey_deinit(x509_key);
276         x509_key = nullptr;
277     }
278     key = o.key; x509_key = o.x509_key;
279     o.key = nullptr; o.x509_key = nullptr;
280     return *this;
281 }
282 
283 Blob
sign(const Blob & data) const284 PrivateKey::sign(const Blob& data) const
285 {
286     if (!key)
287         throw CryptoException("Can't sign data: no private key set !");
288     if (std::numeric_limits<unsigned>::max() < data.size())
289         throw CryptoException("Can't sign data: too large !");
290     gnutls_datum_t sig;
291     const gnutls_datum_t dat {(unsigned char*)data.data(), (unsigned)data.size()};
292     if (gnutls_privkey_sign_data(key, GNUTLS_DIG_SHA512, 0, &dat, &sig) != GNUTLS_E_SUCCESS)
293         throw CryptoException("Can't sign data !");
294     Blob ret(sig.data, sig.data+sig.size);
295     gnutls_free(sig.data);
296     return ret;
297 }
298 
299 Blob
decryptBloc(const uint8_t * src,size_t src_size) const300 PrivateKey::decryptBloc(const uint8_t* src, size_t src_size) const
301 {
302     const gnutls_datum_t dat {(uint8_t*)src, (unsigned)src_size};
303     gnutls_datum_t out;
304     int err = gnutls_privkey_decrypt_data(key, 0, &dat, &out);
305     if (err != GNUTLS_E_SUCCESS)
306         throw DecryptError(std::string("Can't decrypt data: ") + gnutls_strerror(err));
307     Blob ret {out.data, out.data+out.size};
308     gnutls_free(out.data);
309     return ret;
310 }
311 
312 Blob
decrypt(const Blob & cipher) const313 PrivateKey::decrypt(const Blob& cipher) const
314 {
315     if (!key)
316         throw CryptoException("Can't decrypt data without private key !");
317 
318     unsigned key_len = 0;
319     int err = gnutls_privkey_get_pk_algorithm(key, &key_len);
320     if (err < 0)
321         throw CryptoException("Can't read public key length !");
322     if (err != GNUTLS_PK_RSA)
323         throw CryptoException("Must be an RSA key");
324 
325     unsigned cypher_block_sz = key_len / 8;
326     if (cipher.size() < cypher_block_sz)
327         throw DecryptError("Unexpected cipher length");
328     else if (cipher.size() == cypher_block_sz)
329         return decryptBloc(cipher.data(), cypher_block_sz);
330 
331     return aesDecrypt(Blob {cipher.begin() + cypher_block_sz, cipher.end()}, decryptBloc(cipher.data(), cypher_block_sz));
332 }
333 
334 Blob
serialize(const std::string & password) const335 PrivateKey::serialize(const std::string& password) const
336 {
337     if (!x509_key)
338         return {};
339     size_t buf_sz = 8192;
340     Blob buffer;
341     buffer.resize(buf_sz);
342     int err = password.empty()
343         ? gnutls_x509_privkey_export_pkcs8(x509_key, GNUTLS_X509_FMT_PEM, nullptr,          GNUTLS_PKCS_PLAIN,         buffer.data(), &buf_sz)
344         : gnutls_x509_privkey_export_pkcs8(x509_key, GNUTLS_X509_FMT_PEM, password.c_str(), GNUTLS_PKCS_PBES2_AES_256, buffer.data(), &buf_sz);
345     if (err != GNUTLS_E_SUCCESS) {
346         std::cerr << "Could not export private key - " << gnutls_strerror(err) << std::endl;
347         return {};
348     }
349     buffer.resize(buf_sz);
350     return buffer;
351 }
352 
353 PublicKey
getPublicKey() const354 PrivateKey::getPublicKey() const
355 {
356     PublicKey pk;
357     if (auto err = gnutls_pubkey_import_privkey(pk.pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0))
358         throw CryptoException(std::string("Can't retreive public key: ") + gnutls_strerror(err));
359     return pk;
360 }
361 
PublicKey()362 PublicKey::PublicKey()
363 {
364     if (auto err = gnutls_pubkey_init(&pk))
365         throw CryptoException(std::string("Can't initialize public key: ") + gnutls_strerror(err));
366 }
367 
PublicKey(const Blob & dat)368 PublicKey::PublicKey(const Blob& dat) : PublicKey()
369 {
370     unpack(dat.data(), dat.size());
371 }
372 
~PublicKey()373 PublicKey::~PublicKey()
374 {
375     if (pk) {
376         gnutls_pubkey_deinit(pk);
377         pk = nullptr;
378     }
379 }
380 
381 PublicKey&
operator =(PublicKey && o)382 PublicKey::operator=(PublicKey&& o) noexcept
383 {
384     if (pk)
385         gnutls_pubkey_deinit(pk);
386     pk = o.pk;
387     o.pk = nullptr;
388     return *this;
389 }
390 
391 void
pack(Blob & b) const392 PublicKey::pack(Blob& b) const
393 {
394     if (not pk)
395         throw CryptoException(std::string("Could not export public key: null key"));
396     std::vector<uint8_t> tmp(2048);
397     size_t sz = tmp.size();
398     if (int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_DER, tmp.data(), &sz))
399         throw CryptoException(std::string("Could not export public key: ") + gnutls_strerror(err));
400     tmp.resize(sz);
401     b.insert(b.end(), tmp.begin(), tmp.end());
402 }
403 
404 void
unpack(const uint8_t * data,size_t data_size)405 PublicKey::unpack(const uint8_t* data, size_t data_size)
406 {
407     const gnutls_datum_t dat {(uint8_t*)data, (unsigned)data_size};
408     int err = gnutls_pubkey_import(pk, &dat, GNUTLS_X509_FMT_PEM);
409     if (err != GNUTLS_E_SUCCESS)
410         err = gnutls_pubkey_import(pk, &dat, GNUTLS_X509_FMT_DER);
411     if (err != GNUTLS_E_SUCCESS)
412         throw CryptoException(std::string("Could not read public key: ") + gnutls_strerror(err));
413 }
414 
415 std::string
toString() const416 PublicKey::toString() const
417 {
418     if (not pk)
419         throw CryptoException(std::string("Could not print public key: null key"));
420     std::string ret;
421     size_t sz = ret.size();
422     int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_PEM, (void*)ret.data(), &sz);
423     if (err ==  GNUTLS_E_SHORT_MEMORY_BUFFER) {
424         ret.resize(sz);
425         err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_PEM, (void*)ret.data(), &sz);
426     }
427     if (err != GNUTLS_E_SUCCESS)
428         throw CryptoException(std::string("Could not print public key: ") + gnutls_strerror(err));
429     return ret;
430 }
431 
432 void
msgpack_unpack(msgpack::object o)433 PublicKey::msgpack_unpack(msgpack::object o)
434 {
435     if (o.type == msgpack::type::BIN)
436         unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
437     else {
438         Blob dat = unpackBlob(o);
439         unpack(dat.data(), dat.size());
440     }
441 }
442 
443 bool
checkSignature(const Blob & data,const Blob & signature) const444 PublicKey::checkSignature(const Blob& data, const Blob& signature) const
445 {
446     if (!pk)
447         return false;
448     const gnutls_datum_t sig {(uint8_t*)signature.data(), (unsigned)signature.size()};
449     const gnutls_datum_t dat {(uint8_t*)data.data(), (unsigned)data.size()};
450     int rc = gnutls_pubkey_verify_data2(pk, GNUTLS_SIGN_RSA_SHA512, 0, &dat, &sig);
451     return rc >= 0;
452 }
453 
454 void
encryptBloc(const uint8_t * src,size_t src_size,uint8_t * dst,size_t dst_size) const455 PublicKey::encryptBloc(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_size) const
456 {
457     const gnutls_datum_t key_dat {(uint8_t*)src, (unsigned)src_size};
458     gnutls_datum_t encrypted;
459     auto err = gnutls_pubkey_encrypt_data(pk, 0, &key_dat, &encrypted);
460     if (err != GNUTLS_E_SUCCESS)
461         throw CryptoException(std::string("Can't encrypt data: ") + gnutls_strerror(err));
462     if (encrypted.size != dst_size)
463         throw CryptoException("Unexpected cypherblock size");
464     std::copy_n(encrypted.data, encrypted.size, dst);
465     gnutls_free(encrypted.data);
466 }
467 
468 Blob
encrypt(const Blob & data) const469 PublicKey::encrypt(const Blob& data) const
470 {
471     if (!pk)
472         throw CryptoException("Can't read public key !");
473 
474     unsigned key_len = 0;
475     int err = gnutls_pubkey_get_pk_algorithm(pk, &key_len);
476     if (err < 0)
477         throw CryptoException("Can't read public key length !");
478     if (err != GNUTLS_PK_RSA)
479         throw CryptoException("Must be an RSA key");
480 
481     const unsigned max_block_sz = key_len / 8 - 11;
482     const unsigned cypher_block_sz = key_len / 8;
483 
484     /* Use plain RSA if the data is small enough */
485     if (data.size() <= max_block_sz) {
486         Blob ret(cypher_block_sz);
487         encryptBloc(data.data(), data.size(), ret.data(), cypher_block_sz);
488         return ret;
489     }
490 
491     /* Otherwise use RSA+AES-GCM,
492        using the max. AES key size that can fit
493        in a single RSA packet () */
494     unsigned aes_key_sz = aesKeySize(max_block_sz);
495     if (aes_key_sz == 0)
496         throw CryptoException("Key is not long enough for AES128");
497     Blob key(aes_key_sz);
498     {
499         crypto::random_device rdev;
500         std::generate_n(key.begin(), key.size(), std::bind(rand_byte, std::ref(rdev)));
501     }
502     auto data_encrypted = aesEncrypt(data, key);
503 
504     Blob ret;
505     ret.reserve(cypher_block_sz + data_encrypted.size());
506 
507     ret.resize(cypher_block_sz);
508     encryptBloc(key.data(), key.size(), ret.data(), cypher_block_sz);
509     ret.insert(ret.end(), data_encrypted.begin(), data_encrypted.end());
510     return ret;
511 }
512 
513 InfoHash
getId() const514 PublicKey::getId() const
515 {
516     if (not pk)
517         return {};
518     InfoHash id;
519     size_t sz = id.size();
520 #if GNUTLS_VERSION_NUMBER < 0x030401
521     const int flags = 0;
522 #else
523     const int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
524 #endif
525     if (auto err = gnutls_pubkey_get_key_id(pk, flags, id.data(), &sz))
526         throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
527     if (sz != id.size())
528         throw CryptoException("Can't get public key ID: wrong output length.");
529     return id;
530 }
531 
532 PkId
getLongId() const533 PublicKey::getLongId() const
534 {
535     if (not pk)
536         return {};
537 #if GNUTLS_VERSION_NUMBER < 0x030401
538     throw CryptoException("Can't get 256 bits public key ID: GnuTLS 3.4.1 or higher required.");
539 #else
540     PkId h;
541     size_t sz = h.size();
542     if (auto err = gnutls_pubkey_get_key_id(pk, GNUTLS_KEYID_USE_SHA256, h.data(), &sz))
543         throw CryptoException(std::string("Can't get 256 bits public key ID: ") + gnutls_strerror(err));
544     if (sz != h.size())
545         throw CryptoException("Can't get 256 bits public key ID: wrong output length.");
546     return h;
547 #endif
548 }
549 
550 gnutls_digest_algorithm_t
getPreferredDigest() const551 PublicKey::getPreferredDigest() const
552 {
553     gnutls_digest_algorithm_t dig;
554     int result = gnutls_pubkey_get_preferred_hash_algorithm(pk, &dig, nullptr);
555     if (result < 0)
556         return GNUTLS_DIG_UNKNOWN;
557     return dig;
558 }
559 
560 // Certificate Request
561 
562 static NameType
typeFromGnuTLS(gnutls_x509_subject_alt_name_t type)563 typeFromGnuTLS(gnutls_x509_subject_alt_name_t type)
564 {
565     switch(type) {
566     case GNUTLS_SAN_DNSNAME:
567         return NameType::DNS;
568     case GNUTLS_SAN_RFC822NAME:
569         return NameType::RFC822;
570     case GNUTLS_SAN_URI:
571         return NameType::URI;
572     case GNUTLS_SAN_IPADDRESS:
573         return NameType::IP;
574     default:
575         return NameType::UNKNOWN;
576     }
577 }
578 
579 static gnutls_x509_subject_alt_name_t
GnuTLSFromType(NameType type)580 GnuTLSFromType(NameType type)
581 {
582     switch(type) {
583     case NameType::DNS:
584         return GNUTLS_SAN_DNSNAME;
585     case NameType::RFC822:
586         return GNUTLS_SAN_RFC822NAME;
587     case NameType::URI:
588         return GNUTLS_SAN_URI;
589     case NameType::IP:
590         return GNUTLS_SAN_IPADDRESS;
591     default:
592         return (gnutls_x509_subject_alt_name_t)0;
593     }
594 }
595 
596 static std::string
getDN(gnutls_x509_crq_t request,const char * oid)597 getDN(gnutls_x509_crq_t request, const char* oid)
598 {
599     std::string dn;
600     dn.resize(512);
601     size_t dn_sz = dn.size();
602     int ret = gnutls_x509_crq_get_dn_by_oid(request, oid, 0, 0, &(*dn.begin()), &dn_sz);
603     if (ret != GNUTLS_E_SUCCESS)
604         return {};
605     dn.resize(dn_sz);
606     return dn;
607 }
608 
CertificateRequest()609 CertificateRequest::CertificateRequest()
610 {
611     if (auto err = gnutls_x509_crq_init(&request))
612         throw CryptoException(std::string("Can't initialize certificate request: ") + gnutls_strerror(err));
613 }
614 
CertificateRequest(const uint8_t * data,size_t size)615 CertificateRequest::CertificateRequest(const uint8_t* data, size_t size) : CertificateRequest()
616 {
617     const gnutls_datum_t dat {(uint8_t*)data, (unsigned)size};
618     if (auto err = gnutls_x509_crq_import(request, &dat, GNUTLS_X509_FMT_PEM))
619         throw CryptoException(std::string("Can't import certificate request: ") + gnutls_strerror(err));
620 }
621 
~CertificateRequest()622 CertificateRequest::~CertificateRequest()
623 {
624     if (request) {
625         gnutls_x509_crq_deinit(request);
626         request = nullptr;
627     }
628 }
629 
630 CertificateRequest&
operator =(CertificateRequest && o)631 CertificateRequest::operator=(CertificateRequest&& o) noexcept
632 {
633     if (request)
634         gnutls_x509_crq_deinit(request);
635     request = o.request;
636     o.request = nullptr;
637     return *this;
638 }
639 
640 void
setAltName(NameType type,const std::string & name)641 CertificateRequest::setAltName(NameType type, const std::string& name)
642 {
643     gnutls_x509_crq_set_subject_alt_name(request, GnuTLSFromType(type), name.data(), name.size(), 0);
644 }
645 
646 void
setName(const std::string & name)647 CertificateRequest::setName(const std::string& name)
648 {
649     gnutls_x509_crq_set_dn_by_oid(request, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
650 }
651 
652 std::string
getName() const653 CertificateRequest::getName() const
654 {
655     return getDN(request, GNUTLS_OID_X520_COMMON_NAME);
656 }
657 
658 std::string
getUID() const659 CertificateRequest::getUID() const
660 {
661     return getDN(request, GNUTLS_OID_LDAP_UID);
662 }
663 
664 void
setUID(const std::string & uid)665 CertificateRequest::setUID(const std::string& uid)
666 {
667     gnutls_x509_crq_set_dn_by_oid(request, GNUTLS_OID_LDAP_UID, 0, uid.data(), uid.length());
668 }
669 
670 void
sign(const PrivateKey & key,const std::string & password)671 CertificateRequest::sign(const PrivateKey& key, const std::string& password)
672 {
673     gnutls_x509_crq_set_version(request, 1);
674     if (not password.empty())
675         gnutls_x509_crq_set_challenge_password(request, password.c_str());
676 
677     if (auto err = gnutls_x509_crq_set_key(request,  key.x509_key))
678         throw CryptoException(std::string("Can't set certificate request key: ") + gnutls_strerror(err));
679 
680 #if GNUTLS_VERSION_NUMBER < 0x030601
681     if (auto err = gnutls_x509_crq_privkey_sign(request, key.key, key.getPublicKey().getPreferredDigest(), 0))
682         throw CryptoException(std::string("Can't sign certificate request: ") + gnutls_strerror(err));
683 #else
684     if (auto err = gnutls_x509_crq_privkey_sign(request, key.key, GNUTLS_DIG_UNKNOWN, 0))
685         throw CryptoException(std::string("Can't sign certificate request: ") + gnutls_strerror(err));
686 #endif
687 }
688 
689 bool
verify() const690 CertificateRequest::verify() const
691 {
692     return gnutls_x509_crq_verify(request, 0) >= 0;
693 }
694 
695 Blob
pack() const696 CertificateRequest::pack() const
697 {
698     gnutls_datum_t dat {nullptr, 0};
699     if (auto err = gnutls_x509_crq_export2(request, GNUTLS_X509_FMT_PEM, &dat))
700         throw CryptoException(std::string("Can't export certificate request: ") + gnutls_strerror(err));
701     Blob ret(dat.data, dat.data + dat.size);
702     gnutls_free(dat.data);
703     return ret;
704 }
705 
706 // Certificate
707 
708 static std::string
getDN(gnutls_x509_crt_t cert,const char * oid,bool issuer=false)709 getDN(gnutls_x509_crt_t cert, const char* oid, bool issuer = false)
710 {
711     std::string dn;
712     dn.resize(512);
713     size_t dn_sz = dn.size();
714     int ret = issuer
715             ? gnutls_x509_crt_get_issuer_dn_by_oid(cert, oid, 0, 0, &(*dn.begin()), &dn_sz)
716             : gnutls_x509_crt_get_dn_by_oid(       cert, oid, 0, 0, &(*dn.begin()), &dn_sz);
717     if (ret != GNUTLS_E_SUCCESS)
718         return {};
719     dn.resize(dn_sz);
720     return dn;
721 }
722 
Certificate(const Blob & certData)723 Certificate::Certificate(const Blob& certData)
724 {
725     unpack(certData.data(), certData.size());
726 }
727 
728 Certificate&
operator =(Certificate && o)729 Certificate::operator=(Certificate&& o) noexcept
730 {
731     if (cert)
732         gnutls_x509_crt_deinit(cert);
733     cert = o.cert;
734     o.cert = nullptr;
735     issuer = std::move(o.issuer);
736     return *this;
737 }
738 
739 void
unpack(const uint8_t * dat,size_t dat_size)740 Certificate::unpack(const uint8_t* dat, size_t dat_size)
741 {
742     if (cert) {
743         gnutls_x509_crt_deinit(cert);
744         cert = nullptr;
745     }
746     gnutls_x509_crt_t* cert_list;
747     unsigned cert_num;
748     const gnutls_datum_t crt_dt {(uint8_t*)dat, (unsigned)dat_size};
749     int err = gnutls_x509_crt_list_import2(&cert_list, &cert_num, &crt_dt, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED);
750     if (err != GNUTLS_E_SUCCESS)
751         err = gnutls_x509_crt_list_import2(&cert_list, &cert_num, &crt_dt, GNUTLS_X509_FMT_DER, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED);
752     if (err != GNUTLS_E_SUCCESS || cert_num == 0) {
753         cert = nullptr;
754         throw CryptoException(std::string("Could not read certificate - ") + gnutls_strerror(err));
755     }
756 
757     cert = cert_list[0];
758     Certificate* crt = this;
759     size_t i = 1;
760     while (crt and i < cert_num) {
761         crt->issuer = std::make_shared<Certificate>(cert_list[i++]);
762         crt = crt->issuer.get();
763     }
764     gnutls_free(cert_list);
765 }
766 
767 void
msgpack_unpack(msgpack::object o)768 Certificate::msgpack_unpack(msgpack::object o)
769 {
770     if (o.type == msgpack::type::BIN)
771         unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
772     else {
773         Blob dat = unpackBlob(o);
774         unpack(dat.data(), dat.size());
775     }
776 }
777 
778 void
pack(Blob & b) const779 Certificate::pack(Blob& b) const
780 {
781     const Certificate* crt = this;
782     while (crt) {
783         std::string str;
784         size_t buf_sz = 8192;
785         str.resize(buf_sz);
786         if (int err = gnutls_x509_crt_export(crt->cert, GNUTLS_X509_FMT_PEM, &(*str.begin()), &buf_sz)) {
787             std::cerr << "Could not export certificate - " << gnutls_strerror(err) << std::endl;
788             return;
789         }
790         str.resize(buf_sz);
791         b.insert(b.end(), str.begin(), str.end());
792         crt = crt->issuer.get();
793     }
794 }
795 
~Certificate()796 Certificate::~Certificate()
797 {
798     if (cert) {
799         gnutls_x509_crt_deinit(cert);
800         cert = nullptr;
801     }
802 }
803 
804 PublicKey
getPublicKey() const805 Certificate::getPublicKey() const
806 {
807     PublicKey pk_ret;
808     if (auto err = gnutls_pubkey_import_x509(pk_ret.pk, cert, 0))
809         throw CryptoException(std::string("Can't get certificate public key: ") + gnutls_strerror(err));
810     return pk_ret;
811 }
812 
813 InfoHash
getId() const814 Certificate::getId() const
815 {
816     if (not cert)
817         return {};
818     InfoHash id;
819     size_t sz = id.size();
820     if (auto err = gnutls_x509_crt_get_key_id(cert, 0, id.data(), &sz))
821         throw CryptoException(std::string("Can't get certificate public key ID: ") + gnutls_strerror(err));
822     if (sz != id.size())
823         throw CryptoException("Can't get certificate public key ID: wrong output length.");
824     return id;
825 }
826 
827 PkId
getLongId() const828 Certificate::getLongId() const
829 {
830     if (not cert)
831         return {};
832 #if GNUTLS_VERSION_NUMBER < 0x030401
833     throw CryptoException("Can't get certificate 256 bits public key ID: GnuTLS 3.4.1 or higher required.");
834 #else
835     PkId id;
836     size_t sz = id.size();
837     if (auto err = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256, id.data(), &sz))
838         throw CryptoException(std::string("Can't get certificate 256 bits public key ID: ") + gnutls_strerror(err));
839     if (sz != id.size())
840         throw CryptoException("Can't get certificate 256 bits public key ID: wrong output length.");
841     return id;
842 #endif
843 }
844 
845 std::string
getName() const846 Certificate::getName() const
847 {
848     return getDN(cert, GNUTLS_OID_X520_COMMON_NAME);
849 }
850 
851 std::string
getUID() const852 Certificate::getUID() const
853 {
854     return getDN(cert, GNUTLS_OID_LDAP_UID);
855 }
856 
857 std::string
getIssuerName() const858 Certificate::getIssuerName() const
859 {
860     return getDN(cert, GNUTLS_OID_X520_COMMON_NAME, true);
861 }
862 
863 std::string
getIssuerUID() const864 Certificate::getIssuerUID() const
865 {
866     return getDN(cert, GNUTLS_OID_LDAP_UID, true);
867 }
868 
869 std::vector<std::pair<NameType, std::string>>
getAltNames() const870 Certificate::getAltNames() const
871 {
872     std::vector<std::pair<NameType, std::string>> names;
873     unsigned i = 0;
874     std::string name;
875     while (true) {
876         name.resize(512);
877         size_t name_sz = name.size();
878         unsigned type;
879         int ret = gnutls_x509_crt_get_subject_alt_name2(cert, i++, &(*name.begin()), &name_sz, &type, nullptr);
880         if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
881             break;
882         name.resize(name_sz);
883         names.emplace_back(typeFromGnuTLS((gnutls_x509_subject_alt_name_t)type), name);
884     }
885     return names;
886 }
887 
888 bool
isCA() const889 Certificate::isCA() const
890 {
891     unsigned critical;
892     bool ca_flag = gnutls_x509_crt_get_ca_status(cert, &critical) > 0;
893     if (ca_flag) {
894         unsigned usage;
895         auto ret = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
896         /* Conforming CAs MUST include this extension in certificates that
897            contain public keys that are used to validate digital signatures on
898            other public key certificates or CRLs. */
899         if (ret < 0)
900             return false;
901         if (not critical)
902             return true;
903         return usage & GNUTLS_KEY_KEY_CERT_SIGN;
904     }
905     return false;
906 }
907 
908 std::string
toString(bool chain) const909 Certificate::toString(bool chain) const
910 {
911     std::ostringstream ss;
912     const Certificate* crt = this;
913     while (crt) {
914         std::string str;
915         size_t buf_sz = 8192;
916         str.resize(buf_sz);
917         if (int err = gnutls_x509_crt_export(crt->cert, GNUTLS_X509_FMT_PEM, &(*str.begin()), &buf_sz)) {
918             std::cerr << "Could not export certificate - " << gnutls_strerror(err) << std::endl;
919             return {};
920         }
921         str.resize(buf_sz);
922         ss << str;
923         if (not chain)
924             break;
925         crt = crt->issuer.get();
926     }
927     return ss.str();
928 }
929 
930 std::string
print() const931 Certificate::print() const
932 {
933     gnutls_datum_t out;
934     gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &out);
935     std::string ret(out.data, out.data+out.size);
936     gnutls_free(out.data);
937     return ret;
938 }
939 
940 void
revoke(const PrivateKey & key,const Certificate & to_revoke)941 Certificate::revoke(const PrivateKey& key, const Certificate& to_revoke)
942 {
943     if (revocation_lists.empty())
944         revocation_lists.emplace(std::make_shared<RevocationList>());
945     auto& list = *(*revocation_lists.begin());
946     list.revoke(to_revoke);
947     list.sign(key, *this);
948 }
949 
950 void
addRevocationList(RevocationList && list)951 Certificate::addRevocationList(RevocationList&& list)
952 {
953     addRevocationList(std::make_shared<RevocationList>(std::forward<RevocationList>(list)));
954 }
955 
956 void
addRevocationList(std::shared_ptr<RevocationList> list)957 Certificate::addRevocationList(std::shared_ptr<RevocationList> list)
958 {
959     if (revocation_lists.find(list) != revocation_lists.end())
960         return; // Already in the list
961     if (not list->isSignedBy(*this))
962         throw CryptoException("CRL is not signed by this certificate");
963     revocation_lists.emplace(std::move(list));
964 }
965 
966 std::chrono::system_clock::time_point
getActivation() const967 Certificate::getActivation() const
968 {
969     auto t = gnutls_x509_crt_get_activation_time(cert);
970     if (t == (time_t)-1)
971         return std::chrono::system_clock::time_point::min();
972     return std::chrono::system_clock::from_time_t(t);
973 }
974 
975 std::chrono::system_clock::time_point
getExpiration() const976 Certificate::getExpiration() const
977 {
978     auto t = gnutls_x509_crt_get_expiration_time(cert);
979     if (t == (time_t)-1)
980         return std::chrono::system_clock::time_point::min();
981     return std::chrono::system_clock::from_time_t(t);
982 }
983 
984 gnutls_digest_algorithm_t
getPreferredDigest() const985 Certificate::getPreferredDigest() const
986 {
987     return getPublicKey().getPreferredDigest();
988 }
989 
990 // PrivateKey
991 
992 PrivateKey
generate(unsigned key_length)993 PrivateKey::generate(unsigned key_length)
994 {
995     gnutls_x509_privkey_t key;
996     if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
997         throw CryptoException("Can't initialize private key.");
998     int err = gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, key_length, 0);
999     if (err != GNUTLS_E_SUCCESS) {
1000         gnutls_x509_privkey_deinit(key);
1001         throw CryptoException(std::string("Can't generate RSA key pair: ") + gnutls_strerror(err));
1002     }
1003     return PrivateKey{key};
1004 }
1005 
1006 PrivateKey
generateEC()1007 PrivateKey::generateEC()
1008 {
1009     gnutls_x509_privkey_t key;
1010     if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
1011         throw CryptoException("Can't initialize private key.");
1012     int err = gnutls_x509_privkey_generate(key, GNUTLS_PK_EC, gnutls_sec_param_to_pk_bits(GNUTLS_PK_EC, GNUTLS_SEC_PARAM_ULTRA), 0);
1013     if (err != GNUTLS_E_SUCCESS) {
1014         gnutls_x509_privkey_deinit(key);
1015         throw CryptoException(std::string("Can't generate EC key pair: ") + gnutls_strerror(err));
1016     }
1017     return PrivateKey{key};
1018 }
1019 
1020 Identity
generateIdentity(const std::string & name,const Identity & ca,unsigned key_length,bool is_ca)1021 generateIdentity(const std::string& name, const Identity& ca, unsigned key_length, bool is_ca)
1022 {
1023     auto key = std::make_shared<PrivateKey>(PrivateKey::generate(key_length));
1024     auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
1025     return {std::move(key), std::move(cert)};
1026 }
1027 
1028 
1029 Identity
generateIdentity(const std::string & name,const Identity & ca,unsigned key_length)1030 generateIdentity(const std::string& name, const Identity& ca, unsigned key_length) {
1031     return generateIdentity(name, ca, key_length, !ca.first || !ca.second);
1032 }
1033 
1034 Identity
generateEcIdentity(const std::string & name,const Identity & ca,bool is_ca)1035 generateEcIdentity(const std::string& name, const Identity& ca, bool is_ca)
1036 {
1037     auto key = std::make_shared<PrivateKey>(PrivateKey::generateEC());
1038     auto cert = std::make_shared<Certificate>(Certificate::generate(*key, name, ca, is_ca));
1039     return {std::move(key), std::move(cert)};
1040 }
1041 
1042 Identity
generateEcIdentity(const std::string & name,const Identity & ca)1043 generateEcIdentity(const std::string& name, const Identity& ca) {
1044     return generateEcIdentity(name, ca, !ca.first || !ca.second);
1045 }
1046 
1047 void
saveIdentity(const Identity & id,const std::string & path,const std::string & privkey_password)1048 saveIdentity(const Identity& id, const std::string& path, const std::string& privkey_password)
1049 {
1050     {
1051         auto ca_key_data = id.first->serialize(privkey_password);
1052         std::ofstream key_file(path + ".pem");
1053         key_file.write((char*)ca_key_data.data(), ca_key_data.size());
1054     }
1055     {
1056         auto ca_key_data = id.second->getPacked();
1057         std::ofstream crt_file(path + ".crt");
1058         crt_file.write((char*)ca_key_data.data(), ca_key_data.size());
1059     }
1060 }
1061 
1062 void
setValidityPeriod(gnutls_x509_crt_t cert,int64_t validity)1063 setValidityPeriod(gnutls_x509_crt_t cert, int64_t validity)
1064 {
1065     int64_t now = time(nullptr);
1066     /* 2038 bug: don't allow time wrap */
1067     auto boundTime = [](int64_t t) -> time_t {
1068         return std::min<int64_t>(t, std::numeric_limits<time_t>::max());
1069     };
1070     gnutls_x509_crt_set_activation_time(cert, boundTime(now));
1071     gnutls_x509_crt_set_expiration_time(cert, boundTime(now + validity));
1072 }
1073 
1074 void
setRandomSerial(gnutls_x509_crt_t cert)1075 setRandomSerial(gnutls_x509_crt_t cert)
1076 {
1077     random_device rdev;
1078     std::uniform_int_distribution<uint64_t> dist{};
1079     uint64_t cert_serial = dist(rdev);
1080     gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
1081 }
1082 
1083 Certificate
generate(const PrivateKey & key,const std::string & name,const Identity & ca,bool is_ca)1084 Certificate::generate(const PrivateKey& key, const std::string& name, const Identity& ca, bool is_ca)
1085 {
1086     gnutls_x509_crt_t cert;
1087     if (not key.x509_key or gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
1088         return {};
1089     Certificate ret {cert};
1090 
1091     setValidityPeriod(cert, 10 * 365 * 24 * 60 * 60);
1092     if (gnutls_x509_crt_set_key(cert, key.x509_key) != GNUTLS_E_SUCCESS) {
1093         std::cerr << "Error when setting certificate key" << std::endl;
1094         return {};
1095     }
1096     if (gnutls_x509_crt_set_version(cert, 3) != GNUTLS_E_SUCCESS) {
1097         std::cerr << "Error when setting certificate version" << std::endl;
1098         return {};
1099     }
1100 
1101     // TODO: compute the subject key using the recommended RFC method
1102     auto pk = key.getPublicKey();
1103     auto pk_id = pk.getId();
1104     const std::string uid_str = pk_id.toString();
1105 
1106     gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
1107     gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
1108     gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
1109 
1110     setRandomSerial(cert);
1111 
1112     unsigned key_usage = 0;
1113     if (is_ca) {
1114         gnutls_x509_crt_set_ca_status(cert, 1);
1115         key_usage |= GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN;
1116     } else {
1117         key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT;
1118     }
1119     gnutls_x509_crt_set_key_usage(cert, key_usage);
1120 
1121     if (ca.first && ca.second) {
1122         if (not ca.second->isCA()) {
1123             // Signing certificate must be CA.
1124             return {};
1125         }
1126         if (gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, pk.getPreferredDigest(), 0) != GNUTLS_E_SUCCESS) {
1127             std::cerr << "Error when signing certificate" << std::endl;
1128             return {};
1129         }
1130         ret.issuer = ca.second;
1131     } else {
1132         if (gnutls_x509_crt_privkey_sign(cert, cert, key.key, pk.getPreferredDigest(), 0) != GNUTLS_E_SUCCESS) {
1133             std::cerr << "Error when signing certificate" << std::endl;
1134             return {};
1135         }
1136     }
1137 
1138     return ret.getPacked();
1139 }
1140 
1141 Certificate
generate(const CertificateRequest & request,const Identity & ca)1142 Certificate::generate(const CertificateRequest& request, const Identity& ca)
1143 {
1144     gnutls_x509_crt_t cert;
1145     if (auto err = gnutls_x509_crt_init(&cert))
1146         throw CryptoException(std::string("Can't initialize certificate: ") + gnutls_strerror(err));
1147     Certificate ret {cert};
1148     if (auto err = gnutls_x509_crt_set_crq(cert, request.get()))
1149         throw CryptoException(std::string("Can't initialize certificate: ") + gnutls_strerror(err));
1150 
1151     if (auto err = gnutls_x509_crt_set_version(cert, 3)) {
1152         throw CryptoException(std::string("Can't set certificate version: ") + gnutls_strerror(err));
1153     }
1154 
1155     setValidityPeriod(cert, 10 * 365 * 24 * 60 * 60);
1156     setRandomSerial(cert);
1157 
1158     if (auto err = gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, ca.second->getPreferredDigest(), 0)) {
1159         throw CryptoException(std::string("Can't sign certificate: ") + gnutls_strerror(err));
1160     }
1161     ret.issuer = ca.second;
1162 
1163     return ret.getPacked();
1164 }
1165 
1166 std::vector<std::shared_ptr<RevocationList>>
getRevocationLists() const1167 Certificate::getRevocationLists() const
1168 {
1169     std::vector<std::shared_ptr<RevocationList>> ret;
1170     ret.reserve(revocation_lists.size());
1171     for (const auto& crl : revocation_lists)
1172         ret.emplace_back(crl);
1173     return ret;
1174 }
1175 
1176 // RevocationList
1177 
RevocationList()1178 RevocationList::RevocationList()
1179 {
1180     gnutls_x509_crl_init(&crl);
1181 }
1182 
RevocationList(const Blob & b)1183 RevocationList::RevocationList(const Blob& b)
1184 {
1185     gnutls_x509_crl_init(&crl);
1186     try {
1187         unpack(b.data(), b.size());
1188     } catch (const std::exception& e) {
1189         gnutls_x509_crl_deinit(crl);
1190         crl = nullptr;
1191         throw e;
1192     }
1193 }
1194 
~RevocationList()1195 RevocationList::~RevocationList()
1196 {
1197     if (crl) {
1198         gnutls_x509_crl_deinit(crl);
1199         crl = nullptr;
1200     }
1201 }
1202 
1203 void
pack(Blob & b) const1204 RevocationList::pack(Blob& b) const
1205 {
1206     gnutls_datum_t gdat {nullptr, 0};
1207     if (auto err = gnutls_x509_crl_export2(crl, GNUTLS_X509_FMT_DER, &gdat)) {
1208         throw CryptoException(std::string("Can't export CRL: ") + gnutls_strerror(err));
1209     }
1210     b.insert(b.end(), gdat.data, gdat.data + gdat.size);
1211     gnutls_free(gdat.data);
1212 }
1213 
1214 void
unpack(const uint8_t * dat,size_t dat_size)1215 RevocationList::unpack(const uint8_t* dat, size_t dat_size)
1216 {
1217     if (std::numeric_limits<unsigned>::max() < dat_size)
1218         throw CryptoException("Can't load CRL: too large!");
1219     const gnutls_datum_t gdat {(uint8_t*)dat, (unsigned)dat_size};
1220     if (auto err_pem = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_PEM))
1221         if (auto err_der = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_DER)) {
1222             throw CryptoException(std::string("Can't load CRL: PEM: ") + gnutls_strerror(err_pem)
1223                                                            + " DER: "  + gnutls_strerror(err_der));
1224         }
1225 }
1226 
1227 void
msgpack_unpack(msgpack::object o)1228 RevocationList::msgpack_unpack(msgpack::object o)
1229 {
1230     try {
1231         if (o.type == msgpack::type::BIN)
1232             unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
1233         else {
1234             Blob dat = unpackBlob(o);
1235             unpack(dat.data(), dat.size());
1236         }
1237     } catch (...) {
1238         throw msgpack::type_error();
1239     }
1240 }
1241 
1242 bool
isRevoked(const Certificate & crt) const1243 RevocationList::isRevoked(const Certificate& crt) const
1244 {
1245     auto ret = gnutls_x509_crt_check_revocation(crt.cert, &crl, 1);
1246     if (ret < 0)
1247         throw CryptoException(std::string("Can't check certificate revocation status: ") + gnutls_strerror(ret));
1248     return ret != 0;
1249 }
1250 
1251 void
revoke(const Certificate & crt,std::chrono::system_clock::time_point t)1252 RevocationList::revoke(const Certificate& crt, std::chrono::system_clock::time_point t)
1253 {
1254     if (t == time_point::min())
1255         t = clock::now();
1256     if (auto err = gnutls_x509_crl_set_crt(crl, crt.cert, std::chrono::system_clock::to_time_t(t)))
1257         throw CryptoException(std::string("Can't revoke certificate: ") + gnutls_strerror(err));
1258 }
1259 
1260 static std::string
getCRLIssuerDN(gnutls_x509_crl_t cert,const char * oid)1261 getCRLIssuerDN(gnutls_x509_crl_t cert, const char* oid)
1262 {
1263     std::string dn;
1264     dn.resize(512);
1265     size_t dn_sz = dn.size();
1266     int ret = gnutls_x509_crl_get_issuer_dn_by_oid(cert, oid, 0, 0, &(*dn.begin()), &dn_sz);
1267     if (ret != GNUTLS_E_SUCCESS)
1268         return {};
1269     dn.resize(dn_sz);
1270     return dn;
1271 }
1272 
1273 std::string
getIssuerName() const1274 RevocationList::getIssuerName() const
1275 {
1276     return getCRLIssuerDN(crl, GNUTLS_OID_X520_COMMON_NAME);
1277 }
1278 
1279 /** Read CRL issuer User ID (UID) */
1280 std::string
getIssuerUID() const1281 RevocationList::getIssuerUID() const
1282 {
1283     return getCRLIssuerDN(crl, GNUTLS_OID_LDAP_UID);
1284 }
1285 
1286 RevocationList::time_point
getNextUpdateTime() const1287 RevocationList::getNextUpdateTime() const
1288 {
1289     auto t = gnutls_x509_crl_get_next_update(crl);
1290     if (t == (time_t)-1)
1291         return std::chrono::system_clock::time_point::min();
1292     return std::chrono::system_clock::from_time_t(t);
1293 }
1294 
1295 RevocationList::time_point
getUpdateTime() const1296 RevocationList::getUpdateTime() const
1297 {
1298     auto t = gnutls_x509_crl_get_this_update(crl);
1299     if (t == (time_t)-1)
1300         return std::chrono::system_clock::time_point::min();
1301     return std::chrono::system_clock::from_time_t(t);
1302 }
1303 
1304 enum class Endian : uint32_t
1305 {
1306     LITTLE = 0,
1307     BIG = 1
1308 };
1309 
1310 template <typename T>
endian(T w,Endian endian=Endian::BIG)1311 T endian(T w, Endian endian = Endian::BIG)
1312 {
1313     // this gets optimized out into if (endian == host_endian) return w;
1314     union { uint64_t quad; uint32_t islittle; } t;
1315     t.quad = 1;
1316     if (t.islittle ^ (uint32_t)endian) return w;
1317     T r = 0;
1318 
1319     // decent compilers will unroll this (gcc)
1320     // or even convert straight into single bswap (clang)
1321     for (size_t i = 0; i < sizeof(r); i++) {
1322         r <<= 8;
1323         r |= w & 0xff;
1324         w >>= 8;
1325     }
1326     return r;
1327 }
1328 
1329 void
sign(const PrivateKey & key,const Certificate & ca,duration validity)1330 RevocationList::sign(const PrivateKey& key, const Certificate& ca, duration validity)
1331 {
1332     if (auto err = gnutls_x509_crl_set_version(crl, 2))
1333         throw CryptoException(std::string("Can't set CRL version: ") + gnutls_strerror(err));
1334     auto now = std::chrono::system_clock::now();
1335     auto next_update = (validity == duration{}) ? ca.getExpiration() : now + validity;
1336     if (auto err = gnutls_x509_crl_set_this_update(crl, std::chrono::system_clock::to_time_t(now)))
1337         throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err));
1338     if (auto err = gnutls_x509_crl_set_next_update(crl, std::chrono::system_clock::to_time_t(next_update)))
1339         throw CryptoException(std::string("Can't set CRL next update time: ") + gnutls_strerror(err));
1340     uint64_t number {0};
1341     size_t number_sz {sizeof(number)};
1342     unsigned critical {0};
1343     gnutls_x509_crl_get_number(crl, &number, &number_sz, &critical);
1344     if (number == 0) {
1345         // initialize to a random number
1346         number_sz = sizeof(number);
1347         random_device rdev;
1348         std::generate_n((uint8_t*)&number, sizeof(number), std::bind(rand_byte, std::ref(rdev)));
1349     } else
1350         number = endian(endian(number) + 1);
1351     if (auto err = gnutls_x509_crl_set_number(crl, &number, sizeof(number)))
1352         throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err));
1353     if (auto err = gnutls_x509_crl_sign2(crl, ca.cert, key.x509_key, GNUTLS_DIG_SHA512, 0))
1354         throw CryptoException(std::string("Can't sign certificate revocation list: ") + gnutls_strerror(err));
1355     // to be able to actually use the CRL we need to serialize/deserialize it
1356     auto packed = getPacked();
1357     unpack(packed.data(), packed.size());
1358 }
1359 
1360 bool
isSignedBy(const Certificate & issuer) const1361 RevocationList::isSignedBy(const Certificate& issuer) const
1362 {
1363     unsigned result {0};
1364     auto err = gnutls_x509_crl_verify(crl, &issuer.cert, 1, 0, &result);
1365     if (err < 0) {
1366         //std::cout << "Can't verify CRL: " << err << " " << result << " " << gnutls_strerror(err) << std::endl;
1367         return false;
1368     }
1369     return result == 0;
1370 }
1371 
1372 
1373 Blob
getNumber() const1374 RevocationList::getNumber() const
1375 {
1376     Blob number(20);
1377     size_t number_sz {number.size()};
1378     unsigned critical {0};
1379     gnutls_x509_crl_get_number(crl, number.data(), &number_sz, &critical);
1380     if (number_sz != number.size())
1381         number.resize(number_sz);
1382     return number;
1383 }
1384 
1385 std::string
toString() const1386 RevocationList::toString() const
1387 {
1388     gnutls_datum_t out;
1389     gnutls_x509_crl_print(crl, GNUTLS_CRT_PRINT_FULL, &out);
1390     std::string ret(out.data, out.data+out.size);
1391     gnutls_free(out.data);
1392     return ret;
1393 }
1394 
1395 // TrustList
1396 
TrustList()1397 TrustList::TrustList() {
1398     gnutls_x509_trust_list_init(&trust, 0);
1399 }
1400 
~TrustList()1401 TrustList::~TrustList() {
1402     gnutls_x509_trust_list_deinit(trust, 1);
1403 }
1404 
1405 TrustList&
operator =(TrustList && o)1406 TrustList::operator=(TrustList&& o) noexcept
1407 {
1408     if (trust)
1409         gnutls_x509_trust_list_deinit(trust, true);
1410     trust = o.trust;
1411     o.trust = nullptr;
1412     return *this;
1413 }
1414 
add(const Certificate & crt)1415 void TrustList::add(const Certificate& crt)
1416 {
1417     auto chain = crt.getChainWithRevocations(true);
1418     gnutls_x509_trust_list_add_cas(trust, chain.first.data(), chain.first.size(), GNUTLS_TL_NO_DUPLICATES);
1419     if (not chain.second.empty())
1420         gnutls_x509_trust_list_add_crls(
1421                 trust,
1422                 chain.second.data(), chain.second.size(),
1423                 GNUTLS_TL_VERIFY_CRL | GNUTLS_TL_NO_DUPLICATES, 0);
1424 }
1425 
add(const RevocationList & crl)1426 void TrustList::add(const RevocationList& crl)
1427 {
1428     auto copy = crl.getCopy();
1429     gnutls_x509_trust_list_add_crls(trust, &copy, 1, GNUTLS_TL_VERIFY_CRL | GNUTLS_TL_NO_DUPLICATES, 0);
1430 }
1431 
remove(const Certificate & crt,bool parents)1432 void TrustList::remove(const Certificate& crt, bool parents)
1433 {
1434     gnutls_x509_trust_list_remove_cas(trust, &crt.cert, 1);
1435     if (parents) {
1436         for (auto c = crt.issuer; c; c = c->issuer)
1437             gnutls_x509_trust_list_remove_cas(trust, &c->cert, 1);
1438     }
1439 }
1440 
1441 TrustList::VerifyResult
verify(const Certificate & crt) const1442 TrustList::verify(const Certificate& crt) const
1443 {
1444     auto chain = crt.getChain();
1445     VerifyResult ret;
1446     ret.ret = gnutls_x509_trust_list_verify_crt2(
1447         trust,
1448         chain.data(), chain.size(),
1449         nullptr, 0,
1450         GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_MEDIUM),
1451         &ret.result, nullptr);
1452     return ret;
1453 }
1454 
1455 std::string
toString() const1456 TrustList::VerifyResult::toString() const
1457 {
1458     std::ostringstream ss;
1459     ss << *this;
1460     return ss.str();
1461 }
1462 
operator <<(std::ostream & o,const TrustList::VerifyResult & h)1463 std::ostream& operator<< (std::ostream& o, const TrustList::VerifyResult& h)
1464 {
1465     if (h.ret < 0) {
1466         o << "Error verifying certificate: " << gnutls_strerror(h.ret) << std::endl;
1467     } else if (h.result & GNUTLS_CERT_INVALID) {
1468         o << "Certificate check failed with code: " << h.result << std::endl;
1469         if (h.result & GNUTLS_CERT_SIGNATURE_FAILURE)
1470             o << "* The signature verification failed." << std::endl;
1471         if (h.result & GNUTLS_CERT_REVOKED)
1472             o << "* Certificate is revoked" << std::endl;
1473         if (h.result & GNUTLS_CERT_SIGNER_NOT_FOUND)
1474             o << "* Certificate's issuer is not known" << std::endl;
1475         if (h.result & GNUTLS_CERT_SIGNER_NOT_CA)
1476             o << "* Certificate's issuer not a CA" << std::endl;
1477         if (h.result & GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE)
1478             o << "* Certificate's signer constraints were violated" << std::endl;
1479         if (h.result & GNUTLS_CERT_INSECURE_ALGORITHM)
1480             o << "* Certificate was signed using an insecure algorithm" << std::endl;
1481         if (h.result & GNUTLS_CERT_NOT_ACTIVATED)
1482             o << "* Certificate is not yet activated" << std::endl;
1483         if (h.result & GNUTLS_CERT_EXPIRED)
1484             o << "* Certificate has expired" << std::endl;
1485         if (h.result & GNUTLS_CERT_UNEXPECTED_OWNER)
1486             o << "* The owner is not the expected one" << std::endl;
1487 #if GNUTLS_VERSION_NUMBER >= 0x030401
1488         if (h.result & GNUTLS_CERT_PURPOSE_MISMATCH)
1489             o << "* Certificate or an intermediate does not match the intended purpose" << std::endl;
1490 #endif
1491         if (h.result & GNUTLS_CERT_MISMATCH)
1492             o << "* Certificate presented isn't the expected one" << std::endl;
1493     } else {
1494         o << "Certificate is valid" << std::endl;
1495     }
1496     return o;
1497 }
1498 
1499 }
1500 }
1501