Browse Source

Store signed group avatars without replacing channel keys

master
evgeny 6 days ago
parent
commit
e3745f279e
  1. 2
      src/chat/chat_core_priv.h
  2. 30
      src/chat/group_avatar.h
  3. 16
      src/routing_layer/topo_node_sqlite.c

2
src/chat/chat_core_priv.h

@ -77,6 +77,7 @@ static inline size_t b64_decode(const char* src, size_t src_len, uint8_t* dst, s
struct ms_props_cbk; /* member_sync.c */
struct ms_apply_cbk; /* member_sync.c */
struct group_avatar;
struct chat_core_ctx {
struct UTUN_INSTANCE* inst;
@ -88,6 +89,7 @@ struct chat_core_ctx {
/* per-instance sub-module state (was file-scope statics) */
struct ms_props_cbk* props_cbks; /* was member_sync.c g_props_cbks */
struct ms_apply_cbk* apply_cbks; /* was member_sync.c g_apply_cbks */
struct group_avatar* group_avatar;
uint64_t media_cache_bytes; /* was chat_msg.c g_media_cache_bytes */
void* media_backfill_timer;/* was chat_msg.c g_media_startup_backfill_timer */
int media_backfill_ran; /* was chat_msg.c g_media_startup_backfill_ran */

30
src/chat/group_avatar.h

@ -0,0 +1,30 @@
/* Подписанная аватарка канала: SQLite, максимум 16 КиБ, router-блоки до 4 КиБ.
* Все API/callbacks — uasync; данные публикуются только после полной проверки подписи. */
#ifndef GROUP_AVATAR_H
#define GROUP_AVATAR_H
#include <stdint.h>
#include <stddef.h>
struct UTUN_INSTANCE;
#define GROUP_AVATAR_MAX 16384
#define GROUP_AVATAR_BLOCK 4096
struct group_avatar_record {
uint64_t version;
uint16_t size;
uint8_t hash[32], signature[64], data[GROUP_AVATAR_MAX];
};
int group_avatar_init(struct UTUN_INSTANCE* inst);
void group_avatar_destroy(struct UTUN_INSTANCE* inst);
void group_avatar_cancel_group(struct UTUN_INSTANCE* inst, uint64_t group);
int group_avatar_get(struct UTUN_INSTANCE* inst, uint64_t group, struct group_avatar_record* record);
/* 1=изменено, 0=прежняя/старая запись; <0 — ошибка. */
int group_avatar_apply(struct UTUN_INSTANCE* inst, uint64_t group, const struct group_avatar_record* record);
/* Публикация своей картинки требует приватный Ed25519 ключ канала. size=0 — подписанное удаление. */
int group_avatar_set(struct UTUN_INSTANCE* inst, uint64_t group, const uint8_t* data, size_t size);
struct group_avatar_gui_request {
struct UTUN_INSTANCE* inst;
uint64_t id, group;
uint16_t size;
uint8_t data[GROUP_AVATAR_MAX];
};
void group_avatar_gui_set(void* arg);
#endif

16
src/routing_layer/topo_node_sqlite.c

@ -295,6 +295,10 @@ int topo_node_sqlite_init(sqlite3* db) {
" ed25519_pubkey BLOB NOT NULL," // ключ для проверки подписи
" ed25519_privkey BLOB," // ключ для создания подписи
" signature BLOB NOT NULL," // подпись при создании канал приватным ключом канала channel_id(NUL-terminated) || name(NUL-terminated) || owner_node_id(8 байт, LE uint64) || x25519_pubkey(32 байта) || ed25519_pubkey(32 байта)
" avatar_version INTEGER NOT NULL DEFAULT 0,"
" avatar_data BLOB,"
" avatar_hash BLOB,"
" avatar_signature BLOB,"
" last_read_msg_id INTEGER," // для скроллинга чата
" last_pos_msg_id INTEGER," // для скроллинга чата
" last_msg_at INTEGER DEFAULT 0," // время последнего сообщения (unix timestamp)
@ -448,9 +452,14 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db,
"INSERT OR REPLACE INTO channels(channel_id, name, owner_node_id,"
"INSERT INTO channels(channel_id, name, owner_node_id,"
" x25519_pubkey, x25519_privkey, ed25519_pubkey, ed25519_privkey, signature)"
" VALUES(?,?,?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) return -1;
" VALUES(?,?,?,?,?,?,?,?) ON CONFLICT(channel_id) DO UPDATE SET"
" name=excluded.name,owner_node_id=excluded.owner_node_id,signature=excluded.signature,"
" x25519_privkey=COALESCE(excluded.x25519_privkey,channels.x25519_privkey),"
" ed25519_privkey=COALESCE(excluded.ed25519_privkey,channels.ed25519_privkey)"
" WHERE channels.x25519_pubkey=excluded.x25519_pubkey AND channels.ed25519_pubkey=excluded.ed25519_pubkey",
-1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)owner_node_id);
@ -462,6 +471,9 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
else sqlite3_bind_null(stmt, 7);
sqlite3_bind_blob(stmt, 8, signature, 64, SQLITE_STATIC);
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
if (rc == SQLITE_DONE && !sqlite3_changes(db)) {
DEBUG_WARN(DEBUG_CATEGORY_CHAT, "channel: rejected identity change channel=%s", channel_id); return -1;
}
return rc == SQLITE_DONE ? 0 : -1;
}

Loading…
Cancel
Save