61 auto sync_date = clock::time_point(clock::duration(sync.date));
70 JAMI_DEBUG(
"[Account {}] [Contacts] received device sync data ({:d} devices, {:d} contacts, {:d} requests)",
72 sync.devices_known.size() + sync.devices.size(),
74 sync.trust_requests.size());
75 for (
const auto&
d : sync.devices_known) {
80 foundAccountDevice(crt, d.second);
83 for (
const auto&
d : sync.devices) {
85 [
this,
d](
const std::shared_ptr<dht::crypto::Certificate>&
crt) {
86 if (not crt || crt->getLongId() != d.first)
89 foundAccountDevice(crt, d.second.name);
95 if (!sync.peers.empty()) {
96 for (
const auto &peer: sync.peers) {
97 info_->contacts->updateContact(peer.first, peer.second);
99 info_->contacts->saveContacts();
103 for (
const auto&
tr : sync.trust_requests)
104 info_->contacts->onTrustRequest(
tr.first,
108 tr.second.conversationId,
113AccountManager::loadIdentity(
const std::string& crt_path,
114 const std::string& key_path,
115 const std::string& key_pwd)
const
119 if (crt_path.empty() or key_path.empty())
122 JAMI_DEBUG(
"[Account {}] [Auth] Loading certificate from '{}' and key from '{}' at {}",
128 dht::crypto::Certificate dht_cert(fileutils::loadFile(crt_path, path_));
129 dht::crypto::PrivateKey dht_key(fileutils::loadFile(key_path, path_), key_pwd);
130 auto crt_id = dht_cert.getLongId();
131 if (!crt_id or crt_id != dht_key.getPublicKey().getLongId()) {
132 JAMI_ERROR(
"[Account {}] [Auth] Device certificate not matching public key!", accountId_);
135 auto& issuer = dht_cert.issuer;
137 JAMI_ERROR(
"[Account {}] [Auth] Device certificate {:s} has no issuer", accountId_, dht_cert.getId().to_view());
141 Manager::instance().certStore(accountId_).loadRevocations(*issuer);
143 return {std::make_shared<dht::crypto::PrivateKey>(std::move(dht_key)),
144 std::make_shared<dht::crypto::Certificate>(std::move(dht_cert))};
145 }
catch (
const std::exception& e) {
146 JAMI_ERROR(
"[Account {}] [Auth] Error loading identity: {}", accountId_, e.what());
152AccountManager::parseAnnounce(
const std::string& announceBase64,
153 const std::string& accountId,
154 const std::string& deviceSha1,
155 const std::string& deviceSha256)
157 auto announce_val = std::make_shared<dht::Value>();
159 auto announce = base64::decode(announceBase64);
160 msgpack::object_handle announce_msg = msgpack::unpack((
const char*) announce.data(),
162 announce_val->msgpack_unpack(announce_msg.get());
163 if (not announce_val->checkSignature()) {
164 JAMI_ERROR(
"[Auth] announce signature check failed");
168 da.unpackValue(*announce_val);
169 if (da.from.toString() != accountId) {
170 JAMI_ERROR(
"[Auth] Account ID mismatch in announce (account: {}, in announce: {})", accountId, da.from.toString());
173 if ((da.
pk && da.
pk->getLongId().to_view() != deviceSha256) || da.
dev.toString() != deviceSha1) {
174 JAMI_ERROR(
"[Auth] Device ID mismatch in announce (device: {}, in announce: {})", da.
pk ? deviceSha256 : deviceSha1, da.
pk ? da.
pk->getLongId().to_view() : da.
dev.toString());
177 }
catch (
const std::exception& e) {
178 JAMI_ERROR(
"[Auth] unable to read announce: {}", e.what());
185AccountManager::useIdentity(
const dht::crypto::Identity& identity,
186 const std::string& receipt,
187 const std::vector<uint8_t>& receiptSignature,
188 const std::string& username,
191 if (receipt.empty() or receiptSignature.empty())
194 if (not identity.first or not identity.second) {
195 JAMI_ERROR(
"[Account {}] [Auth] no identity provided", accountId_);
199 auto accountCertificate = identity.second->issuer;
200 if (not accountCertificate) {
201 JAMI_ERROR(
"[Account {}] [Auth] device certificate must be issued by the account certificate", accountId_);
206 auto contactList = std::make_unique<ContactList>(accountId_, accountCertificate, path_, onChange);
207 auto result = contactList->isValidAccountDevice(*identity.second);
209 JAMI_ERROR(
"[Account {}] [Auth] unable to use identity: device certificate chain is unable to be verified: {}", accountId_,
214 auto pk = accountCertificate->getSharedPublicKey();
215 JAMI_LOG(
"[Account {}] [Auth] checking device receipt for account:{} device:{}", accountId_, pk->getId().toString(), identity.second->getLongId().toString());
216 if (!pk->checkSignature({receipt.begin(), receipt.end()}, receiptSignature)) {
217 JAMI_ERROR(
"[Account {}] [Auth] device receipt signature check failed", accountId_);
222 if (!json::parse(receipt, root) || !root.isMember(
"announce")) {
223 JAMI_ERROR(
"[Account {}] [Auth] device receipt parsing error", accountId_);
227 auto dev_id = root[
"dev"].asString();
228 if (dev_id != identity.second->getId().toString()) {
229 JAMI_ERROR(
"[Account {}] [Auth] device ID mismatch between receipt and certificate", accountId_);
232 auto id = root[
"id"].asString();
233 if (
id != pk->getId().toString()) {
234 JAMI_ERROR(
"[Account {}] [Auth] account ID mismatch between receipt and certificate", accountId_);
238 auto devicePk = identity.first->getSharedPublicKey();
240 JAMI_ERROR(
"[Account {}] [Auth] No device pk found", accountId_);
244 auto announce = parseAnnounce(root[
"announce"].asString(),
id, devicePk->getId().toString(), devicePk->getLongId().toString());
249 onChange_ = std::move(onChange);
251 auto info = std::make_unique<AccountInfo>();
252 info->identity = identity;
253 info->contacts = std::move(contactList);
254 info->contacts->load();
255 info->accountId = id;
256 info->devicePk = std::move(devicePk);
257 info->deviceId = info->devicePk->getLongId().toString();
258 info->announce = std::move(announce);
259 info->ethAccount = root[
"eth"].asString();
260 info->username = username;
261 info_ = std::move(info);
263 JAMI_LOG(
"[Account {}] [Auth] Device {} receipt checked successfully for user {}", accountId_,
264 info_->deviceId,
id);
280 if (info_->announce) {
281 auto h = dht::InfoHash(info_->accountId);
282 if (publishPresence) {
286 [dcb = std::move(dcb), h, accountId=accountId_](
bool ok) {
288 JAMI_DEBUG(
"[Account {}] device announced at {}", accountId, h.toString());
297 for (
const auto& crl : info_->identity.second->issuer->getRevocationLists())
298 dht_->put(h, crl, dht::DoneCallback {}, {},
true);
300 findCertificate(
dev.dev,
301 [
this, cb](
const std::shared_ptr<dht::crypto::Certificate>& crt) {
302 foundAccountDevice(crt);
308 dht_->listen<dht::crypto::RevocationList>(h, [
this](dht::crypto::RevocationList&& crl) {
309 if (crl.isSignedBy(*info_->identity.second->issuer)) {
310 JAMI_DEBUG(
"[Account {}] Found CRL for account.", accountId_);
312 .pinRevocationList(info_->accountId,
313 std::make_shared<dht::crypto::RevocationList>(
320 JAMI_ERROR(
"[Account {}] Unable to announce device: no announcement.", accountId_);
323 auto inboxKey = dht::InfoHash::get(
"inbox:" + info_->devicePk->getId().toString());
324 dht_->listen<dht::TrustRequest>(inboxKey, [
this](dht::TrustRequest&& v) {
325 if (v.service != DHT_TYPE_NS)
332 [
this, v](
const std::shared_ptr<dht::crypto::Certificate>&,
333 dht::InfoHash peer_account)
mutable {
334 JAMI_WARNING(
"[Account {}] Got trust request (confirm: {}) from: {} / {}. ConversationId: {}",
337 peer_account.toString(),
341 if (info_->contacts->onTrustRequest(peer_account,
346 std::move(v.payload))) {
349 auto conversationId = v.conversationId;
351 if (auto details = info_->contacts->getContactInfo(peer_account)) {
352 if (!details->conversationId.empty()) {
353 if (details->conversationId == conversationId) {
358 info_->contacts->acceptConversation(conversationId, v.owner->getLongId().toString());
361 conversationId = details->conversationId;
362 JAMI_WARNING(
"Accept with old convId: {}", conversationId);
365 sendTrustRequestConfirm(peer_account, conversationId);
402AccountManager::foundPeerDevice(
const std::string& accountId,
const std::shared_ptr<dht::crypto::Certificate>& crt,
403 dht::InfoHash& peer_id)
408 auto top_issuer = crt;
409 while (top_issuer->issuer)
410 top_issuer = top_issuer->issuer;
413 if (top_issuer == crt) {
414 JAMI_WARNING(
"[Account {}] Found invalid peer device: {}", accountId, crt->getLongId().toString());
420 dht::crypto::TrustList peer_trust;
421 peer_trust.add(*top_issuer);
422 if (not peer_trust.verify(*crt)) {
423 JAMI_WARNING(
"[Account {}] Found invalid peer device: {}", accountId, crt->getLongId().toString());
428 if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
429 JAMI_WARNING(
"[Account {}] Certificate {} is disabled by cached OCSP response", accountId, crt->getLongId());
433 peer_id = crt->issuer->getId();
434 JAMI_LOG(
"[Account {}] Found peer device: {} account:{} CA:{}",
436 crt->getLongId().toString(),
438 top_issuer->getId().toString());
443AccountManager::onPeerMessage(
const dht::crypto::PublicKey& peer_device,
445 std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>& crt,
446 const dht::InfoHash& peer_account)>&& cb)
449 auto trustStatus = getCertificateStatus(peer_device.toString());
450 if (trustStatus == dhtnet::tls::TrustStore::PermissionStatus::BANNED) {
451 JAMI_WARNING(
"[Account {}] [Auth] Discarding message from banned device {}", accountId_, peer_device.toString());
455 findCertificate(peer_device.getId(),
456 [
this, cb = std::move(cb), allowPublic](
457 const std::shared_ptr<dht::crypto::Certificate>& cert) {
458 dht::InfoHash peer_account_id;
459 if (onPeerCertificate(cert, allowPublic, peer_account_id)) {
460 cb(cert, peer_account_id);
466AccountManager::onPeerCertificate(
const std::shared_ptr<dht::crypto::Certificate>& cert,
468 dht::InfoHash& account_id)
470 dht::InfoHash peer_account_id;
471 if (not foundPeerDevice(accountId_, cert, peer_account_id)) {
472 JAMI_WARNING(
"[Account {}] [Auth] Discarding message from invalid peer certificate", accountId_);
476 if (not isAllowed(*cert, allowPublic)) {
477 JAMI_WARNING(
"[Account {}] [Auth] Discarding message from unauthorized peer {}.",
478 accountId_, peer_account_id.toString());
482 account_id = peer_account_id;
487AccountManager::addContact(
const dht::InfoHash& uri,
bool confirmed,
const std::string& conversationId)
490 JAMI_ERROR(
"addContact(): account not loaded");
493 JAMI_WARNING(
"[Account {}] addContact {}", accountId_, confirmed);
494 if (info_->contacts->addContact(uri, confirmed, conversationId)) {
556AccountManager::getContacts(
bool includeRemoved)
const
559 JAMI_ERROR(
"[Account {}] getContacts(): account not loaded", accountId_);
562 const auto& contacts = info_->contacts->getContacts();
563 std::vector<std::map<std::string, std::string>> ret;
564 ret.reserve(contacts.size());
566 for (
const auto& c : contacts) {
567 if (!c.second.isActive() && !includeRemoved && !c.second.isBanned())
569 auto details = c.second.toMap();
570 if (not details.empty()) {
571 details[
"id"] = c.first.toString();
572 ret.emplace_back(std::move(details));
610AccountManager::findCertificate(
611 const dht::InfoHash& h,
612 std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
614 if (
auto cert = certStore().getCertificate(h.toString())) {
618 dht_->findCertificate(h,
619 [cb = std::move(cb),
this](
620 const std::shared_ptr<dht::crypto::Certificate>& crt) {
622 certStore().pinCertificate(crt);
655AccountManager::setCertificateStatus(
const std::shared_ptr<crypto::Certificate>& cert,
656 dhtnet::tls::TrustStore::PermissionStatus status,
659 return info_ and info_->contacts->setCertificateStatus(cert, status, local);
718AccountManager::sendTrustRequest(
const std::string& to,
719 const std::string& convId,
720 const std::vector<uint8_t>& payload)
722 JAMI_WARNING(
"[Account {}] AccountManager::sendTrustRequest", accountId_);
723 auto toH = dht::InfoHash(to);
725 JAMI_ERROR(
"[Account {}] Unable to send trust request to invalid hash: {}", accountId_, to);
729 JAMI_ERROR(
"[Account {}] sendTrustRequest(): account not loaded", accountId_);
732 if (info_->contacts->addContact(toH,
false, convId)) {
736 [
this, toH, convId, payload](
const std::shared_ptr<dht::crypto::PublicKey>&
dev) {
737 auto to = toH.toString();
738 JAMI_WARNING(
"[Account {}] Sending trust request to: {:s} / {:s} of size {:d}",
739 accountId_, to,
dev->getLongId(), payload.size());
740 dht_->putEncrypted(dht::InfoHash::get(
"inbox:" +
dev->getId().toString()),
742 dht::TrustRequest(DHT_TYPE_NS, convId, payload),
743 [to, size = payload.size()](
bool ok) {
745 JAMI_ERROR(
"Tried to send request {:s} (size: "
746 "{:d}), but put failed",
754AccountManager::sendTrustRequestConfirm(
const dht::InfoHash& toH,
const std::string& convId)
756 JAMI_WARNING(
"[Account {}] AccountManager::sendTrustRequestConfirm to {} (conversation {})", accountId_, toH, convId);
757 dht::TrustRequest answer {DHT_TYPE_NS, convId};
758 answer.confirm =
true;
760 if (!convId.empty() && info_)
761 info_->contacts->acceptConversation(convId);
763 forEachDevice(toH, [
this, toH, answer](
const std::shared_ptr<dht::crypto::PublicKey>&
dev) {
764 JAMI_WARNING(
"[Account {}] sending trust request reply: {} / {}",
765 accountId_, toH,
dev->getLongId());
766 dht_->putEncrypted(dht::InfoHash::get(
"inbox:" +
dev->getId().toString()),
dev, answer);
771AccountManager::forEachDevice(
772 const dht::InfoHash& to,
773 std::function<
void(
const std::shared_ptr<dht::crypto::PublicKey>&)>&& op,
774 std::function<
void(
bool)>&& end)
777 JAMI_ERROR(
"[Account {}] forEachDevice: no dht", accountId_);
782 dht_->get<dht::crypto::RevocationList>(to, [to,
this](dht::crypto::RevocationList&& crl) {
783 certStore().pinRevocationList(to.toString(), std::move(crl));
789 const dht::InfoHash to;
790 const std::string accountId;
792 unsigned remaining {1};
793 std::set<dht::PkId> treatedDevices {};
794 std::function<void(
const std::shared_ptr<dht::crypto::PublicKey>&)> onDevice;
795 std::function<void(
bool)> onEnd;
797 State(dht::InfoHash to, std::string accountId)
798 : to(std::move(to)), accountId(std::move(accountId)) {}
800 void found(
const std::shared_ptr<dht::crypto::PublicKey>& pk)
804 auto longId = pk->getLongId();
805 if (treatedDevices.emplace(longId).second) {
814 if (remaining == 0 && onEnd) {
815 JAMI_LOG(
"[Account {}] Found {:d} device(s) for {}", accountId, treatedDevices.size(), to);
816 onEnd(not treatedDevices.empty());
822 auto state = std::make_shared<State>(to, accountId_);
823 state->onDevice = std::move(op);
824 state->onEnd = std::move(end);
832 findCertificate(
dev.dev, [state](
const std::shared_ptr<dht::crypto::Certificate>& cert) {
833 state->found(cert ? cert->getSharedPublicKey()
834 : std::shared_ptr<dht::crypto::PublicKey> {});
838 [state](
bool ) { state->found({}); });