diff --git a/include/opendht/crypto.h b/include/opendht/crypto.h index f2276523d24feed69bb123753c1388ad03d6e95f..1390f4caa2c9110db61a40d7a959ce99fcb2b454 100644 --- a/include/opendht/crypto.h +++ b/include/opendht/crypto.h @@ -317,6 +317,38 @@ private: }; +class OPENDHT_PUBLIC RevocationList +{ +public: + RevocationList(); + RevocationList(const Blob& b); + ~RevocationList(); + + bool isRevoked(const Certificate& crt) const; + + void revoke(const Certificate& crt, std::chrono::system_clock::time_point t = {}); + + /** + * Sign this revocation list using provided key and certificate. + */ + void sign(const PrivateKey&, const Certificate&); + void sign(const Identity& id) { sign(*id.first, *id.second); } + + bool isIssuedBy(const Certificate& crt); + + bool isValid(const Certificate& issuer); + + std::string toString(); + + gnutls_x509_crl_t get() { return crl; } + +private: + gnutls_x509_crl_t crl {}; + RevocationList(const RevocationList&) = delete; + RevocationList& operator=(const RevocationList&) = delete; +}; + + /** * Generate an RSA key pair (4096 bits) and a certificate. * @param name the name used in the generated certificate diff --git a/src/crypto.cpp b/src/crypto.cpp index d205ae4bf0ac8f7bef46c419f262bca3588e9819..280e472a9e1c04ba7c90f51fc547868aafa71c31 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -812,5 +812,119 @@ Certificate::generate(const PrivateKey& key, const std::string& name, Identity c return ret; } +RevocationList::RevocationList() +{ + gnutls_x509_crl_init(&crl); +} + +RevocationList::RevocationList(const Blob& b) +{ + gnutls_x509_crl_init(&crl); + const gnutls_datum_t gdat {(uint8_t*)b.data(), (unsigned)b.size()}; + if (auto err_pem = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_PEM)) + if (auto err_der = gnutls_x509_crl_import(crl, &gdat, GNUTLS_X509_FMT_DER)) { + gnutls_x509_crl_deinit(crl); + crl = nullptr; + throw CryptoException(std::string("Can't load CRL: PEM: ") + gnutls_strerror(err_pem) + + " DER: " + gnutls_strerror(err_der)); + } +} + +RevocationList::~RevocationList() +{ + if (crl) { + gnutls_x509_crl_deinit(crl); + crl = nullptr; + } +} + +bool +RevocationList::isRevoked(const Certificate& crt) const +{ + auto ret = gnutls_x509_crt_check_revocation(crt.cert, &crl, 1); + if (ret < 0) + throw CryptoException(std::string("Can't check certificate revocation status: ") + gnutls_strerror(ret)); + return ret != 0; +} + +void +RevocationList::revoke(const Certificate& crt, std::chrono::system_clock::time_point t) +{ + if (auto err = gnutls_x509_crl_set_crt(crl, crt.cert, std::chrono::system_clock::to_time_t(t))) + throw CryptoException(std::string("Can't revoke certificate: ") + gnutls_strerror(err)); +} + +enum class Endian : uint32_t +{ + LITTLE = 0, + BIG = 1 +}; + +template <typename T> +T endian(T w, Endian endian) +{ + // this gets optimized out into if (endian == host_endian) return w; + union { uint64_t quad; uint32_t islittle; } t; + t.quad = 1; + if (t.islittle ^ (uint32_t)endian) return w; + T r = 0; + + // decent compilers will unroll this (gcc) + // or even convert straight into single bswap (clang) + for (size_t i = 0; i < sizeof(r); i++) { + r <<= 8; + r |= w & 0xff; + w >>= 8; + } + return r; +} + +void +RevocationList::sign(const PrivateKey& key, const Certificate& ca) +{ + if (auto err = gnutls_x509_crl_set_version(crl, 2)) + throw CryptoException(std::string("Can't set CRL version: ") + gnutls_strerror(err)); + auto now = std::chrono::system_clock::now(); + auto next_update = now + std::chrono::hours(24*7); + if (auto err = gnutls_x509_crl_set_this_update(crl, std::chrono::system_clock::to_time_t(now))) + throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err)); + if (auto err = gnutls_x509_crl_set_next_update(crl, std::chrono::system_clock::to_time_t(next_update))) + throw CryptoException(std::string("Can't set CRL next update time: ") + gnutls_strerror(err)); + uint64_t number {0}; + size_t number_sz {sizeof(number)}; + unsigned critical {0}; + gnutls_x509_crl_get_number(crl, &number, &number_sz, &critical); + auto s = endian(number, Endian::BIG); + s++; + number = endian(s, Endian::BIG); + if (auto err = gnutls_x509_crl_set_number(crl, &number, sizeof(number))) + throw CryptoException(std::string("Can't set CRL update time: ") + gnutls_strerror(err)); + if (auto err = gnutls_x509_crl_sign2(crl, ca.cert, key.x509_key, GNUTLS_DIG_SHA512, 0)) + throw CryptoException(std::string("Can't sign certificate revocation list: ") + gnutls_strerror(err)); +} + +bool +RevocationList::isIssuedBy(const Certificate& crt) +{ + return gnutls_x509_crl_check_issuer(crl, crt.cert); +} + +bool +RevocationList::isValid(const Certificate& issuer) +{ + unsigned result {0}; + gnutls_x509_crl_verify(crl, &issuer.cert, 1, 0, &result); +} + +std::string +RevocationList::toString() +{ + gnutls_datum_t out; + gnutls_x509_crl_print(crl, GNUTLS_CRT_PRINT_FULL, &out); + std::string ret(out.data, out.data+out.size); + gnutls_free(out.data); + return ret; +} + } }