fix(chat): commit avatar updates before notification and validate SQL reads

This commit is contained in:
evgeny committed 2026-10-27 05:08:31 +01:00
1 parent bc41bb68b6
commit e0598ccc22
3 files changed
+212 -108

No files matched your search

+40 -27
View File
@@ -10,6 +10,7 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/crypto.h>
#define PEERS_JOIN_SIG_SIZE 64
#define PEERS_JOIN_TS_SIZE 8
@@ -984,38 +985,50 @@ done:
return result;
}
int topo_node_sqlite_channel_get_priv(sqlite3* db, const char* channel_id,
uint8_t* ed25519_priv_out) {
if (!db || !channel_id || !ed25519_priv_out) return -1;
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db,
"SELECT ed25519_privkey FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; }
const uint8_t* blob = (const uint8_t*)sqlite3_column_blob(stmt, 0);
if (!blob || sqlite3_column_bytes(stmt, 0) < 32) { sqlite3_finalize(stmt); return -1; }
memcpy(ed25519_priv_out, blob, 32);
sqlite3_finalize(stmt);
return 0;
/* Выходные ключи публикуются только после успешного завершения SELECT. */
static int channel_private_keys(sqlite3* db, const char* channel, uint8_t* x25519, uint8_t* ed25519) {
if (!db || !channel || !ed25519) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: missing argument"); return -1;
}
const char* sql = x25519 ? "SELECT x25519_privkey, ed25519_privkey FROM channels WHERE channel_id=?" :
"SELECT ed25519_privkey FROM channels WHERE channel_id=?";
sqlite3_stmt* st = NULL; uint8_t keys[64] = {0}; int columns = x25519 ? 2 : 1, result = -1;
const char* stage = "prepare"; int rc = sqlite3_prepare_v2(db, sql, -1, &st, NULL);
if (rc != SQLITE_OK) goto done;
stage = "bind"; rc = sqlite3_bind_text(st, 1, channel, -1, SQLITE_STATIC); if (rc != SQLITE_OK) goto done;
stage = "step"; rc = sqlite3_step(st); if (rc != SQLITE_ROW) goto done;
for (int i = 0; i < columns; i++) {
if (sqlite3_column_type(st, i) == SQLITE_NULL) { rc = SQLITE_DONE; goto done; }
stage = "key size/type"; rc = SQLITE_CORRUPT;
if (sqlite3_column_type(st, i) != SQLITE_BLOB || sqlite3_column_bytes(st, i) != 32) goto done;
const void* key = sqlite3_column_blob(st, i); if (!key) { rc = SQLITE_NOMEM; goto done; }
memcpy(keys + i * 32, key, 32);
}
rc = SQLITE_OK;
done:
{
int final = sqlite3_finalize(st);
if (rc == SQLITE_OK && final == SQLITE_OK) {
if (x25519) memcpy(x25519, keys, 32);
memcpy(ed25519, keys + (columns - 1) * 32, 32); result = 0;
} else if (rc == SQLITE_DONE && final == SQLITE_OK) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "channel private keys unavailable channel=%s", channel);
} else DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys channel=%s stage=%s rc=%d finalize=%d: %s",
channel, stage, rc, final, sqlite3_errmsg(db));
}
OPENSSL_cleanse(keys, sizeof(keys)); return result;
}
int topo_node_sqlite_channel_get_priv(sqlite3* db, const char* channel_id, uint8_t* ed25519_priv_out) {
return channel_private_keys(db, channel_id, NULL, ed25519_priv_out);
}
int topo_node_sqlite_channel_get_privs(sqlite3* db, const char* channel_id,
uint8_t* x25519_priv_out, uint8_t* ed25519_priv_out) {
if (!db || !channel_id || !x25519_priv_out || !ed25519_priv_out) return -1;
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db,
"SELECT x25519_privkey, ed25519_privkey FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; }
const uint8_t* x = (const uint8_t*)sqlite3_column_blob(stmt, 0);
const uint8_t* e = (const uint8_t*)sqlite3_column_blob(stmt, 1);
if (!x || sqlite3_column_bytes(stmt, 0) < 32 || !e || sqlite3_column_bytes(stmt, 1) < 32) {
sqlite3_finalize(stmt); return -1;
if (!x25519_priv_out) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: X25519 output missing"); return -1;
}
memcpy(x25519_priv_out, x, 32);
memcpy(ed25519_priv_out, e, 32);
sqlite3_finalize(stmt);
return 0;
return channel_private_keys(db, channel_id, x25519_priv_out, ed25519_priv_out);
}
/* Импорт пары: savepoint охватывает UPDATE и его завершение, внешний commit остаётся вызывающему. */
+4 -2
View File
@@ -122,11 +122,13 @@ int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
char* name_out, int name_sz, uint64_t* owner_node_id,
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature);
/* Прочитать первые 32 байта Ed25519 privkey; 0/-1, отсутствующий/короткий ключ даёт -1. */
/* Прочитать Ed25519 privkey ровно32 байта BLOB; 0/-1, отсутствие/повреждение/SQL-ошибка даёт -1.
* Проверяются bind/step/finalize; при отказе выход не меняется. */
int topo_node_sqlite_channel_get_priv(sqlite3* db, const char* channel_id,
uint8_t* ed25519_priv_out);
/* Прочитать оба канальных privkey (x25519 + ed25519). Возвращает 0 если оба есть. */
/* Прочитать оба канальных privkey (x25519 + ed25519), каждый ровно32 байта BLOB.
* 0 — оба прочитаны, -1 — отказ с сохранением обоих выходов. Временные ключи очищаются. */
int topo_node_sqlite_channel_get_privs(sqlite3* db, const char* channel_id,
uint8_t* x25519_priv_out, uint8_t* ed25519_priv_out);