62 auto sync_date = clock::time_point(clock::duration(sync.date));
71 JAMI_DEBUG(
"[Account {}] [Contacts] received device sync data ({:d} devices, {:d} contacts, {:d} requests)",
75 sync.trust_requests.size());
76 for (
const auto&
d : sync.devices) {
81 foundAccountDevice(crt, d.second.name);
87 if (!sync.peers.empty()) {
88 for (
const auto& peer : sync.peers) {
89 info_->contacts->updateContact(peer.first, peer.second);
91 info_->contacts->saveContacts();
95 for (
const auto&
tr : sync.trust_requests)
97 ->onTrustRequest(
tr.first,
tr.second.device,
tr.second.received,
false,
tr.second.conversationId, {});
101AccountManager::loadIdentity(
const std::string& crt_path,
const std::string& key_path,
const std::string& key_pwd)
const
105 if (crt_path.empty() or key_path.empty())
108 JAMI_DEBUG(
"[Account {}] [Auth] Loading certificate from '{}' and key from '{}' at {}",
114 dht::crypto::Certificate dht_cert(fileutils::loadFile(crt_path, path_));
115 dht::crypto::PrivateKey dht_key(fileutils::loadFile(key_path, path_), key_pwd);
116 auto crt_id = dht_cert.getLongId();
117 if (!crt_id or crt_id != dht_key.getPublicKey().getLongId()) {
118 JAMI_ERROR(
"[Account {}] [Auth] Device certificate not matching public key!", accountId_);
121 auto& issuer = dht_cert.issuer;
123 JAMI_ERROR(
"[Account {}] [Auth] Device certificate {:s} has no issuer",
125 dht_cert.getId().to_view());
129 Manager::instance().certStore(accountId_).loadRevocations(*issuer);
131 return {std::make_shared<dht::crypto::PrivateKey>(std::move(dht_key)),
132 std::make_shared<dht::crypto::Certificate>(std::move(dht_cert))};
133 }
catch (
const std::exception& e) {
134 JAMI_ERROR(
"[Account {}] [Auth] Error loading identity: {}", accountId_, e.what());
140AccountManager::parseAnnounce(
const std::string& announceBase64,
141 const std::string& accountId,
142 const std::string& deviceSha1,
143 const std::string& deviceSha256)
145 auto announce_val = std::make_shared<dht::Value>();
147 auto announce = base64::decode(announceBase64);
148 msgpack::object_handle announce_msg = msgpack::unpack((
const char*) announce.data(), announce.size());
149 announce_val->msgpack_unpack(announce_msg.get());
150 if (not announce_val->checkSignature()) {
151 JAMI_ERROR(
"[Auth] announce signature check failed");
155 da.unpackValue(*announce_val);
156 if (da.from.toString() != accountId) {
157 JAMI_ERROR(
"[Auth] Account ID mismatch in announce (account: {}, in announce: {})",
162 if ((da.
pk && da.
pk->getLongId().to_view() != deviceSha256) || da.
dev.toString() != deviceSha1) {
163 JAMI_ERROR(
"[Auth] Device ID mismatch in announce (device: {}, in announce: {})",
164 da.
pk ? deviceSha256 : deviceSha1,
165 da.
pk ? da.
pk->getLongId().to_view() : da.
dev.toString());
168 }
catch (
const std::exception& e) {
169 JAMI_ERROR(
"[Auth] unable to read announce: {}", e.what());
176AccountManager::useIdentity(
const dht::crypto::Identity& identity,
177 const std::string& receipt,
178 const std::vector<uint8_t>& receiptSignature,
179 const std::string& username,
182 if (receipt.empty() or receiptSignature.empty())
185 if (not identity.first or not identity.second) {
186 JAMI_ERROR(
"[Account {}] [Auth] no identity provided", accountId_);
190 auto accountCertificate = identity.second->issuer;
191 if (not accountCertificate) {
192 JAMI_ERROR(
"[Account {}] [Auth] device certificate must be issued by the account certificate", accountId_);
197 auto contactList = std::make_unique<ContactList>(accountId_, accountCertificate, path_, onChange);
198 auto result = contactList->isValidAccountDevice(*identity.second);
200 JAMI_ERROR(
"[Account {}] [Auth] unable to use identity: device certificate chain is unable to be verified: {}",
206 auto pk = accountCertificate->getSharedPublicKey();
207 JAMI_LOG(
"[Account {}] [Auth] checking device receipt for account:{} device:{}",
209 pk->getId().toString(),
210 identity.second->getLongId().toString());
211 if (!pk->checkSignature({receipt.begin(), receipt.end()}, receiptSignature)) {
212 JAMI_ERROR(
"[Account {}] [Auth] device receipt signature check failed", accountId_);
217 if (!json::parse(receipt, root) || !root.isMember(
"announce")) {
218 JAMI_ERROR(
"[Account {}] [Auth] device receipt parsing error", accountId_);
222 auto dev_id = root[
"dev"].asString();
223 if (dev_id != identity.second->getId().toString()) {
224 JAMI_ERROR(
"[Account {}] [Auth] device ID mismatch between receipt and certificate", accountId_);
227 auto id = root[
"id"].asString();
228 if (
id != pk->getId().toString()) {
229 JAMI_ERROR(
"[Account {}] [Auth] account ID mismatch between receipt and certificate", accountId_);
233 auto devicePk = identity.first->getSharedPublicKey();
235 JAMI_ERROR(
"[Account {}] [Auth] No device pk found", accountId_);
239 auto announce = parseAnnounce(root[
"announce"].asString(),
241 devicePk->getId().toString(),
242 devicePk->getLongId().toString());
247 onChange_ = std::move(onChange);
249 auto info = std::make_unique<AccountInfo>();
250 info->identity = identity;
251 info->contacts = std::move(contactList);
252 info->contacts->load();
253 info->accountId = id;
254 info->devicePk = std::move(devicePk);
255 info->deviceId = info->devicePk->getLongId().toString();
256 info->announce = std::move(announce);
257 info->ethAccount = root[
"eth"].asString();
258 info->username = username;
259 info_ = std::move(info);
261 JAMI_LOG(
"[Account {}] [Auth] Device {} receipt checked successfully for user {}", accountId_, info_->deviceId,
id);
277 if (info_->announce) {
278 auto h = dht::InfoHash(info_->accountId);
279 if (publishPresence) {
283 [dcb = std::move(dcb), h, accountId = accountId_](
bool ok) {
285 JAMI_DEBUG(
"[Account {}] device announced at {}", accountId, h.toString());
294 for (
const auto& crl : info_->identity.second->issuer->getRevocationLists())
295 dht_->put(h, crl, dht::DoneCallback {}, {},
true);
298 findCertificate(
dev.pk->getLongId(), [
this, cb](
const std::shared_ptr<dht::crypto::Certificate>& crt) {
299 foundAccountDevice(crt);
304 findCertificate(
dev.dev, [
this, cb](
const std::shared_ptr<dht::crypto::Certificate>& crt) {
305 foundAccountDevice(crt);
312 dht_->listen<dht::crypto::RevocationList>(h, [
this](dht::crypto::RevocationList&& crl) {
313 if (crl.isSignedBy(*info_->identity.second->issuer)) {
314 JAMI_DEBUG(
"[Account {}] Found CRL for account.", accountId_);
315 certStore().pinRevocationList(info_->accountId,
316 std::make_shared<dht::crypto::RevocationList>(std::move(crl)));
322 JAMI_ERROR(
"[Account {}] Unable to announce device: no announcement.", accountId_);
325 auto inboxKey = dht::InfoHash::get(
"inbox:" + info_->devicePk->getId().toString());
326 dht_->listen<dht::TrustRequest>(inboxKey, [
this](dht::TrustRequest&& v) {
327 if (v.service != DHT_TYPE_NS)
334 [
this, v](
const std::shared_ptr<dht::crypto::Certificate>&, dht::InfoHash peer_account)
mutable {
335 JAMI_WARNING(
"[Account {}] [device {}] Got trust request (confirm: {}) from: {}. ConversationId: {}",
337 v.owner->getLongId().toString(),
339 peer_account.toString(),
342 if (info_->contacts->onTrustRequest(peer_account,
347 std::move(v.payload))) {
350 auto conversationId = v.conversationId;
352 if (auto details = info_->contacts->getContactInfo(peer_account)) {
353 if (!details->conversationId.empty()) {
354 if (details->conversationId == conversationId) {
359 info_->contacts->acceptConversation(conversationId, v.owner->getLongId().toString());
362 conversationId = details->conversationId;
363 JAMI_WARNING(
"Accept with old convId: {}", conversationId);
366 sendTrustRequestConfirm(peer_account, conversationId);
403AccountManager::foundPeerDevice(
const std::string& accountId,
404 const std::shared_ptr<dht::crypto::Certificate>& crt,
405 dht::InfoHash& peer_id)
410 auto top_issuer = crt;
411 while (top_issuer->issuer)
412 top_issuer = top_issuer->issuer;
415 if (top_issuer == crt) {
416 JAMI_WARNING(
"[Account {}] Found invalid peer device: {}", accountId, crt->getLongId().toString());
422 dht::crypto::TrustList peer_trust;
423 peer_trust.add(*top_issuer);
424 if (not peer_trust.verify(*crt)) {
425 JAMI_WARNING(
"[Account {}] Found invalid peer device: {}", accountId, crt->getLongId().toString());
430 if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
431 JAMI_WARNING(
"[Account {}] Certificate {} is disabled by cached OCSP response", accountId, crt->getLongId());
435 peer_id = crt->issuer->getId();
436 JAMI_LOG(
"[Account {}] [device {}] Found device for peer: {} CA:{}",
438 crt->getLongId().toString(),
440 top_issuer->getId().toString());
445AccountManager::onPeerMessage(
446 const dht::crypto::PublicKey& peer_device,
448 std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>& crt,
const dht::InfoHash& peer_account)>&& cb)
451 auto trustStatus = getCertificateStatus(peer_device.getLongId().toString());
452 if (trustStatus == dhtnet::tls::TrustStore::PermissionStatus::BANNED) {
453 JAMI_WARNING(
"[Account {}] [Auth] Discarding message from banned device {}",
455 peer_device.getLongId().to_view());
459 findCertificate(peer_device.getLongId(),
460 [
this, cb = std::move(cb), allowPublic](
const std::shared_ptr<dht::crypto::Certificate>& cert) {
461 dht::InfoHash peer_account_id;
462 if (onPeerCertificate(cert, allowPublic, peer_account_id)) {
463 cb(cert, peer_account_id);
469AccountManager::onPeerCertificate(
const std::shared_ptr<dht::crypto::Certificate>& cert,
471 dht::InfoHash& account_id)
473 dht::InfoHash peer_account_id;
474 if (not foundPeerDevice(accountId_, cert, peer_account_id)) {
475 JAMI_WARNING(
"[Account {}] [Auth] Discarding message from invalid peer certificate", accountId_);
479 if (not isAllowed(*cert, allowPublic)) {
480 JAMI_WARNING(
"[Account {}] [Auth] Discarding message from unauthorized peer {}.",
482 peer_account_id.toString());
486 account_id = peer_account_id;
491AccountManager::addContact(
const dht::InfoHash& uri,
bool confirmed,
const std::string& conversationId)
494 JAMI_ERROR(
"addContact(): account not loaded");
497 JAMI_WARNING(
"[Account {}] addContact {}", accountId_, confirmed);
498 if (info_->contacts->addContact(uri, confirmed, conversationId)) {
538AccountManager::updateContactConversation(
const std::string& uri,
const std::string& convId,
bool added)
540 dht::InfoHash h(uri);
542 JAMI_ERROR(
"[Account {}] updateContactConversation: invalid contact URI: {}", accountId_, uri);
546 JAMI_ERROR(
"[Account {}] updateContactConversation: account not loaded", accountId_);
549 if (info_->contacts->updateConversation(h, convId, added)) {
551 auto req = info_->contacts->getTrustRequest(h);
553 if (convIt != req.end() && convIt->second == convId) {
554 discardTrustRequest(uri);
561AccountManager::getContacts(
bool includeRemoved)
const
564 JAMI_ERROR(
"[Account {}] getContacts(): account not loaded", accountId_);
567 const auto& contacts = info_->contacts->getContacts();
568 std::map<dht::InfoHash, Contact> ret;
569 for (
const auto& c : contacts) {
570 if (!c.second.isActive() && !includeRemoved && !c.second.isBanned())
572 ret.emplace(c.first, c.second);
609AccountManager::findCertificate(
const dht::InfoHash& h,
610 std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
612 if (
auto cert = certStore().getCertificate(h.toString())) {
616 dht_->findCertificate(h, [cb = std::move(cb),
this](
const std::shared_ptr<dht::crypto::Certificate>& crt) {
618 certStore().pinCertificate(crt);
629AccountManager::findCertificate(
const dht::PkId&
id,
630 std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
632 if (
auto cert = certStore().getCertificate(
id.toString())) {
636 dht_->findCertificate(
id, [cb = std::move(cb),
this](
const std::shared_ptr<dht::crypto::Certificate>& crt) {
638 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);
715AccountManager::sendTrustRequest(
const std::string& to,
const std::string& convId,
const std::vector<uint8_t>& payload)
717 JAMI_WARNING(
"[Account {}] AccountManager::sendTrustRequest", accountId_);
718 auto toH = dht::InfoHash(to);
720 JAMI_ERROR(
"[Account {}] Unable to send trust request to invalid hash: {}", accountId_, to);
724 JAMI_ERROR(
"[Account {}] sendTrustRequest(): account not loaded", accountId_);
727 if (info_->contacts->addContact(toH,
false, convId)) {
730 forEachDevice(toH, [
this, toH, convId, payload](
const std::shared_ptr<dht::crypto::PublicKey>&
dev) {
731 auto to = toH.toString();
732 JAMI_WARNING(
"[Account {}] [device {}] Sending trust request (size {:d}) to: {:s}",
737 dht_->putEncrypted(dht::InfoHash::get(
concat(
"inbox:"sv,
dev->getId().to_view())),
739 dht::TrustRequest(DHT_TYPE_NS, convId, payload),
740 [to, size = payload.size()](
bool ok) {
742 JAMI_ERROR(
"Tried to send request {:s} (size: "
743 "{:d}), but put failed",
751AccountManager::sendTrustRequestConfirm(
const dht::InfoHash& toH,
const std::string& convId)
753 JAMI_WARNING(
"[Account {}] AccountManager::sendTrustRequestConfirm to {} (conversation {})",
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: {} / {}", accountId_, toH,
dev->getLongId());
765 dht_->putEncrypted(dht::InfoHash::get(
"inbox:" +
dev->getId().toString()),
dev, answer);
770AccountManager::forEachDevice(
const dht::InfoHash& to,
771 std::function<
void(
const std::shared_ptr<dht::crypto::PublicKey>&)>&& op,
772 std::function<
void(
bool)>&& end)
775 JAMI_ERROR(
"[Account {}] forEachDevice: no dht", accountId_);
780 dht_->get<dht::crypto::RevocationList>(to, [to,
this](dht::crypto::RevocationList&& crl) {
781 certStore().pinRevocationList(to.toString(), std::move(crl));
787 const dht::InfoHash to;
788 const std::string accountId;
790 unsigned remaining {1};
791 std::set<dht::PkId> treatedDevices {};
792 std::function<void(
const std::shared_ptr<dht::crypto::PublicKey>&)> onDevice;
793 std::function<void(
bool)> onEnd;
795 State(dht::InfoHash to, std::string accountId)
797 , 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);
833 findCertificate(
dev.pk->getLongId(), [state](
const std::shared_ptr<dht::crypto::Certificate>& cert) {
834 state->found(cert ? cert->getSharedPublicKey() : std::shared_ptr<dht::crypto::PublicKey> {});
837 findCertificate(
dev.dev, [state](
const std::shared_ptr<dht::crypto::Certificate>& cert) {
838 state->found(cert ? cert->getSharedPublicKey() : std::shared_ptr<dht::crypto::PublicKey> {});
843 [state](
bool ) { state->found({}); });