30#include <dhtnet/multiplexed_socket.h>
31#include <opendht/dhtrunner.h>
32#include <opendht/thread_pool.h>
48 std::string deviceName,
49 std::unique_ptr<AccountCredentials> credentials,
54 JAMI_WARNING(
"[Account {}] [Auth] starting authentication with scheme '{}'",
57 auto ctx = std::make_shared<AuthContext>();
61 ctx->deviceName = std::move(deviceName);
63 ctx->onSuccess = std::move(onSuccess);
64 ctx->onFailure = std::move(onFailure);
66 if (
not ctx->credentials) {
72 if (
ctx->credentials->scheme ==
"p2p") {
73 JAMI_DEBUG(
"[LinkDevice] Importing account via p2p scheme.");
74 startLoadArchiveFromDevice(
ctx);
78 dht::ThreadPool::computation().run([
ctx = std::move(
ctx),
wthis = weak()] {
83 if (
ctx->credentials->scheme ==
"file") {
85 this_->loadFromFile(*ctx);
88 bool hasArchive = not ctx->credentials->uri.empty()
89 and std::filesystem::is_regular_file(ctx->credentials->uri);
92 if (ctx->credentials->updateIdentity.first
93 and ctx->credentials->updateIdentity.second
94 and needsMigration(this_->accountId_, ctx->credentials->updateIdentity)) {
95 this_->migrateAccount(*ctx);
97 this_->loadFromFile(*ctx);
99 }
else if (
ctx->credentials->updateIdentity.first
100 and ctx->credentials->updateIdentity.second) {
101 auto future_keypair = dht::ThreadPool::computation().get<dev::KeyPair>(
102 &dev::KeyPair::create);
104 JAMI_WARNING(
"[Account {}] [Auth] Converting certificate from old account {}",
106 ctx->credentials->updateIdentity.first->getPublicKey()
109 a.id = std::move(ctx->credentials->updateIdentity);
111 a.ca_key = std::make_shared<dht::crypto::PrivateKey>(
112 fileutils::loadFile(
"ca.key", this_->path_));
115 this_->updateCertificates(
a,
ctx->credentials->updateIdentity);
117 this_->onArchiveLoaded(*
ctx, std::move(
a),
false);
122 }
catch (
const std::exception&
e) {
123 ctx->onFailure(AuthError::UNKNOWN,
e.what());
129ArchiveAccountManager::updateCertificates(AccountArchive& archive, dht::crypto::Identity& device)
131 JAMI_WARNING(
"[Account {}] [Auth] Updating certificates", accountId_);
132 using Certificate = dht::crypto::Certificate;
135 if (not archive.id.first or not *archive.id.first or not archive.id.second or not archive.ca_key
136 or not *archive.ca_key)
140 bool updated =
false;
142 auto& cert = archive.id.second;
143 auto ca = cert->issuer;
145 if (not ca or (not ca->issuer and (not ca->isCA() or ca->getExpiration() < clock::now()))) {
146 ca = std::make_shared<Certificate>(
147 Certificate::generate(*archive.ca_key,
"Jami CA", {},
true));
149 JAMI_LOG(
"[Account {}] [Auth] CA certificate re-generated", accountId_);
153 if (updated or not cert->isCA() or cert->getExpiration() < clock::now()) {
154 cert = std::make_shared<Certificate>(
155 Certificate::generate(*archive.id.first,
157 dht::crypto::Identity {archive.ca_key, ca},
160 JAMI_LOG(
"[Account {}] [Auth] Account certificate for {} re-generated",
165 if (updated and device.first and *device.first) {
167 device.second = std::make_shared<Certificate>(
168 Certificate::generate(*device.first,
"Jami device", archive.id));
169 JAMI_LOG(
"[Account {}] [Auth] Device certificate re-generated", accountId_);
176ArchiveAccountManager::setValidity(std::string_view scheme,
177 const std::string& password,
178 dht::crypto::Identity& device,
179 const dht::InfoHash&
id,
182 auto archive = readArchive(scheme, password);
184 if (not archive.id.first or not *archive.id.first or not archive.id.second or not archive.ca_key
185 or not *archive.ca_key)
188 auto updated =
false;
191 JAMI_WARNING(
"[Account {}] [Auth] Updating validity for certificate with id: {}",
195 JAMI_WARNING(
"[Account {}] [Auth] Updating validity for certificates", accountId_);
197 auto& cert = archive.id.second;
198 auto ca = cert->issuer;
204 if (not
id or ca->getId() ==
id) {
205 ca->setValidity(*archive.ca_key, validity);
207 JAMI_LOG(
"[Account {}] [Auth] CA certificate re-generated", accountId_);
211 if (updated or not
id or cert->getId() ==
id) {
212 cert->setValidity(dht::crypto::Identity {archive.ca_key, ca}, validity);
213 device.second->issuer = cert;
215 JAMI_LOG(
"[Account {}] [Auth] Jami certificate re-generated", accountId_);
219 archive.save(fileutils::getFullPath(path_, archivePath_), scheme, password);
222 if (updated or not
id or device.second->getId() ==
id) {
224 device.second->setValidity(archive.id, validity);
232ArchiveAccountManager::createAccount(AuthContext&
ctx)
235 auto ca = dht::crypto::generateIdentity(
"Jami CA");
236 if (!ca.first || !ca.second) {
237 throw std::runtime_error(
"Unable to generate CA for this account.");
239 a.
id = dht::crypto::generateIdentity(
"Jami", ca, 4096,
true);
240 if (!a.
id.first || !a.
id.second) {
241 throw std::runtime_error(
"Unable to generate identity for this account.");
243 JAMI_WARNING(
"[Account {}] [Auth] New account: CA: {}, ID: {}",
246 a.
id.second->getId());
249 a.
eth_key = keypair.secret().makeInsecure().asBytes();
250 onArchiveLoaded(ctx, std::move(a),
false);
254ArchiveAccountManager::loadFromFile(AuthContext& ctx)
256 JAMI_WARNING(
"[Account {}] [Auth] Loading archive from: {}",
258 ctx.credentials->uri.c_str());
259 AccountArchive archive;
261 archive = AccountArchive(ctx.credentials->uri,
262 ctx.credentials->password_scheme,
263 ctx.credentials->password);
264 }
catch (
const std::exception& ex) {
265 JAMI_WARNING(
"[Account {}] [Auth] Unable to read archive file: {}", accountId_, ex.what());
266 ctx.onFailure(AuthError::INVALID_ARGUMENTS, ex.what());
269 onArchiveLoaded(ctx, std::move(archive),
false);
276 std::pair<bool, bool> stateOld {
false,
true};
277 std::pair<bool, bool> stateNew {
false,
true};
295static constexpr std::string_view
299 case AuthDecodingState::HANDSHAKE:
300 return "HANDSHAKE"sv;
301 case AuthDecodingState::EST:
303 case AuthDecodingState::AUTH:
305 case AuthDecodingState::DATA:
307 case AuthDecodingState::AUTH_ERROR:
308 return "AUTH_ERROR"sv;
309 case AuthDecodingState::DONE:
311 case AuthDecodingState::TIMEOUT:
313 case AuthDecodingState::CANCELED:
315 case AuthDecodingState::ERR:
321namespace PayloadKey {
332 uint8_t schemeId {0};
334 MSGPACK_DEFINE_MAP(schemeId, payload)
336 void set(
std::string_view key,
std::string_view value) {
337 payload.emplace(std::string(key), std::string(value));
340 auto find(std::string_view key)
const {
return payload.find(std::string(key)); }
342 auto at(std::string_view key)
const {
return payload.at(std::string(key)); }
347 std::string logStr =
"=========\n";
348 logStr += fmt::format(
"scheme: {}\n", schemeId);
349 for (
const auto& [msgKey, msgVal] : payload) {
350 logStr += fmt::format(
" - {}: {}\n", msgKey, msgVal);
352 logStr +=
"=========";
358 timeoutMsg.
set(PayloadKey::stateMsg,
toString(AuthDecodingState::TIMEOUT));
366 static constexpr auto token =
"token"sv;
367 static constexpr auto error =
"error"sv;
368 static constexpr auto auth_scheme =
"auth_scheme"sv;
369 static constexpr auto peer_id =
"peer_id"sv;
370 static constexpr auto auth_error =
"auth_error"sv;
371 static constexpr auto peer_address =
"peer_address"sv;
376 using Map = std::map<std::string, std::string>;
386 void set(std::string_view key, std::string_view value) {
387 emplace(std::string(key), std::string(value));
400 case Error::AUTH_ERROR:
401 errStr =
"auth_error";
403 case Error::CANCELED:
422 bool authEnabled {
false};
423 bool archiveTransferredWithoutFailure {
false};
428 , state(initialState)
435 auto stateMsgIt = msg.
find(PayloadKey::stateMsg);
436 if (stateMsgIt != msg.
payload.end()) {
437 if (stateMsgIt->second ==
toString(AuthDecodingState::TIMEOUT)) {
438 this->state = AuthDecodingState::TIMEOUT;
447 auto stateMsgIt = msg.
find(PayloadKey::stateMsg);
448 if (stateMsgIt != msg.
payload.end()) {
449 if (stateMsgIt->second ==
toString(AuthDecodingState::CANCELED)) {
450 this->state = AuthDecodingState::CANCELED;
459 if (state == AuthDecodingState::AUTH_ERROR) {
460 return DeviceAuthInfo::Error::AUTH_ERROR;
461 }
else if (state == AuthDecodingState::TIMEOUT) {
462 return DeviceAuthInfo::Error::TIMEOUT;
463 }
else if (state == AuthDecodingState::CANCELED) {
464 return DeviceAuthInfo::Error::CANCELED;
465 }
else if (state == AuthDecodingState::ERR) {
466 return DeviceAuthInfo::Error::UNKNOWN;
467 }
else if (archiveTransferredWithoutFailure) {
468 return DeviceAuthInfo::Error::NONE;
470 return DeviceAuthInfo::Error::NETWORK;
475 return state == AuthDecodingState::DONE || state == AuthDecodingState::ERR
476 || state == AuthDecodingState::AUTH_ERROR || state == AuthDecodingState::TIMEOUT
477 || state == AuthDecodingState::CANCELED;
485 unsigned numOpenChannels {0};
486 unsigned maxOpenChannels {1};
487 std::shared_ptr<dhtnet::ChannelSocket>
channel;
488 msgpack::unpacker pac {[](msgpack::type::object_type, std::size_t,
void*) {
return true; },
491 std::string authScheme {fileutils::ARCHIVE_AUTH_SCHEME_NONE};
492 std::string credentialsFromUser {
""};
496 , tmpId(
std::move(id))
503 unsigned numTries {0};
504 unsigned maxTries {3};
505 std::shared_ptr<dhtnet::ChannelSocket>
channel;
511 , channel(
std::move(c))
517 timeoutMsg.
set(PayloadKey::stateMsg,
toString(AuthDecodingState::CANCELED));
523ArchiveAccountManager::provideAccountAuthentication(
const std::string& key,
524 const std::string& scheme)
526 if (scheme != fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD) {
527 JAMI_ERROR(
"[LinkDevice] Unsupported account authentication scheme attempted.");
536 if (
ctx->linkDevCtx->state != AuthDecodingState::AUTH) {
537 JAMI_WARNING(
"[LinkDevice] Invalid state for providing account authentication.");
541 ctx->linkDevCtx->state = AuthDecodingState::DATA;
542 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
543 ctx->accountId,
static_cast<uint8_t
>(DeviceAuthState::IN_PROGRESS),
DeviceAuthInfo {});
545 dht::ThreadPool::io().run([key = std::move(key), scheme,
ctx]()
mutable {
547 toSend.
set(PayloadKey::password, std::move(key));
548 msgpack::sbuffer buffer(UINT16_MAX);
550 msgpack::pack(buffer, toSend);
553 ctx->linkDevCtx->channel->write(
reinterpret_cast<const unsigned char*
>(buffer.data()),
556 }
catch (
const std::exception& e) {
557 JAMI_WARNING(
"[LinkDevice] Failed to send password over auth ChannelSocket. Channel "
567 msgpack::unpacker pac {[](msgpack::type::object_type, std::size_t,
void*) {
return true; },
574ArchiveAccountManager::startLoadArchiveFromDevice(
const std::shared_ptr<AuthContext>&
ctx)
577 JAMI_WARNING(
"[LinkDevice] Already loading archive from device.");
578 ctx->onFailure(AuthError::INVALID_ARGUMENTS,
"Already loading archive from device.");
581 JAMI_DEBUG(
"[LinkDevice] Starting load archive from device {} {}.",
586 dht::ThreadPool::computation().run([ctx, wthis = weak()] {
587 auto ca = dht::crypto::generateEcIdentity(
"Jami Temporary CA");
588 if (!ca.first || !ca.second) {
589 throw std::runtime_error(
"[LinkDevice] Can't generate CA for this account.");
592 auto user = dht::crypto::generateIdentity(
"Jami Temporary User", ca, 4096,
true);
593 if (!user.first || !user.second) {
594 throw std::runtime_error(
"[LinkDevice] Can't generate identity for this account.");
597 auto this_ = wthis.lock();
599 JAMI_WARNING(
"[LinkDevice] Failed to get the ArchiveAccountManager.");
604 ctx->linkDevCtx = std::make_shared<LinkDeviceContext>(
605 dht::crypto::generateIdentity(
"Jami Temporary device", user));
606 JAMI_LOG(
"[LinkDevice] Established linkDevCtx. {} {} {}.",
609 fmt::ptr(ctx->linkDevCtx));
612 auto gen = Manager::instance().getSeededRandomEngine();
613 ctx->linkDevCtx->opId = std::uniform_int_distribution<uint64_t>(100000, 999999)(gen);
615 ctx->linkDevCtx->tempConnMgr.oniOSConnected(
616 [&](
const std::string& connType, dht::InfoHash peer_h) {
return false; });
618 ctx->linkDevCtx->tempConnMgr.onDhtConnected(ctx->linkDevCtx->tmpId.second->getPublicKey());
620 auto accountScheme = fmt::format(
"{}{}/{}",
622 ctx->linkDevCtx->tmpId.second->getId(),
623 ctx->linkDevCtx->opId);
624 JAMI_LOG(
"[LinkDevice] auth scheme will be: {}", accountScheme);
627 info.set(DeviceAuthInfo::token, accountScheme);
629 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
630 ctx->accountId,
static_cast<uint8_t
>(DeviceAuthState::TOKEN_AVAILABLE), info);
632 ctx->linkDevCtx->tempConnMgr.onICERequest(
633 [wctx = std::weak_ptr(ctx)](
const DeviceId& deviceId) {
634 if (
auto ctx = wctx.lock()) {
635 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
637 static_cast<uint8_t>(DeviceAuthState::CONNECTING),
644 ctx->linkDevCtx->tempConnMgr.onChannelRequest(
645 [wthis, ctx](
const std::shared_ptr<dht::crypto::Certificate>& cert,
646 const std::string& name) {
647 std::string_view url(name);
648 if (!starts_with(url, CHANNEL_SCHEME)) {
650 "[LinkDevice] Temporary connection manager received invalid scheme: {}",
657 if (ctx->linkDevCtx->opId == parsedOpId
658 && ctx->linkDevCtx->numOpenChannels < ctx->linkDevCtx->maxOpenChannels) {
659 ctx->linkDevCtx->numOpenChannels++;
660 JAMI_DEBUG(
"[LinkDevice] Opening channel ({}/{}): {}",
661 ctx->linkDevCtx->numOpenChannels,
662 ctx->linkDevCtx->maxOpenChannels,
669 ctx->linkDevCtx->tempConnMgr.onConnectionReady([ctx,
671 wthis](
const DeviceId& deviceId,
672 const std::string& name,
673 std::shared_ptr<dhtnet::ChannelSocket> socket) {
675 JAMI_WARNING(
"[LinkDevice] Temporary connection manager received invalid socket.");
677 ctx->timeout->cancel();
678 ctx->timeout.reset();
679 ctx->linkDevCtx->numOpenChannels--;
680 if (
auto sthis = wthis.lock())
681 sthis->authCtx_.reset();
682 ctx->linkDevCtx->state = AuthDecodingState::ERR;
683 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
685 static_cast<uint8_t
>(DeviceAuthState::DONE),
686 DeviceAuthInfo::createError(DeviceAuthInfo::Error::NETWORK));
689 ctx->linkDevCtx->channel = socket;
691 ctx->timeout = std::make_unique<asio::steady_timer>(*Manager::instance().ioContext());
692 ctx->timeout->expires_from_now(OP_TIMEOUT);
693 ctx->timeout->async_wait([c = std::weak_ptr(ctx), socket](
const std::error_code& ec) {
697 if (
auto ctx = c.lock()) {
698 if (!ctx->linkDevCtx->isCompleted()) {
699 ctx->linkDevCtx->state = AuthDecodingState::TIMEOUT;
700 JAMI_WARNING(
"[LinkDevice] timeout: {}", socket->name());
703 msgpack::sbuffer buffer(UINT16_MAX);
704 msgpack::pack(buffer, AuthMsg::timeout());
706 socket->write(reinterpret_cast<const unsigned char*>(buffer.data()),
714 socket->onShutdown([ctx, name, wthis]() {
715 JAMI_WARNING(
"[LinkDevice] Temporary connection manager closing socket: {}", name);
717 ctx->timeout->cancel();
718 ctx->timeout.reset();
719 ctx->linkDevCtx->numOpenChannels--;
720 ctx->linkDevCtx->channel.reset();
721 if (
auto sthis = wthis.lock())
722 sthis->authCtx_.reset();
724 DeviceAuthInfo::Error error = ctx->linkDevCtx->getErrorState();
725 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
727 static_cast<uint8_t
>(DeviceAuthState::DONE),
728 DeviceAuthInfo::createError(error));
731 socket->setOnRecv([ctx,
732 decodingCtx = std::make_shared<DecodingContext>(),
733 wthis](
const uint8_t* buf,
size_t len) {
738 decodingCtx->pac.reserve_buffer(len);
739 std::copy_n(buf, len, decodingCtx->pac.buffer());
740 decodingCtx->pac.buffer_consumed(len);
743 msgpack::object_handle oh;
744 if (decodingCtx->pac.next(oh)) {
745 JAMI_DEBUG(
"[LinkDevice] NEW: Unpacking message.");
746 oh.get().convert(toRecv);
750 }
catch (
const std::exception& e) {
751 ctx->linkDevCtx->state = AuthDecodingState::ERR;
752 JAMI_ERROR(
"[LinkDevice] Error unpacking message from source device: {}", e.what());
756 JAMI_DEBUG(
"[LinkDevice] NEW: Successfully unpacked message from source\n{}",
758 JAMI_DEBUG(
"[LinkDevice] NEW: State is {}:{}",
759 ctx->linkDevCtx->scheme,
760 ctx->linkDevCtx->formattedAuthState());
763 if (toRecv.schemeId != 0) {
764 JAMI_WARNING(
"[LinkDevice] NEW: Unsupported scheme received from source");
765 ctx->linkDevCtx->state = AuthDecodingState::ERR;
770 if (ctx->linkDevCtx->handleCanceledMessage(toRecv)) {
775 bool shouldShutdown =
false;
776 auto accDataIt = toRecv.find(PayloadKey::accData);
777 bool shouldLoadArchive = accDataIt != toRecv.payload.end();
779 if (ctx->linkDevCtx->state == AuthDecodingState::HANDSHAKE) {
780 auto peerCert = ctx->linkDevCtx->channel->peerCertificate();
781 auto authScheme = toRecv.at(PayloadKey::authScheme);
783 != fileutils::ARCHIVE_AUTH_SCHEME_NONE;
785 JAMI_DEBUG(
"[LinkDevice] NEW: Auth scheme from payload is '{}'", authScheme);
786 ctx->linkDevCtx->state = AuthDecodingState::AUTH;
788 info.set(DeviceAuthInfo::auth_scheme, authScheme);
789 info.set(DeviceAuthInfo::peer_id, peerCert->issuer->getId().toString());
790 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
791 ctx->accountId,
static_cast<uint8_t
>(DeviceAuthState::AUTHENTICATING), info);
792 }
else if (ctx->linkDevCtx->state == AuthDecodingState::DATA) {
793 auto passwordCorrectIt = toRecv.find(PayloadKey::passwordCorrect);
794 auto canRetry = toRecv.find(PayloadKey::canRetry);
797 if (canRetry != toRecv.payload.end() &&
canRetry->second ==
"false") {
798 JAMI_DEBUG(
"[LinkDevice] Authentication failed: maximum retry attempts "
800 ctx->linkDevCtx->state = AuthDecodingState::AUTH_ERROR;
805 if (passwordCorrectIt != toRecv.payload.end()
806 && passwordCorrectIt->second ==
"false") {
807 ctx->linkDevCtx->state = AuthDecodingState::AUTH;
809 JAMI_DEBUG(
"[LinkDevice] NEW: Password incorrect.");
810 auto peerCert = ctx->linkDevCtx->channel->peerCertificate();
811 auto peer_id = peerCert->issuer->getId().toString();
814 auto authScheme = fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD;
817 info.set(DeviceAuthInfo::auth_scheme, authScheme);
818 info.set(DeviceAuthInfo::peer_id, peer_id);
819 info.set(DeviceAuthInfo::auth_error,
"invalid_credentials");
821 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
823 static_cast<uint8_t
>(DeviceAuthState::AUTHENTICATING),
828 if (!shouldLoadArchive) {
829 JAMI_DEBUG(
"[LinkDevice] NEW: no archive received.");
832 ctx->linkDevCtx->state = AuthDecodingState::ERR;
833 shouldShutdown =
true;
838 if (shouldLoadArchive) {
839 emitSignal<libjami::ConfigurationSignal::DeviceAuthStateChanged>(
841 static_cast<uint8_t
>(DeviceAuthState::IN_PROGRESS),
844 auto archive = AccountArchive(std::string_view(accDataIt->second));
845 if (
auto this_ = wthis.lock()) {
846 JAMI_DEBUG(
"[LinkDevice] NEW: Reading archive from peer.");
847 this_->onArchiveLoaded(*ctx, std::move(archive),
true);
848 JAMI_DEBUG(
"[LinkDevice] NEW: Successfully loaded archive.");
849 ctx->linkDevCtx->archiveTransferredWithoutFailure =
true;
851 ctx->linkDevCtx->archiveTransferredWithoutFailure =
false;
852 JAMI_ERROR(
"[LinkDevice] NEW: Failed to load account because of "
853 "null ArchiveAccountManager!");
855 }
catch (
const std::exception& e) {
856 ctx->linkDevCtx->state = AuthDecodingState::ERR;
857 ctx->linkDevCtx->archiveTransferredWithoutFailure =
false;
858 JAMI_WARNING(
"[LinkDevice] NEW: Error reading archive.");
860 shouldShutdown =
true;
863 if (shouldShutdown) {
864 ctx->linkDevCtx->channel->shutdown();
870 ctx->linkDevCtx->state = AuthDecodingState::HANDSHAKE;
874 JAMI_DEBUG(
"[LinkDevice] NEW: Packing first message for SOURCE.\nCurrent state is: "
878 ctx->linkDevCtx->formattedAuthState());
879 msgpack::sbuffer buffer(UINT16_MAX);
880 msgpack::pack(buffer, toSend);
882 ctx->linkDevCtx->channel->write(
reinterpret_cast<const unsigned char*
>(buffer.data()),
886 JAMI_LOG(
"[LinkDevice {}] Generated temporary account.",
887 ctx->linkDevCtx->tmpId.second->getId());
890 JAMI_DEBUG(
"[LinkDevice] Starting load archive from device END {} {}.",
896ArchiveAccountManager::addDevice(
const std::string& uriProvided,
897 std::string_view auth_scheme,
901 JAMI_WARNING(
"[LinkDevice] addDevice: auth context already exists.");
902 return static_cast<int32_t
>(AccountManager::AddDeviceError::ALREADY_LINKING);
904 JAMI_LOG(
"[LinkDevice] ArchiveAccountManager::addDevice({}, {})", accountId_, uriProvided);
906 std::string_view url(uriProvided);
908 JAMI_ERROR(
"[LinkDevice] Invalid uri provided: {}", uriProvided);
909 return static_cast<int32_t
>(AccountManager::AddDeviceError::INVALID_URI);
912 auto peerCodeS = url.substr(
AUTH_URI_SCHEME.length() + peerTempAcc.length() + 1, 6);
913 JAMI_LOG(
"[LinkDevice] ======\n * tempAcc = {}\n * code = {}", peerTempAcc, peerCodeS);
915 auto gen = Manager::instance().getSeededRandomEngine();
916 std::uniform_int_distribution<int32_t> dist(1, INT32_MAX);
917 auto token = dist(gen);
918 JAMI_WARNING(
"[LinkDevice] SOURCE: Creating auth context, token: {}.", token);
919 auto ctx = std::make_shared<AuthContext>();
920 ctx->accountId = accountId_;
922 ctx->credentials = std::make_unique<ArchiveAccountCredentials>();
926 dht::InfoHash(peerTempAcc),
928 [wthis = weak(), auth_scheme,
ctx, accountId=accountId_](std::shared_ptr<dhtnet::ChannelSocket> socket,
929 const dht::InfoHash& infoHash) {
930 auto this_ = wthis.lock();
931 if (!socket || !this_) {
933 "[LinkDevice] Invalid socket event while AccountManager connecting.");
935 this_->authCtx_.reset();
936 emitSignal<libjami::ConfigurationSignal::AddDeviceStateChanged>(
939 static_cast<uint8_t
>(DeviceAuthState::DONE),
940 DeviceAuthInfo::createError(DeviceAuthInfo::Error::NETWORK));
942 if (!this_->doAddDevice(auth_scheme,
ctx, socket))
943 emitSignal<libjami::ConfigurationSignal::AddDeviceStateChanged>(
946 static_cast<uint8_t
>(DeviceAuthState::DONE),
947 DeviceAuthInfo::createError(DeviceAuthInfo::Error::UNKNOWN));
950 runOnMainThread([token,
id = accountId_] {
951 emitSignal<libjami::ConfigurationSignal::AddDeviceStateChanged>(
952 id, token,
static_cast<uint8_t
>(DeviceAuthState::CONNECTING),
DeviceAuthInfo {});
955 }
catch (
const std::exception& e) {
956 JAMI_ERROR(
"[LinkDevice] Parsing uri failed: {}", uriProvided);
957 return static_cast<int32_t
>(AccountManager::AddDeviceError::GENERIC);
962ArchiveAccountManager::doAddDevice(std::string_view scheme,
963 const std::shared_ptr<AuthContext>&
ctx,
964 const std::shared_ptr<dhtnet::ChannelSocket>& channel)
967 JAMI_WARNING(
"[LinkDevice] SOURCE: addDevice canceled.");
971 JAMI_DEBUG(
"[LinkDevice] Setting up addDevice logic on SOURCE device.");
972 JAMI_DEBUG(
"[LinkDevice] SOURCE: Creating addDeviceCtx.");
973 ctx->addDeviceCtx = std::make_unique<AddDeviceContext>(channel);
974 ctx->addDeviceCtx->authScheme = scheme;
975 ctx->addDeviceCtx->state = AuthDecodingState::HANDSHAKE;
977 ctx->timeout = std::make_unique<asio::steady_timer>(*Manager::instance().ioContext());
978 ctx->timeout->expires_from_now(OP_TIMEOUT);
979 ctx->timeout->async_wait(
980 [wthis = weak(), wctx = std::weak_ptr(ctx)](
const std::error_code& ec) {
984 if (
auto ctx = wctx.lock()) {
985 if (!ctx->addDeviceCtx->isCompleted()) {
986 if (auto this_ = wthis.lock()) {
987 ctx->addDeviceCtx->state = AuthDecodingState::TIMEOUT;
988 JAMI_WARNING(
"[LinkDevice] Timeout for addDevice.");
991 msgpack::sbuffer buffer(UINT16_MAX);
992 msgpack::pack(buffer, AuthMsg::timeout());
994 ctx->addDeviceCtx->channel->write(reinterpret_cast<const unsigned char*>(
998 ctx->addDeviceCtx->channel->shutdown();
1004 JAMI_DEBUG(
"[LinkDevice] SOURCE: Creating callbacks.");
1005 channel->onShutdown([ctx, w = weak()]() {
1006 JAMI_DEBUG(
"[LinkDevice] SOURCE: Shutdown with state {}... xfer {}uccessful",
1007 ctx->addDeviceCtx->formattedAuthState(),
1008 ctx->addDeviceCtx->archiveTransferredWithoutFailure ?
"s" :
"uns");
1011 ctx->timeout->cancel();
1012 ctx->timeout.reset();
1014 if (
auto this_ = w.lock()) {
1015 this_->authCtx_.reset();
1018 DeviceAuthInfo::Error error = ctx->addDeviceCtx->getErrorState();
1019 emitSignal<libjami::ConfigurationSignal::AddDeviceStateChanged>(ctx->accountId,
1021 static_cast<uint8_t
>(
1022 DeviceAuthState::DONE),
1023 DeviceAuthInfo::createError(
1029 JAMI_DEBUG(
"[LinkDevice] Setting up receiving logic callback.");
1030 channel->setOnRecv([ctx,
1032 decodeCtx = std::make_shared<ArchiveAccountManager::DecodingContext>()](
1033 const uint8_t* buf,
size_t len) {
1034 JAMI_DEBUG(
"[LinkDevice] Setting up receiver callback for communication logic on SOURCE "
1038 auto this_ = wthis.lock();
1040 JAMI_ERROR(
"[LinkDevice] Invalid state for ArchiveAccountManager.");
1049 if (ctx->canceled || ctx->addDeviceCtx->state == AuthDecodingState::ERR) {
1054 decodeCtx->pac.reserve_buffer(len);
1055 std::copy_n(buf, len, decodeCtx->pac.buffer());
1056 decodeCtx->pac.buffer_consumed(len);
1059 JAMI_DEBUG(
"[LinkDevice] SOURCE: addDevice: setOnRecv: handling msg from NEW");
1060 msgpack::object_handle oh;
1063 if (decodeCtx->pac.next(oh)) {
1064 oh.get().convert(toRecv);
1065 JAMI_DEBUG(
"[LinkDevice] SOURCE: Successfully unpacked message from NEW "
1066 "(NEW->SOURCE)\n{}",
1067 toRecv.formatMsg());
1071 }
catch (
const std::exception& e) {
1073 ctx->addDeviceCtx->state = AuthDecodingState::ERR;
1074 JAMI_ERROR(
"[LinkDevice] error unpacking message from new device: {}", e.what());
1077 JAMI_DEBUG(
"[LinkDevice] SOURCE: State is '{}'", ctx->addDeviceCtx->formattedAuthState());
1082 if (toRecv.schemeId != 0) {
1083 ctx->addDeviceCtx->state = AuthDecodingState::ERR;
1084 JAMI_WARNING(
"[LinkDevice] Unsupported scheme received from a connection.");
1087 if (ctx->addDeviceCtx->state == AuthDecodingState::ERR
1088 || ctx->addDeviceCtx->state == AuthDecodingState::AUTH_ERROR) {
1089 JAMI_WARNING(
"[LinkDevice] Undefined behavior encountered during a link auth session.");
1090 ctx->addDeviceCtx->channel->shutdown();
1093 if (ctx->addDeviceCtx->handleTimeoutMessage(toRecv)) {
1097 bool shouldSendMsg =
false;
1098 bool shouldShutdown =
false;
1099 bool shouldSendArchive =
false;
1102 if (ctx->addDeviceCtx->state == AuthDecodingState::AUTH) {
1105 JAMI_DEBUG(
"[LinkDevice] SOURCE: addDevice: setOnRecv: verifying sent "
1106 "credentials from NEW");
1107 shouldSendMsg =
true;
1108 const auto& passwordIt = toRecv.find(PayloadKey::password);
1109 if (passwordIt != toRecv.payload.end()) {
1112 JAMI_DEBUG(
"[LinkDevice] Injecting account archive into outbound message.");
1113 ctx->addDeviceCtx->accData
1115 ->readArchive(fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD,
1118 shouldSendArchive =
true;
1119 JAMI_DEBUG(
"[LinkDevice] Sending account archive.");
1120 }
catch (
const std::exception& e) {
1121 ctx->addDeviceCtx->state = AuthDecodingState::ERR;
1122 JAMI_DEBUG(
"[LinkDevice] Finished reading archive: FAILURE: {}", e.what());
1123 shouldSendArchive =
false;
1126 if (!shouldSendArchive) {
1128 if (ctx->addDeviceCtx->numTries < ctx->addDeviceCtx->maxTries) {
1130 ctx->addDeviceCtx->numTries++;
1131 JAMI_DEBUG(
"[LinkDevice] Incorrect password received. "
1132 "Attempt {} out of {}.",
1133 ctx->addDeviceCtx->numTries,
1134 ctx->addDeviceCtx->maxTries);
1135 toSend.set(PayloadKey::passwordCorrect,
"false");
1136 toSend.set(PayloadKey::canRetry,
"true");
1139 JAMI_WARNING(
"[LinkDevice] Incorrect password received, maximum attempts reached.");
1140 toSend.set(PayloadKey::canRetry,
"false");
1141 ctx->addDeviceCtx->state = AuthDecodingState::AUTH_ERROR;
1142 shouldShutdown =
true;
1147 if (shouldSendArchive) {
1148 JAMI_DEBUG(
"[LinkDevice] SOURCE: Archive in message has encryption scheme '{}'",
1149 ctx->addDeviceCtx->authScheme);
1150 emitSignal<libjami::ConfigurationSignal::AddDeviceStateChanged>(
1153 static_cast<uint8_t
>(DeviceAuthState::IN_PROGRESS),
1155 shouldShutdown =
true;
1156 shouldSendMsg =
true;
1157 ctx->addDeviceCtx->archiveTransferredWithoutFailure =
true;
1158 toSend.set(PayloadKey::accData, ctx->addDeviceCtx->accData);
1160 if (shouldSendMsg) {
1161 JAMI_DEBUG(
"[LinkDevice] SOURCE: Sending msg to NEW:\n{}", toSend.formatMsg());
1162 msgpack::sbuffer buffer(UINT16_MAX);
1163 msgpack::pack(buffer, toSend);
1165 ctx->addDeviceCtx->channel->write(
reinterpret_cast<const unsigned char*
>(buffer.data()),
1170 if (shouldShutdown) {
1171 ctx->addDeviceCtx->channel->shutdown();
1177 if (ctx->addDeviceCtx->state == AuthDecodingState::HANDSHAKE) {
1178 ctx->addDeviceCtx->state = AuthDecodingState::EST;
1179 DeviceAuthInfo info;
1180 info.set(DeviceAuthInfo::peer_address, channel->getRemoteAddress().toString(
true));
1181 emitSignal<libjami::ConfigurationSignal::AddDeviceStateChanged>(
1182 ctx->accountId, ctx->token,
static_cast<uint8_t
>(DeviceAuthState::AUTHENTICATING), info);
1189ArchiveAccountManager::cancelAddDevice(uint32_t token)
1191 if (
auto ctx = authCtx_) {
1192 if (
ctx->token == token) {
1193 ctx->canceled =
true;
1194 if (
ctx->addDeviceCtx) {
1195 ctx->addDeviceCtx->state = AuthDecodingState::CANCELED;
1196 if (
ctx->addDeviceCtx->channel) {
1198 auto canceledMsg =
ctx->addDeviceCtx->createCanceledMsg();
1199 msgpack::sbuffer buffer(UINT16_MAX);
1200 msgpack::pack(buffer, canceledMsg);
1202 ctx->addDeviceCtx->channel->write(
reinterpret_cast<const unsigned char*
>(
1206 ctx->addDeviceCtx->channel->shutdown();
1210 ctx->onFailure(AuthError::UNKNOWN,
"");
1219ArchiveAccountManager::confirmAddDevice(uint32_t token)
1221 if (
auto ctx = authCtx_) {
1222 if (
ctx->token == token &&
ctx->addDeviceCtx
1223 &&
ctx->addDeviceCtx->state == AuthDecodingState::EST) {
1224 dht::ThreadPool::io().run([
ctx] {
1225 ctx->addDeviceCtx->state = AuthDecodingState::AUTH;
1227 JAMI_DEBUG(
"[LinkDevice] SOURCE: Packing first message for NEW and switching to "
1229 ctx->addDeviceCtx->formattedAuthState());
1230 toSend.
set(PayloadKey::authScheme,
ctx->addDeviceCtx->authScheme);
1231 msgpack::sbuffer buffer(UINT16_MAX);
1232 msgpack::pack(buffer, toSend);
1234 ctx->addDeviceCtx->channel->write(
reinterpret_cast<const unsigned char*
>(
1246ArchiveAccountManager::loadFromDHT(
const std::shared_ptr<AuthContext>&
ctx)
1248 ctx->dhtContext = std::make_unique<DhtLoadContext>();
1249 ctx->dhtContext->dht.run(
ctx->credentials->dhtPort, {},
true);
1250 for (
const auto& bootstrap :
ctx->credentials->dhtBootstrap) {
1251 ctx->dhtContext->dht.bootstrap(bootstrap);
1252 auto searchEnded = [
ctx, accountId = accountId_]() {
1253 if (not
ctx->dhtContext or
ctx->dhtContext->found) {
1256 auto& s = *ctx->dhtContext;
1257 if (s.stateOld.first && s.stateNew.first) {
1258 dht::ThreadPool::computation().run(
1260 network_error = !s.stateOld.second && !s.stateNew.second,
1261 accountId = std::move(accountId)] {
1262 ctx->dhtContext.reset();
1263 JAMI_WARNING(
"[Account {}] [Auth] Failure looking for archive on DHT: {}",
1265 network_error ?
"network error" :
"not found");
1266 ctx->onFailure(network_error ? AuthError::NETWORK : AuthError::UNKNOWN,
"");
1271 auto search = [ctx, searchEnded, w = weak()](
bool previous) {
1272 std::vector<uint8_t> key;
1274 auto& s = previous ? ctx->dhtContext->stateOld : ctx->dhtContext->stateNew;
1278 std::tie(key, loc) = computeKeys(ctx->credentials->password,
1279 ctx->credentials->uri,
1281 JAMI_LOG(
"[Auth] Attempting to load account from DHT with {:s} at {:s}",
1282 ctx->credentials->uri,
1284 if (not ctx->dhtContext or ctx->dhtContext->found) {
1287 ctx->dhtContext->dht.get(
1289 [ctx, key = std::move(key), w](
const std::shared_ptr<dht::Value>& val) {
1290 std::vector<uint8_t> decrypted;
1292 decrypted = archiver::decompress(
1293 dht::crypto::aesDecrypt(val->data, key));
1294 }
catch (
const std::exception& ex) {
1297 JAMI_DBG(
"[Auth] Found archive on the DHT");
1298 ctx->dhtContext->found =
true;
1299 dht::ThreadPool::computation().run(
1300 [ctx, decrypted = std::move(decrypted), w] {
1302 auto archive = AccountArchive(decrypted);
1303 if (
auto sthis = w.lock()) {
1304 if (ctx->dhtContext) {
1305 ctx->dhtContext->dht.join();
1306 ctx->dhtContext.reset();
1308 sthis->onArchiveLoaded(*ctx, std::move(archive),
false);
1310 }
catch (
const std::exception& e) {
1311 ctx->onFailure(AuthError::UNKNOWN,
"");
1314 return not ctx->dhtContext->found;
1317 JAMI_LOG(
"[Auth] DHT archive search ended at {}", loc.toString());
1322 }
catch (
const std::exception& e) {
1330 dht::ThreadPool::computation().run(std::bind(search,
true));
1331 dht::ThreadPool::computation().run(std::bind(search,
false));
1336ArchiveAccountManager::migrateAccount(AuthContext& ctx)
1338 JAMI_WARN(
"[Auth] Account migration needed");
1339 AccountArchive archive;
1341 archive =
readArchive(ctx.credentials->password_scheme, ctx.credentials->password);
1343 JAMI_DBG(
"[Auth] Unable to load archive");
1344 ctx.onFailure(AuthError::INVALID_ARGUMENTS,
"");
1348 updateArchive(archive);
1350 if (updateCertificates(archive, ctx.credentials->updateIdentity)) {
1353 onArchiveLoaded(ctx, std::move(archive),
false);
1355 ctx.onFailure(AuthError::UNKNOWN,
"");
1360ArchiveAccountManager::onArchiveLoaded(AuthContext& ctx, AccountArchive&& a,
bool isLinkDevProtocol)
1363 dhtnet::fileutils::check_dir(path_, 0700);
1365 if (isLinkDevProtocol) {
1366 a.
save(fileutils::getFullPath(path_, archivePath_),
1367 ctx.linkDevCtx->authScheme,
1368 ctx.linkDevCtx->credentialsFromUser);
1370 a.
save(fileutils::getFullPath(path_, archivePath_),
1371 ctx.credentials ? ctx.credentials->password_scheme :
"",
1372 ctx.credentials ? ctx.credentials->
password :
"");
1375 if (not a.
id.second->isCA()) {
1376 JAMI_ERROR(
"[Account {}] [Auth] Attempting to sign a certificate with a non-CA.",
1380 std::shared_ptr<dht::crypto::Certificate> deviceCertificate;
1381 std::unique_ptr<ContactList> contacts;
1382 auto usePreviousIdentity =
false;
1384 if (
auto oldId = ctx.credentials->updateIdentity.second) {
1385 contacts = std::make_unique<ContactList>(ctx.accountId, oldId, path_, onChange_);
1386 if (contacts->isValidAccountDevice(*oldId) && ctx.credentials->updateIdentity.first) {
1387 deviceCertificate = oldId;
1388 usePreviousIdentity =
true;
1389 JAMI_WARNING(
"[Account {}] [Auth] Using previously generated device certificate {}",
1391 deviceCertificate->getLongId());
1398 if (!deviceCertificate) {
1399 JAMI_WARNING(
"[Account {}] [Auth] Creating new device certificate", accountId_);
1400 auto request = ctx.request.get();
1401 if (not request->verify()) {
1402 JAMI_ERROR(
"[Account {}] [Auth] Invalid certificate request.", accountId_);
1403 ctx.onFailure(AuthError::INVALID_ARGUMENTS,
"");
1406 deviceCertificate = std::make_shared<dht::crypto::Certificate>(
1407 dht::crypto::Certificate::generate(*request, a.
id));
1408 JAMI_WARNING(
"[Account {}] [Auth] Created new device: {}",
1410 deviceCertificate->getLongId());
1413 auto receipt = makeReceipt(a.
id, *deviceCertificate, ethAccount);
1414 auto receiptSignature = a.
id.first->sign({receipt.first.begin(), receipt.first.end()});
1416 auto info = std::make_unique<AccountInfo>();
1417 auto pk = usePreviousIdentity ? ctx.credentials->updateIdentity.first : ctx.key.get();
1418 auto sharedPk = pk->getSharedPublicKey();
1419 info->identity.first = pk;
1420 info->identity.second = deviceCertificate;
1421 info->accountId = a.
id.second->getId().toString();
1422 info->devicePk = sharedPk;
1423 info->deviceId = info->devicePk->getLongId().toString();
1424 if (ctx.deviceName.empty())
1425 ctx.deviceName = info->deviceId.substr(8);
1428 contacts = std::make_unique<ContactList>(ctx.accountId, a.
id.second, path_, onChange_);
1430 info->contacts = std::move(contacts);
1431 info->contacts->setContacts(a.
contacts);
1432 info->contacts->foundAccountDevice(deviceCertificate, ctx.deviceName, clock::now());
1433 info->ethAccount = ethAccount;
1434 info->announce = std::move(receipt.second);
1435 ConversationModule::saveConvInfosToPath(path_, a.
conversations);
1437 info_ = std::move(info);
1439 ctx.onSuccess(*info_,
1441 std::move(receipt.first),
1442 std::move(receiptSignature));
1445std::pair<std::vector<uint8_t>, dht::InfoHash>
1446ArchiveAccountManager::computeKeys(
const std::string& password,
1447 const std::string& pin,
1451 auto now = std::chrono::duration_cast<std::chrono::seconds>(clock::now().time_since_epoch());
1452 auto tseed = now.count() / std::chrono::seconds(EXPORT_KEY_RENEWAL_TIME).count();
1455 std::ostringstream ss;
1456 ss << std::hex << tseed;
1457 auto tseed_str = ss.str();
1460 std::vector<uint8_t> salt_key;
1461 salt_key.reserve(pin.size() + tseed_str.size());
1462 salt_key.insert(salt_key.end(), pin.begin(), pin.end());
1463 salt_key.insert(salt_key.end(), tseed_str.begin(), tseed_str.end());
1464 auto key = dht::crypto::stretchKey(password, salt_key, 256 / 8);
1467 auto loc = dht::InfoHash::get(key);
1472std::pair<std::string, std::shared_ptr<dht::Value>>
1473ArchiveAccountManager::makeReceipt(
const dht::crypto::Identity&
id,
1474 const dht::crypto::Certificate& device,
1475 const std::string& ethAccount)
1477 JAMI_LOG(
"[Account {}] [Auth] Signing receipt for device {}", accountId_, device.getLongId());
1478 auto devId = device.getId();
1479 DeviceAnnouncement announcement;
1480 announcement.dev = devId;
1481 announcement.pk = device.getSharedPublicKey();
1482 dht::Value ann_val {announcement};
1483 ann_val.sign(*
id.first);
1485 auto packedAnnoucement = ann_val.getPacked();
1486 JAMI_LOG(
"[Account {}] [Auth] Device announcement size: {}",
1488 packedAnnoucement.size());
1490 std::ostringstream is;
1491 is <<
"{\"id\":\"" <<
id.second->getId() <<
"\",\"dev\":\"" << devId <<
"\",\"eth\":\""
1492 << ethAccount <<
"\",\"announce\":\"" << base64::encode(packedAnnoucement) <<
"\"}";
1495 return {is.str(), std::make_shared<dht::Value>(std::move(ann_val))};
1499ArchiveAccountManager::needsMigration(
const std::string& accountId,
const dht::crypto::Identity&
id)
1503 auto cert =
id.second->issuer;
1505 if (not cert->isCA()) {
1506 JAMI_WARNING(
"[Account {}] [Auth] certificate {} is not a CA, needs update.",
1511 if (cert->getExpiration() < clock::now()) {
1512 JAMI_WARNING(
"[Account {}] [Auth] certificate {} is expired, needs update.",
1517 cert = cert->issuer;
1523ArchiveAccountManager::syncDevices()
1525 if (not dht_ or not dht_->isRunning()) {
1526 JAMI_WARNING(
"[Account {}] Not syncing devices: DHT is not running", accountId_);
1529 JAMI_LOG(
"[Account {}] Building device sync from {}", accountId_, info_->deviceId);
1530 auto sync_data = info_->contacts->getSyncData();
1532 for (
const auto&
dev : getKnownDevices()) {
1534 if (
dev.first.toString() == info_->deviceId) {
1537 if (!
dev.second.certificate) {
1538 JAMI_WARNING(
"[Account {}] Unable to find certificate for {}", accountId_,
dev.first);
1541 auto pk =
dev.second.certificate->getSharedPublicKey();
1542 JAMI_LOG(
"[Account {}] Sending device sync to {} {}",
1545 dev.first.toString());
1546 auto syncDeviceKey = dht::InfoHash::get(
"inbox:" + pk->getId().toString());
1547 dht_->putEncrypted(syncDeviceKey, pk, sync_data);
1554 bool publishPresence)
1556 AccountManager::startSync(std::move(cb), std::move(dcb), publishPresence);
1559 dht::InfoHash::get(
"inbox:" + info_->devicePk->getId().toString()),
1565 [
this, sync](
const std::shared_ptr<dht::crypto::Certificate>& cert)
mutable {
1566 if (!cert or cert->getId() != sync.from) {
1567 JAMI_WARNING(
"[Account {}] Unable to find certificate for device {}",
1569 sync.from.toString());
1572 if (not foundAccountDevice(cert))
1574 onSyncData(std::move(sync));
1582ArchiveAccountManager::readArchive(std::string_view scheme,
const std::string& pwd)
const
1584 JAMI_LOG(
"[Account {}] [Auth] Reading account archive", accountId_);
1585 return AccountArchive(fileutils::getFullPath(path_, archivePath_), scheme, pwd);
1589ArchiveAccountManager::updateArchive(AccountArchive& archive)
const
1594 static const auto filtered_keys = {Ringtone::PATH,
1598 Conf::CONFIG_DHT_PORT,
1606 static const auto encoded_keys = {TLS::CA_LIST_FILE,
1607 TLS::CERTIFICATE_FILE,
1608 TLS::PRIVATE_KEY_FILE};
1610 JAMI_LOG(
"[Account {}] [Auth] Building account archive", accountId_);
1611 for (
const auto& it : onExportConfig_()) {
1613 if (std::any_of(std::begin(filtered_keys), std::end(filtered_keys), [&](
const auto& key) {
1614 return key == it.first;
1619 if (std::any_of(std::begin(encoded_keys), std::end(encoded_keys), [&](
const auto& key) {
1620 return key == it.first;
1623 archive.config.emplace(it.first, base64::encode(fileutils::loadFile(it.second)));
1627 archive.config[it.first] = it.second;
1631 archive.contacts = info_->contacts->getContacts();
1633 archive.conversations = ConversationModule::convInfosFromPath(path_);
1634 archive.conversationsRequests = ConversationModule::convRequestsFromPath(path_);
1639ArchiveAccountManager::saveArchive(AccountArchive& archive,
1640 std::string_view scheme,
1641 const std::string& pwd)
1644 updateArchive(archive);
1645 if (archivePath_.empty())
1646 archivePath_ =
"export.gz";
1647 archive.save(fileutils::getFullPath(path_, archivePath_), scheme, pwd);
1648 }
catch (
const std::runtime_error& ex) {
1649 JAMI_ERROR(
"[Account {}] [Auth] Unable to export archive: {}", accountId_, ex.what());
1655ArchiveAccountManager::changePassword(
const std::string& password_old,
1656 const std::string& password_new)
1659 auto path = fileutils::getFullPath(path_, archivePath_);
1660 AccountArchive(path, fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD, password_old)
1661 .
save(path, fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD, password_new);
1663 }
catch (
const std::exception&) {
1669ArchiveAccountManager::getPasswordKey(
const std::string& password)
1672 auto data = dhtnet::fileutils::loadFile(fileutils::getFullPath(path_, archivePath_));
1674 auto key = dht::crypto::aesGetKey(data, password);
1675 auto decrypted = dht::crypto::aesDecrypt(dht::crypto::aesGetEncrypted(data), key);
1677 }
catch (
const std::exception& e) {
1678 JAMI_ERROR(
"[Account {}] Error loading archive: {}", accountId_, e.what());
1684ArchiveAccountManager::revokeDevice(
const std::string& device,
1685 std::string_view scheme,
1686 const std::string& password,
1689 auto fa = dht::ThreadPool::computation().getShared<
AccountArchive>(
1690 [
this, scheme = std::string(scheme), password] {
return readArchive(scheme, password); });
1692 [fa = std::move(fa),
1693 scheme = std::string(scheme),
1698 const std::shared_ptr<dht::crypto::Certificate>& crt)
mutable {
1700 cb(RevokeDeviceResult::ERROR_NETWORK);
1703 auto this_ = w.lock();
1706 this_->info_->contacts->foundAccountDevice(crt);
1711 cb(RevokeDeviceResult::ERROR_CREDENTIALS);
1716 a.
revoked = std::make_shared<
decltype(a.
revoked)::element_type>();
1720 this_->certStore().pinRevocationList(a.
id.second->getId().toString(),
1722 this_->certStore().loadRevocations(*a.
id.second);
1725 auto h = a.
id.second->getId();
1726 this_->dht_->put(h, a.
revoked, dht::DoneCallback {}, {},
true);
1728 this_->saveArchive(a, scheme, password);
1729 this_->info_->contacts->removeAccountDevice(crt->getLongId());
1730 cb(RevokeDeviceResult::SUCCESS);
1731 this_->syncDevices();
1737ArchiveAccountManager::exportArchive(
const std::string& destinationPath,
1738 std::string_view scheme,
1739 const std::string& password)
1744 updateArchive(archive);
1745 auto archivePath = fileutils::getFullPath(path_, archivePath_);
1746 if (!archive.
save(archivePath, scheme, password))
1751 std::filesystem::copy_file(archivePath,
1753 std::filesystem::copy_options::overwrite_existing,
1756 }
catch (
const std::runtime_error& ex) {
1757 JAMI_ERR(
"[Auth] Unable to export archive: %s", ex.what());
1760 JAMI_ERR(
"[Auth] Unable to export archive: Unable to read archive");
1766ArchiveAccountManager::isPasswordValid(
const std::string& password)
1769 readArchive(fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD, password);
1778ArchiveAccountManager::registerName(
const std::string& name,
1779 std::string_view scheme,
1780 const std::string& password,
1781 RegistrationCallback cb)
1783 std::string signedName;
1784 auto nameLowercase {name};
1785 std::transform(nameLowercase.begin(), nameLowercase.end(), nameLowercase.begin(), ::tolower);
1786 std::string publickey;
1787 std::string accountId;
1788 std::string ethAccount;
1791 auto archive = readArchive(scheme, password);
1792 auto privateKey = archive.id.first;
1793 const auto& pk = privateKey->getPublicKey();
1794 publickey = pk.toString();
1795 accountId = pk.getId().toString();
1796 signedName = base64::encode(
1797 privateKey->sign(std::vector<uint8_t>(nameLowercase.begin(), nameLowercase.end())));
1799 }
catch (
const std::exception& e) {
1801 cb(NameDirectory::RegistrationResponse::invalidCredentials, name);
1805 nameDir_.get().registerName(accountId, nameLowercase, ethAccount, cb, signedName, publickey);
Account specific keys/constants that must be shared in daemon and clients.
Simple class that represents a "key pair".
static KeyPair create()
Create a new, randomly generated object.
Address const & address() const
Retrieve the associated address of the public key.
std::function< void(const std::shared_ptr< dht::crypto::Certificate > &)> OnNewDeviceCb
const std::string accountId_
OnChangeCallback onChange_
CertRequest buildRequest(PrivateKey fDeviceKey)
std::function< void()> OnDeviceAnnouncedCb
std::shared_future< std::shared_ptr< dht::crypto::PrivateKey > > PrivateKey
std::function< void(RevokeDeviceResult)> RevokeDeviceCallback
std::function< void(AuthError error, const std::string &message)> AuthFailureCallback
std::function< void(const AccountInfo &info, const std::map< std::string, std::string > &config, std::string &&receipt, std::vector< uint8_t > &&receipt_signature)> AuthSuccessCallback
void initAuthentication(PrivateKey request, std::string deviceName, std::unique_ptr< AccountCredentials > credentials, AuthSuccessCallback onSuccess, AuthFailureCallback onFailure, const OnChangeCallback &onChange) override
Manages channels for syncing informations.
void connect(const DeviceId &deviceId, const std::string &name, ConnectCb &&cb, const std::string &connectionType="", bool forceNewConnection=false) override
Ask for a new sync channel.
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
static constexpr auto stateMsg
static constexpr auto accData
static constexpr auto password
static constexpr auto passwordCorrect
static constexpr auto canRetry
static constexpr auto authScheme
ArchiveStorageData readArchive(const std::filesystem::path &path, std::string_view scheme, const std::string &pwd)
static constexpr std::string_view toString(AuthDecodingState state)
void emitSignal(Args... args)
constexpr auto CHANNEL_SCHEME
const constexpr auto EXPORT_KEY_RENEWAL_TIME
constexpr auto AUTH_URI_SCHEME
constexpr auto OP_TIMEOUT
Crypto material contained in the archive, not persisted in the account configuration.
bool save(const std::filesystem::path &path, std::string_view scheme, const std::string &password) const
Save archive to file, optionally encrypted with provided password.
std::map< dht::InfoHash, Contact > contacts
Contacts.
std::map< std::string, ConversationRequest > conversationsRequests
std::shared_ptr< dht::crypto::RevocationList > revoked
Revoked devices.
std::shared_ptr< dht::crypto::PrivateKey > ca_key
Generated CA key (for self-signed certificates)
dht::crypto::Identity id
Account main private key and certificate chain.
std::map< std::string, ConvInfo > conversations
std::vector< uint8_t > eth_key
Ethereum private key.
std::map< std::string, std::string > config
Account configuration.
AuthMsg createCanceledMsg() const
std::string_view authScheme
AddDeviceContext(std::shared_ptr< dhtnet::ChannelSocket > c)
std::shared_ptr< dhtnet::ChannelSocket > channel
void set(std::string_view key, std::string_view value)
std::map< std::string, std::string > payload
auto at(std::string_view key) const
auto find(std::string_view key) const
std::map< std::string, std::string > Map
static DeviceAuthInfo createError(Error err)
DeviceAuthInfo(const Map &map)
void set(std::string_view key, std::string_view value)
DeviceAuthInfo(Map &&map)
constexpr std::string_view formattedAuthState() const
DeviceAuthInfo::Error getErrorState() const
bool handleTimeoutMessage(const AuthMsg &msg)
bool handleCanceledMessage(const AuthMsg &msg)
DeviceContextBase(uint64_t operationId, AuthDecodingState initialState)
LinkDeviceContext(dht::crypto::Identity id)
dht::crypto::Identity tmpId
dhtnet::ConnectionManager tempConnMgr
std::shared_ptr< dhtnet::ChannelSocket > channel