Store signed group avatars without replacing channel keys
This commit is contained in:
1 parent
8173677475
commit
e3745f279e
3 files changed
+46
-2
No files matched your search
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user