tdlib-obf API
Reference documentation for the public tdlib-obf API, generated from TDLib schemas and public headers
Loading...
Searching...
No Matches
e2e_api.h
1//
2// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2026
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7#pragma once
8
9#include "td/e2e/e2e_errors.h"
10
11#include <array>
12#include <cstdint>
13#include <optional>
14#include <string>
15#include <string_view>
16#include <tuple>
17#include <utility>
18#include <variant>
19#include <vector>
20
21namespace td {
22template <class T>
23class Result;
24class Status;
25} // namespace td
26
27namespace tde2e_api {
28//
29// Result and Error helper classes
30//
31
32struct Error {
33 ErrorCode code;
34 std::string message;
35};
36
37template <typename T>
38class Result {
39 public:
40 Result(const T &value) : data_(value) {
41 }
42 Result(T &&value) : data_(std::move(value)) {
43 }
44
45 Result(const Error &error) : data_(error) {
46 }
47 Result(Error &&error) : data_(std::move(error)) {
48 }
49
50 Result(td::Result<T> &&value);
51 Result(td::Status &&status);
52
53 // Check if the result is a success
54 bool is_ok() const {
55 return std::holds_alternative<T>(data_);
56 }
57
58 // Check if the result is an error
59 bool is_error() const {
60 return std::holds_alternative<Error>(data_);
61 }
62
63 T &value() {
64 return std::get<T>(data_);
65 }
66 const T &value() const {
67 return std::get<T>(data_);
68 }
69
70 Error &error() {
71 return std::get<Error>(data_);
72 }
73 const Error &error() const {
74 return std::get<Error>(data_);
75 }
76
77 private:
78 std::variant<T, Error> data_;
79};
80
81//
82// Encryption
83//
84
85using Int256 = std::array<unsigned char, 32>;
86using Int512 = std::array<unsigned char, 64>;
87//TODO: strong typed ids
88using PublicKey = std::string;
89using HandshakeId = std::int64_t;
90using LoginId = std::int64_t;
91using UserId = std::int64_t;
92using AnyKeyId = std::int64_t;
93using PrivateKeyId = std::int64_t;
94using PublicKeyId = std::int64_t;
95using SymmetricKeyId = std::int64_t;
96using Bytes = std::string;
97using SecureBytes = std::string;
98using Slice = std::string_view;
99using SecureSlice = std::string_view;
100struct Ok {};
101
103 std::vector<std::string> encrypted_headers;
104 std::string encrypted_message;
105};
106
107Result<Ok> set_log_verbosity_level(int level);
108
109// Keys management
110// private keys will stay inside the library when it is possible
111// all keys are stored only in memory and should be created or imported before usage
112Result<PrivateKeyId> key_generate_private_key();
113Result<PrivateKeyId> key_generate_temporary_private_key();
114Result<SymmetricKeyId> key_derive_secret(PrivateKeyId key_id, Slice tag);
115Result<SymmetricKeyId> key_from_bytes(SecureSlice secret);
116Result<Bytes> key_to_encrypted_private_key(PrivateKeyId key_id, SymmetricKeyId secret_id);
117Result<PrivateKeyId> key_from_encrypted_private_key(Slice encrypted_key, SymmetricKeyId secret_id);
118Result<PublicKeyId> key_from_public_key(Slice public_key);
119Result<SymmetricKeyId> key_from_ecdh(PrivateKeyId key_id, PublicKeyId other_public_key_id);
120Result<PublicKey> key_to_public_key(PrivateKeyId key_id);
121Result<SecureBytes> key_to_words(PrivateKeyId key_id);
122Result<PrivateKeyId> key_from_words(SecureSlice words);
123Result<Int512> key_sign(PrivateKeyId key, Slice data);
124Result<Ok> key_destroy(AnyKeyId key_id);
125Result<Ok> key_destroy_all();
126
127// Used to encrypt key between processes, secret_id must be generated with key_from_ecdh
128Result<Bytes> key_to_encrypted_private_key_internal(PrivateKeyId key_id, SymmetricKeyId secret_id);
129Result<PrivateKeyId> key_from_encrypted_private_key_internal(Slice encrypted_key, SymmetricKeyId secret_id);
130
131Result<EncryptedMessageForMany> encrypt_message_for_many(const std::vector<SymmetricKeyId> &key_ids,
132 SecureSlice message);
133// keeps encrypted_message empty in result
134Result<EncryptedMessageForMany> re_encrypt_message_for_many(SymmetricKeyId decrypt_key_id,
135 const std::vector<SymmetricKeyId> &encrypt_key_ids,
136 Slice encrypted_header, Slice encrypted_message);
137Result<SecureBytes> decrypt_message_for_many(SymmetricKeyId key_id, Slice encrypted_header, Slice encrypted_message);
138Result<Bytes> encrypt_message_for_one(SymmetricKeyId key_id, SecureSlice message);
139Result<SecureBytes> decrypt_message_for_one(SymmetricKeyId key_id, Slice encrypted_message);
140
141// Utilities for secret key verification/transfer via qr (or any other alternative channel)
142// Requires:
143// - alternative channel to reliably transfer 'start' message from Bob to Alice (scanning QR is such channel)
144//
145// Alice:
146// - knows shared secret right after the handshake creation
147// - Bob's secret key is verified after finish is received
148//
149// Bob:
150// - knows shared secret right after accept is received
151// - Alice's secret key is verified after accept is received
152//
153// Use cases:
154// - Transfer of the key from old device to the new device
155// - Verification of other person's public key
156// - Contact sharing
157//
158Result<HandshakeId> handshake_create_for_bob(UserId bob_user_id, PrivateKeyId bob_private_key_id);
159Result<HandshakeId> handshake_create_for_alice(UserId alice_user_id, PrivateKeyId alice_private_key_id,
160 UserId bob_user_id, const PublicKey &bob_public_key, Slice start);
161
162Result<Bytes> handshake_bob_send_start(HandshakeId bob_handshake_id);
163Result<Bytes> handshake_alice_send_accept(HandshakeId alice_handshake_id);
164Result<Bytes> handshake_bob_receive_accept_send_finish(HandshakeId bob_handshake_id, UserId alice_id,
165 const PublicKey &alice_public_key, Slice accept);
166Result<Ok> handshake_alice_receive_finish(HandshakeId alice_handshake_id, Slice finish);
167Result<SymmetricKeyId> handshake_get_shared_key_id(HandshakeId handshake_id);
168Result<Ok> handshake_destroy(HandshakeId handshake_id);
169Result<Ok> handshake_destroy_all();
170
171// Helper to get QR-code identifier
172Result<Bytes> handshake_start_id(Slice start);
173
174// There is wrapper for login
175Result<LoginId> login_create_for_bob();
176Result<Bytes> login_bob_send_start(LoginId bob_login_id);
177Result<Bytes> login_create_for_alice(UserId alice_user_id, PrivateKeyId alice_private_key_id, Slice start);
178Result<PrivateKeyId> login_finish_for_bob(LoginId bob_login_id, UserId alice_user_id, const PublicKey &alice_public_key,
179 Slice data);
180Result<Ok> login_destroy(LoginId login_id);
181Result<Ok> login_destroy_all();
182
183// Personal info
184
185// 1. Each entry stored and signed separately
186// 2. Signature is never stored, but always is verified
187// 3. It should be possible to save data without signature (but it can't override data with signature)
188// 4. We should keep source of entry. Our, Server, Contact+Ts
189// 5. We must keep is_contact flag.
190
191template <class T>
192struct Entry {
193 enum Source { Self, Server, Contact };
194 Source source;
195 std::uint32_t timestamp;
196 T value;
197
198 Entry() : source(Self), timestamp(0), value() {
199 }
200 Entry(Source source, std::uint32_t timestamp, T &&value)
201 : source(source), timestamp(timestamp), value(std::move(value)) {
202 }
203};
204
205template <class T>
207 Int512 signature;
208 std::uint32_t timestamp{0};
209 T value;
210};
211
212struct Name {
213 std::string first_name;
214 std::string last_name;
215};
216
218 std::string phone_number;
219};
220
222 std::optional<Int256> self_nonce;
223 std::optional<Int256> contact_nonce_hash;
224 std::optional<Int256> contact_nonce;
225};
226
228 enum State { Unknown, Contact, NotContact };
229 State state{Unknown};
230
231 ContactState() = default;
232
233 explicit ContactState(State state) : state(state) {
234 }
235};
236
237struct Contact {
238 // Each contact has both public_key and user_id
239 // how it is stored internally is not clients library user's concern
240 std::uint32_t generation{0};
241
242 // we always have public key. Essentially contact is defined by its public key
243 PublicKeyId public_key{};
244
245 // Personal data, signed by contact. If it is not signed, it has no relations to this public key
246 std::optional<Entry<UserId>> o_user_id;
247 std::optional<Entry<Name>> o_name;
248 std::optional<Entry<PhoneNumber>> o_phone_number;
249
250 // Personal data save by user_itself
251 // It always exists and it always given but we keep timestamp just in case
252 Entry<EmojiNonces> emoji_nonces;
253 Entry<ContactState> contact_state;
254};
255
256// library can't enforce persistency of changes, because it has no persistent state.
257// so it is client's responsibility to ensure that each change will be eventually saved to the server
258// NB: it is unclear how protect ourself from server ignoring our queries.
259
260using StorageId = std::int64_t;
261using UpdateId = std::int64_t;
262
264 std::string next_suggested_block;
265 std::vector<std::string> required_proofs;
266};
268 std::vector<std::pair<PublicKeyId, std::optional<Contact>>> updates;
269};
270
271Result<StorageId> storage_create(PrivateKeyId key_id, Slice last_block);
272Result<Ok> storage_destroy(StorageId storage_id);
273Result<Ok> storage_destroy_all();
274
275template <class T>
276Result<UpdateId> storage_update_contact(StorageId storage_id, PublicKeyId key, SignedEntry<T> signed_entry);
277template <class T>
278Result<SignedEntry<T>> storage_sign_entry(PrivateKeyId key, Entry<T> entry);
279
280Result<std::optional<Contact>> storage_get_contact(StorageId storage_id, PublicKeyId key);
281Result<std::optional<Contact>> storage_get_contact_optimistic(StorageId storage_id, PublicKeyId key);
282
283Result<std::int64_t> storage_blockchain_height(StorageId storage_id);
284Result<StorageUpdates> storage_blockchain_apply_block(StorageId storage_id, Slice block);
285Result<Ok> storage_blockchain_add_proof(StorageId storage_id, Slice proof, const std::vector<std::string> &keys);
286
287Result<StorageBlockchainState> storage_get_blockchain_state(StorageId);
288
289using CallId = std::int64_t;
290using CallChannelId = std::int32_t;
292 UserId user_id{};
293 PublicKeyId public_key_id{};
294 int permissions{};
295};
296
297struct CallState {
298 int height{};
299 std::vector<CallParticipant> participants;
300};
301
302Result<Bytes> call_create_zero_block(PrivateKeyId private_key_id, const CallState &initial_state);
303Result<Bytes> call_create_self_add_block(PrivateKeyId private_key_id, Slice previous_block,
304 const CallParticipant &self);
305Result<CallId> call_create(UserId user_id, PrivateKeyId private_key_id, Slice last_block);
306
307Result<std::string> call_describe(CallId call);
308Result<std::string> call_describe_block(Slice block);
309Result<std::string> call_describe_message(Slice message);
310
311Result<Bytes> call_create_change_state_block(CallId call_id, const CallState &new_state);
312Result<Bytes> call_encrypt(CallId call_id, CallChannelId channel_id, SecureSlice message,
313 size_t unencrypted_prefix_size);
314Result<SecureBytes> call_decrypt(CallId call_id, UserId user_id, CallChannelId channel_id, Slice message);
315
316Result<int> call_get_height(CallId call_id);
317Result<CallState> call_apply_block(CallId call_id, Slice block);
318
319Result<CallState> call_get_state(CallId call_id);
320
322 int height{};
323 std::optional<Bytes> emoji_hash;
324};
325Result<CallVerificationState> call_get_verification_state(CallId call_id);
326Result<CallVerificationState> call_receive_inbound_message(CallId call_id, Slice message);
327
328// should be called after:
329// - creation
330// - call_apply_block
331// - call_receive_inbound_messages
332Result<std::vector<Bytes>> call_pull_outbound_messages(CallId call_id);
333
335 int height{};
336 std::vector<std::string> words;
337};
338
339Result<CallVerificationWords> call_get_verification_words(CallId call_id);
340Result<Ok> call_destroy(CallId call_id);
341Result<Ok> call_destroy_all();
342
343} // namespace tde2e_api
Definition e2e_api.h:192
Definition e2e_api.h:206