Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include "config.h"
21 : #endif
22 :
23 : #include "contact_list.h"
24 : #include "logger.h"
25 : #ifdef ENABLE_NAMESERVER
26 : #include "namedirectory.h"
27 : #endif
28 :
29 : #include <opendht/crypto.h>
30 : #include <optional>
31 : #include <functional>
32 : #include <map>
33 : #include <string>
34 : #include <filesystem>
35 :
36 : #include <dhtnet/multiplexed_socket.h>
37 :
38 : namespace dht {
39 : class DhtRunner;
40 : }
41 :
42 : namespace jami {
43 :
44 : using DeviceId = dht::PkId;
45 : struct AccountArchive;
46 : class AuthChannelHandler;
47 :
48 : struct AccountInfo
49 : {
50 : dht::crypto::Identity identity;
51 : std::unique_ptr<ContactList> contacts;
52 : std::string accountId;
53 : std::string deviceId;
54 : std::shared_ptr<dht::crypto::PublicKey> devicePk;
55 : std::shared_ptr<dht::Value> announce;
56 : std::string ethAccount;
57 : std::string username;
58 : std::string photo;
59 : };
60 :
61 : template<typename To, typename From>
62 : std::unique_ptr<To>
63 634 : dynamic_unique_cast(std::unique_ptr<From>&& p)
64 : {
65 634 : if (auto cast = dynamic_cast<To*>(p.get())) {
66 634 : std::unique_ptr<To> result(cast);
67 634 : p.release();
68 634 : return result;
69 634 : }
70 0 : return {};
71 : }
72 :
73 : class AccountManager : public std::enable_shared_from_this<AccountManager>
74 : {
75 : public:
76 : using OnChangeCallback = ContactList::OnChangeCallback;
77 : using clock = std::chrono::system_clock;
78 : using time_point = clock::time_point;
79 : using OnNewDeviceCb = std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>;
80 : using OnDeviceAnnouncedCb = std::function<void()>;
81 :
82 647 : AccountManager(const std::string& accountId,
83 : const std::filesystem::path& path,
84 : const std::string& nameServer)
85 1294 : : accountId_(accountId)
86 647 : , path_(path)
87 1294 : , nameDir_(NameDirectory::instance(nameServer)) {};
88 :
89 : virtual ~AccountManager();
90 :
91 : constexpr static const char* const DHT_TYPE_NS = "cx.ring";
92 :
93 : // Auth
94 :
95 : enum class AuthError { UNKNOWN, INVALID_ARGUMENTS, SERVER_ERROR, NETWORK };
96 :
97 : using AuthSuccessCallback = std::function<void(const AccountInfo& info,
98 : const std::map<std::string, std::string>& config,
99 : std::string&& receipt,
100 : std::vector<uint8_t>&& receipt_signature)>;
101 :
102 : using AuthFailureCallback = std::function<void(AuthError error, const std::string& message)>;
103 : using DeviceSyncCallback = std::function<void(DeviceSync&& syncData)>;
104 : using CertRequest = std::future<std::unique_ptr<dht::crypto::CertificateRequest>>;
105 : using PrivateKey = std::shared_future<std::shared_ptr<dht::crypto::PrivateKey>>;
106 :
107 : CertRequest buildRequest(PrivateKey fDeviceKey);
108 :
109 : struct AccountCredentials
110 : {
111 : std::string scheme;
112 : std::string uri;
113 : std::string password_scheme;
114 : std::string password;
115 630 : virtual ~AccountCredentials() {};
116 : };
117 :
118 : virtual void initAuthentication(PrivateKey request,
119 : std::string deviceName,
120 : std::unique_ptr<AccountCredentials> credentials,
121 : AuthSuccessCallback onSuccess,
122 : AuthFailureCallback onFailure,
123 : const OnChangeCallback& onChange)
124 : = 0;
125 :
126 : virtual bool changePassword(const std::string& password_old, const std::string& password_new) = 0;
127 :
128 : virtual void syncDevices() = 0;
129 : virtual void onSyncData(DeviceSync&& device, bool checkDevice = true);
130 :
131 0 : virtual bool isPasswordValid(const std::string& /*password*/) { return false; };
132 0 : virtual std::vector<uint8_t> getPasswordKey(const std::string& /*password*/) { return {}; };
133 :
134 : dht::crypto::Identity loadIdentity(const std::string& crt_path,
135 : const std::string& key_path,
136 : const std::string& key_pwd) const;
137 :
138 : const AccountInfo* useIdentity(const dht::crypto::Identity& id,
139 : const std::string& receipt,
140 : const std::vector<uint8_t>& receiptSignature,
141 : const std::string& username,
142 : const OnChangeCallback& onChange);
143 :
144 587 : void setDht(const std::shared_ptr<dht::DhtRunner>& dht) { dht_ = dht; }
145 :
146 : virtual void startSync(const OnNewDeviceCb& cb,
147 : const OnDeviceAnnouncedCb& dcb,
148 : bool publishPresence = true);
149 :
150 69492 : const AccountInfo* getInfo() const { return info_.get(); }
151 :
152 : void reloadContacts();
153 :
154 : // Device management
155 :
156 : enum class AddDeviceError { INVALID_URI = -1, ALREADY_LINKING = -2, GENERIC = -3 };
157 :
158 : enum class RevokeDeviceResult {
159 : SUCCESS = 0,
160 : ERROR_CREDENTIALS,
161 : ERROR_NETWORK,
162 : };
163 :
164 : using RevokeDeviceCallback = std::function<void(RevokeDeviceResult)>;
165 :
166 : /**
167 : * Initiates the process of adding a new device to the account
168 : * @param uri The URI provided by the new device to be added
169 : * @param auth_scheme The auth scheme (currently only "password" is expected)
170 : * @param chanel
171 : * @return A positive operation ID if successful, or a negative value indicating an AddDeviceError:
172 : * - INVALID_URI (-1): The provided URI is invalid
173 : * - ALREADY_LINKING (-2): A device linking operation is already in progress
174 : * - GENERIC (-3): A generic error occurred during the process
175 : */
176 0 : virtual int32_t addDevice(const std::string& /*uri*/,
177 : std::string_view /*auth_scheme*/,
178 : AuthChannelHandler*)
179 : {
180 0 : return 0;
181 : };
182 0 : virtual bool cancelAddDevice(uint32_t /*token*/) { return false; };
183 0 : virtual bool confirmAddDevice(uint32_t /*token*/) { return false; };
184 0 : virtual bool revokeDevice(const std::string& /*device*/,
185 : std::string_view /*scheme*/,
186 : const std::string& /*password*/,
187 : RevokeDeviceCallback)
188 : {
189 0 : return false;
190 : };
191 :
192 : const std::map<dht::PkId, KnownDevice>& getKnownDevices() const;
193 : bool foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
194 : const std::string& name = {},
195 1611 : const time_point& last_sync = time_point::min());
196 : // bool removeAccountDevice(const dht::InfoHash& device);
197 : void setAccountDeviceName(/*const dht::InfoHash& device, */ const std::string& name);
198 : std::string getAccountDeviceName() const;
199 :
200 : void forEachDevice(const dht::InfoHash& to,
201 : std::function<void(const std::shared_ptr<dht::crypto::PublicKey>&)>&& op,
202 : std::function<void(bool)>&& end = {});
203 :
204 : using PeerCertificateCb = std::function<void(const std::shared_ptr<dht::crypto::Certificate>& crt,
205 : const dht::InfoHash& peer_account)>;
206 : void onPeerMessage(const dht::crypto::PublicKey& peer_device,
207 : bool allowPublic,
208 : PeerCertificateCb&& cb);
209 : bool onPeerCertificate(const std::shared_ptr<dht::crypto::Certificate>& crt,
210 : bool allowPublic,
211 : dht::InfoHash& account_id);
212 :
213 : /**
214 : * Inform that a potential peer device have been found.
215 : * Returns true only if the device certificate is a valid device certificate.
216 : * In that case (true is returned) the account_id parameter is set to the peer account ID.
217 : */
218 : static bool foundPeerDevice(const std::string& accoundId,
219 : const std::shared_ptr<dht::crypto::Certificate>& crt,
220 : dht::InfoHash& account_id);
221 :
222 : // Contact requests
223 :
224 : std::vector<std::map<std::string, std::string>> getTrustRequests() const;
225 : // Note: includeConversation used for compatibility test, do not use if not in test env.
226 : bool acceptTrustRequest(const std::string& from, bool includeConversation = true);
227 : bool discardTrustRequest(const std::string& from);
228 :
229 : void sendTrustRequest(const std::string& to,
230 : const std::string& convId,
231 : const std::vector<uint8_t>& payload);
232 : void sendTrustRequestConfirm(const dht::InfoHash& to,
233 : const std::string& conversationId); // TODO ideally no convId here
234 :
235 : // Contact
236 :
237 : /**
238 : * Add contact to the account contact list.
239 : * Set confirmed if we know the contact also added us.
240 : */
241 : bool addContact(const dht::InfoHash& uri,
242 : bool confirmed = false,
243 : const std::string& conversationId = "");
244 : void removeContact(const std::string& uri, bool banned = true);
245 : void removeContactConversation(const std::string& uri); // for non swarm contacts
246 : void updateContactConversation(const std::string& uri, const std::string& convId);
247 : std::vector<std::map<std::string, std::string>> getContacts(bool includeRemoved = false) const;
248 :
249 : /** Obtain details about one account contact in serializable form. */
250 : std::map<std::string, std::string> getContactDetails(const std::string& uri) const;
251 : std::optional<Contact> getContactInfo(const std::string& uri) const;
252 :
253 : virtual bool findCertificate(
254 : const dht::InfoHash& h,
255 : std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {});
256 :
257 : virtual bool findCertificate(
258 : const dht::PkId& h,
259 : std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {});
260 :
261 : bool setCertificateStatus(const std::string& cert_id,
262 : dhtnet::tls::TrustStore::PermissionStatus status);
263 : bool setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert,
264 : dhtnet::tls::TrustStore::PermissionStatus status,
265 : bool local = true);
266 : std::vector<std::string> getCertificatesByStatus(
267 : dhtnet::tls::TrustStore::PermissionStatus status);
268 : dhtnet::tls::TrustStore::PermissionStatus getCertificateStatus(const std::string& cert_id) const;
269 : bool isAllowed(const crypto::Certificate& crt, bool allowPublic = false);
270 :
271 : static std::shared_ptr<dht::Value> parseAnnounce(const std::string& announceBase64,
272 : const std::string& accountId,
273 : const std::string& deviceSha1,
274 : const std::string& deviceSha256);
275 :
276 : // Name resolver
277 : using LookupCallback = NameDirectory::LookupCallback;
278 : using SearchResult = NameDirectory::SearchResult;
279 : using SearchCallback = NameDirectory::SearchCallback;
280 : using RegistrationCallback = NameDirectory::RegistrationCallback;
281 : using SearchResponse = NameDirectory::Response;
282 :
283 : virtual void lookupUri(const std::string& name,
284 : const std::string& defaultServer,
285 : LookupCallback cb);
286 : virtual void lookupAddress(const std::string& address, LookupCallback cb);
287 0 : virtual bool searchUser(const std::string& /*query*/, SearchCallback /*cb*/) { return false; }
288 : virtual void registerName(const std::string& name,
289 : std::string_view scheme,
290 : const std::string& password,
291 : RegistrationCallback cb)
292 : = 0;
293 :
294 : dhtnet::tls::CertificateStore& certStore() const;
295 :
296 : protected:
297 : const std::string accountId_;
298 : const std::filesystem::path path_;
299 : OnChangeCallback onChange_;
300 : std::unique_ptr<AccountInfo> info_;
301 : std::shared_ptr<dht::DhtRunner> dht_;
302 : std::reference_wrapper<NameDirectory> nameDir_;
303 : };
304 :
305 : } // namespace jami
|