From 6c66cf6873e023e54ab4ece566abf6845e49522b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com> Date: Thu, 29 Dec 2016 16:17:26 -0500 Subject: [PATCH] ringaccount: support device revocation Add support for Ring device revocation. Each device manages a local revocation list, synced with other devices linked with the account. When a device is revoked, its certificate is added to the local certificate revocation list (standard x509 CRL). CRL support from the trust store and OpenDHT is used. Tuleap: #1457 Change-Id: I227e21afb3234e70ad562a5a4d0cf1084d61f174 --- .../cx.ring.Ring.ConfigurationManager.xml | 17 +++++ bin/dbus/dbusconfigurationmanager.cpp | 6 ++ bin/dbus/dbusconfigurationmanager.h | 1 + src/client/configurationmanager.cpp | 9 +++ src/dring/configurationmanager_interface.h | 1 + src/ringdht/ringaccount.cpp | 70 +++++++++++++++++-- src/ringdht/ringaccount.h | 12 +++- 7 files changed, 110 insertions(+), 6 deletions(-) diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml index 76c36ca753..1cda603b11 100644 --- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml +++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml @@ -192,6 +192,23 @@ </arg> </signal> + <method name="revokeDevice" tp:name-for-bindings="revokeDevice"> + <tp:docstring> + Revoke device attached to the given Ring account, and publish the new revocation list. + </tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <arg type="s" name="password" direction="in"> + </arg> + <arg type="s" name="deviceId" direction="in"> + </arg> + <arg type="b" name="success" direction="out"> + <tp:docstring> + True if the operation was performed successfully. + </tp:docstring> + </arg> + </method> + <signal name="knownDevicesChanged" tp:name-for-bindings="knownDevicesChanged"> <tp:docstring> Notify clients when a new device linked to this account is found. diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp index 43169ae24a..a782e90172 100644 --- a/bin/dbus/dbusconfigurationmanager.cpp +++ b/bin/dbus/dbusconfigurationmanager.cpp @@ -80,6 +80,12 @@ DBusConfigurationManager::exportOnRing(const std::string& accountID, const std:: return DRing::exportOnRing(accountID, password); } +auto +DBusConfigurationManager::revokeDevice(const std::string& accountID, const std::string& password, const std::string& device) -> decltype(DRing::revokeDevice(accountID, password, device)) +{ + return DRing::revokeDevice(accountID, password, device); +} + auto DBusConfigurationManager::getKnownRingDevices(const std::string& accountID) -> decltype(DRing::getKnownRingDevices(accountID)) { diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h index 2c530f8596..7b6573c7f5 100644 --- a/bin/dbus/dbusconfigurationmanager.h +++ b/bin/dbus/dbusconfigurationmanager.h @@ -64,6 +64,7 @@ class DBusConfigurationManager : std::map<std::string, std::string> getAccountTemplate(const std::string& accountType); std::string addAccount(const std::map<std::string, std::string>& details); bool exportOnRing(const std::string& accountID, const std::string& password); + bool revokeDevice(const std::string& accountID, const std::string& password, const std::string& device); std::map<std::string, std::string> getKnownRingDevices(const std::string& accountID); bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name); bool lookupAddress(const std::string& account, const std::string& nameserver, const std::string& address); diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp index c235888b36..36324bf8a8 100644 --- a/src/client/configurationmanager.cpp +++ b/src/client/configurationmanager.cpp @@ -287,6 +287,15 @@ exportOnRing(const std::string& accountID, const std::string& password) return false; } +bool +revokeDevice(const std::string& accountID, const std::string& password, const std::string& deviceID) +{ + if (const auto account = ring::Manager::instance().getAccount<ring::RingAccount>(accountID)) { + return account->revokeDevice(password, deviceID); + } + return false; +} + std::map<std::string, std::string> getKnownRingDevices(const std::string& accountId) { diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h index ad396ca40f..02db6f7403 100644 --- a/src/dring/configurationmanager_interface.h +++ b/src/dring/configurationmanager_interface.h @@ -46,6 +46,7 @@ void setAccountActive(const std::string& accountID, bool active); std::map<std::string, std::string> getAccountTemplate(const std::string& accountType); std::string addAccount(const std::map<std::string, std::string>& details); bool exportOnRing(const std::string& accountID, const std::string& password); +bool revokeDevice(const std::string& accountID, const std::string& password, const std::string& deviceID); std::map<std::string, std::string> getKnownRingDevices(const std::string& accountID); bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name); diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp index 22f90f4586..f4305badb5 100644 --- a/src/ringdht/ringaccount.cpp +++ b/src/ringdht/ringaccount.cpp @@ -166,6 +166,9 @@ struct RingAccount::ArchiveContent /** Generated CA key (for self-signed certificates) */ std::shared_ptr<dht::crypto::PrivateKey> ca_key; + /** Revoked devices */ + std::shared_ptr<dht::crypto::RevocationList> revoked; + /** Ethereum private key */ std::vector<uint8_t> eth_key; @@ -763,10 +766,16 @@ RingAccount::hasSignedReceipt() return false; } - auto pk = identity_.second->issuer->getPublicKey(); - RING_WARN("hasSignedReceipt() with %s", pk.getId().toString().c_str()); + auto accountCertificate = identity_.second->issuer; + if (not accountCertificate) { + RING_WARN("Device certificate must be signed by the account certificate"); + return false; + } + + auto pk = accountCertificate->getPublicKey(); + RING_WARN("Checking device receipt for account %s", pk.getId().toString().c_str()); if (!pk.checkSignature({receipt_.begin(), receipt_.end()}, receiptSignature_)) { - RING_WARN("hasSignedReceipt() signature check failed"); + RING_WARN("Device receipt signature check failed"); return false; } @@ -836,6 +845,13 @@ RingAccount::loadIdentity() if (crt_id != dht_key.getPublicKey().getId()) return {}; + if (not dht_cert.issuer) { + RING_ERR("Device certificate has no issuer"); + return {}; + } + // load revocation lists for device authority (account certificate). + tls::CertificateStore::instance().loadRevocations(*dht_cert.issuer); + identity_ = { std::make_shared<dht::crypto::PrivateKey>(std::move(dht_key)), std::make_shared<dht::crypto::Certificate>(std::move(dht_cert)) @@ -907,6 +923,8 @@ RingAccount::loadArchive(const std::vector<uint8_t>& dat) c.id.second = std::make_shared<dht::crypto::Certificate>(base64::decode(itr->asString())); } else if (itr.key().asString().compare(Conf::ETH_KEY) == 0) { c.eth_key = base64::decode(itr->asString()); + } else if (itr.key().asString().compare(Conf::RING_ACCOUNT_CRL) == 0) { + c.revoked = std::make_shared<dht::crypto::RevocationList>(base64::decode(itr->asString())); } else c.config[itr.key().asString()] = itr->asString(); } catch (const std::exception& ex) { @@ -950,6 +968,8 @@ RingAccount::makeArchive(const ArchiveContent& archive) const root[Conf::RING_ACCOUNT_KEY] = base64::encode(archive.id.first->serialize()); root[Conf::RING_ACCOUNT_CERT] = base64::encode(archive.id.second->getPacked()); root[Conf::ETH_KEY] = base64::encode(archive.eth_key); + if (archive.revoked) + root[Conf::RING_ACCOUNT_CRL] = base64::encode(archive.revoked->getPacked()); Json::FastWriter fastWriter; std::string output = fastWriter.write(root); @@ -1058,6 +1078,31 @@ RingAccount::addDevice(const std::string& password) }); } +bool +RingAccount::revokeDevice(const std::string& password, const std::string& device) +{ + // shared_ptr of future + auto fa = ThreadPool::instance().getShared<ArchiveContent>( + std::bind(&RingAccount::readArchive, this, password) + ); + findCertificate(dht::InfoHash(device), + [fa,sthis=shared(),password](const std::shared_ptr<dht::crypto::Certificate>& crt) mutable + { + sthis->foundAccountDevice(crt); + auto a = fa->get(); + // Add revoked device to the revocation list and resign it + if (not a.revoked) + a.revoked = std::make_shared<decltype(a.revoked)::element_type>(); + a.revoked->revoke(*crt); + a.revoked->sign(a.id); + // add to CRL cache + tls::CertificateStore::instance().pinRevocationList(a.id.second->getId().toString(), a.revoked); + tls::CertificateStore::instance().loadRevocations(*sthis->identity_.second->issuer); + sthis->saveArchive(a, password); + }); + return true; +} + std::pair<std::string, std::string> RingAccount::saveIdentity(const dht::crypto::Identity id, const std::string& path) const { @@ -1913,12 +1958,23 @@ RingAccount::doRegister_() auto h = dht::InfoHash(ringAccountId_); RING_WARN("Announcing device at %s: %s", h.toString().c_str(), announce_->toString().c_str()); dht_.put(h, announce_, dht::DoneCallback{}, {}, true); + for (const auto& crl : identity_.second->issuer->getRevocationLists()) + dht_.put(h, crl, dht::DoneCallback{}, {}, true); dht_.listen<DeviceAnnouncement>(h, [shared](DeviceAnnouncement&& dev) { shared->findCertificate(dev.dev, [shared](const std::shared_ptr<dht::crypto::Certificate> crt) { shared->foundAccountDevice(crt); }); return true; }); + dht_.listen<dht::crypto::RevocationList>(h, [shared](dht::crypto::RevocationList&& crl) { + if (crl.isSignedBy(*shared->identity_.second->issuer)) { + RING_WARN("Found CRL for account."); + tls::CertificateStore::instance().pinRevocationList( + shared->ringAccountId_, + std::make_shared<dht::crypto::RevocationList>(std::move(crl))); + } + return true; + }); syncDevices(); } else { RING_WARN("Can't announce device: no annoucement..."); @@ -2287,13 +2343,13 @@ RingAccount::connectivityChanged() } bool -RingAccount::findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>)> cb) +RingAccount::findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb) { if (auto cert = tls::CertificateStore::instance().getCertificate(h.toString())) { if (cb) cb(cert); } else { - dht_.findCertificate(h, [=](const std::shared_ptr<dht::crypto::Certificate> crt) { + dht_.findCertificate(h, [cb{std::move(cb)}](const std::shared_ptr<dht::crypto::Certificate> crt) { if (crt) tls::CertificateStore::instance().pinCertificate(std::move(crt)); if (cb) @@ -2804,6 +2860,10 @@ RingAccount::forEachDevice(const dht::InfoHash& to, { auto shared = std::static_pointer_cast<RingAccount>(shared_from_this()); auto treatedDevices = std::make_shared<std::set<dht::InfoHash>>(); + dht_.get<dht::crypto::RevocationList>(to, [to](dht::crypto::RevocationList&& crl){ + tls::CertificateStore().instance().pinRevocationList(to.toString(), std::move(crl)); + return true; + }); dht_.get<DeviceAnnouncement>(to, [shared,to,treatedDevices,op](DeviceAnnouncement&& dev) { if (dev.from != to) return true; diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h index fcadf05f9f..0316799a78 100644 --- a/src/ringdht/ringaccount.h +++ b/src/ringdht/ringaccount.h @@ -84,6 +84,7 @@ constexpr const char* const RING_ACCOUNT_KEY = "ringAccountKey"; constexpr const char* const RING_ACCOUNT_CERT = "ringAccountCert"; constexpr const char* const RING_ACCOUNT_RECEIPT = "ringAccountReceipt"; constexpr const char* const RING_ACCOUNT_RECEIPT_SIG = "ringAccountReceiptSignature"; +constexpr const char* const RING_ACCOUNT_CRL = "ringAccountCRL"; } class IceTransport; @@ -101,6 +102,13 @@ class RingAccount : public SIPAccountBase { return ACCOUNT_TYPE; } + std::shared_ptr<RingAccount> shared() { + return std::static_pointer_cast<RingAccount>(shared_from_this()); + } + std::shared_ptr<RingAccount const> shared() const { + return std::static_pointer_cast<RingAccount const>(shared_from_this()); + } + /** * Constructor * @param accountID The account identifier @@ -275,7 +283,7 @@ class RingAccount : public SIPAccountBase { std::vector<std::string> getCertificatesByStatus(tls::TrustStore::PermissionStatus status); bool findCertificate(const std::string& id); - bool findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>)> cb = {}); + bool findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {}); /* contact requests */ std::map<std::string, std::string> getTrustRequests() const; @@ -287,6 +295,8 @@ class RingAccount : public SIPAccountBase { void addDevice(const std::string& password); + bool revokeDevice(const std::string& password, const std::string& device); + std::map<std::string, std::string> getKnownDevices() const; void connectivityChanged() override; -- GitLab