docs(media): specify crypto and file transfer ownership contracts

This commit is contained in:
evgeny committed 2026-10-24 17:26:11 +02:00
1 parent 40e2fe2e27
commit 7ff8e1f15a
2 files changed
+47 -14

No files matched your search

+25 -8
View File
@@ -1,8 +1,10 @@
/* Общие AES-256-CCM примитивы. Файлы обрабатываются порциями с независимыми tags.
* nonce определяется author/UUID/part; UUID новый для каждой подготовки. Домен nonce
* utun_dm_media сохранён для существующего формата DM. Повторы используют готовый ciphertext.
* Файловые функции вызываются в worker; caller открывает файлы и публикует результат
* только после успешной проверки и durable сохранения. 0 = успех, -1 = ошибка. */
/* media_crypto — AES-256-CCM сообщений и файлов без БД, транспорта и владения файлами.
* Ключ 32 байта, nonce 13 байт, tag 16 байт; AAD нет. UUID новый для каждой подготовки
* с данным ключом/автором; сетевые повторы используют уже готовый ciphertext.
* FILE-функции вызываются в worker и работают с текущих позиций до EOF источника.
* Caller открывает отдельные src/dst, закрывает их и публикует результат только после
* успешной проверки и durable сохранения. Здесь есть fflush(dst), но нет close/fsync/rename.
* Ключи, буферы и FILE заимствованы на время вызова. int-функции: 0 = успех, -1 = ошибка. */
#ifndef MEDIA_CRYPTO_H
#define MEDIA_CRYPTO_H
#include <stdint.h>
@@ -16,16 +18,31 @@ extern "C" {
#define MEDIA_CIPHER_TAG_SIZE 16
#define MEDIA_CIPHER_PLAIN_MAX 65535u
#define MEDIA_CIPHER_CHUNK_SIZE 32768u
/* Первые 13 байт SHA256("utun_dm_media"[13] || author:uint64 || id:16 || part:uint64).
* Числа в native byte order текущего формата; id/nonce обязательны, проверок аргументов нет.
* part — номер порции с нуля; разным plaintext с тем же ключом нужен новый id. */
void media_cipher_nonce(uint64_t author, const uint8_t id[16], uint64_t part, uint8_t nonce[MEDIA_CIPHER_NONCE_SIZE]);
/* out вмещает plain_len+16; расшифровка проверяет tag до возврата plaintext. */
/* plain_len 0..65535, plain=NULL допустим только при нулевой длине; out вмещает plain_len+16.
* out/key/nonce/out_len обязательны; успех даёт ciphertext||tag и его длину.
* После проверки аргументов *out_len=0 до успеха; при неверных аргументах выходы не меняются. */
int media_ccm_encrypt(const uint8_t key[32], const uint8_t nonce[13], const uint8_t* plain,
size_t plain_len, uint8_t* out, size_t* out_len);
/* cipher_len 16..65551, out вмещает cipher_len-16 байт; все указатели обязательны.
* Только rc=0 разрешает использовать plaintext. При ошибке содержимое out не определено;
* после проверки аргументов *out_len=0 до успешной проверки tag. При неверных аргументах выходы не меняются. */
int media_ccm_decrypt(const uint8_t key[32], const uint8_t nonce[13], const uint8_t* cipher,
size_t cipher_len, uint8_t* out, size_t* out_len);
/* Поток plaintext → ciphertext; возвращает размер оригинала и SHA256 ciphertext. */
/* Порции по 32768 plaintext-байт, каждая с отдельным tag; пустой файл тоже даёт один tag.
* author!=0, все указатели обязательны, src!=dst; файлы должны обозначать разные данные.
* При корректных аргументах plain_size/hash обнуляются и выдаются только при успехе:
* фактический размер plaintext и SHA256 всего ciphertext, без внешнего заголовка контейнера.
* Ошибка может оставить частичный dst; выходные FILE не закрываются. */
int media_encrypt_stream(const uint8_t key[32], uint64_t author, const uint8_t id[16],
FILE* src, FILE* dst, uint64_t* plain_size, uint8_t hash[32]);
/* Проверяет каждый tag, точный размер и полный hash; dst должен быть временным. */
/* Те же требования к аргументам/файлам; plain_size/hash берутся из проверенного manifest.
* Проверяет каждый tag, точный размер (включая EOF) и полный hash ciphertext.
* dst должен быть собственным временным файлом: до общей проверки в него уже записываются порции.
* Частичный/непроверенный dst при ошибке отбрасывает вызывающий. */
int media_decrypt_stream(const uint8_t key[32], uint64_t author, const uint8_t id[16],
uint64_t plain_size, const uint8_t hash[32], FILE* src, FILE* dst);
#ifdef __cplusplus
+22 -6
View File
@@ -3,7 +3,11 @@
* и возвращает путь; получатель проверяет содержимое в своём done.
* Каждая операция владеет CM handle до завершения/отмены, включая DOWN/TIMEOUT.
* Все API/callbacks в uasync; destroy вызывается вне callbacks, до удаления групп.
* Файл назначения принадлежит вызывающему; при ошибке его нужно отбросить.
* До 16 входящих/исходящих операций суммарно. DATA до 8192 байт; router-пакеты подписаны,
* uint64 wire-поля имеют native byte order. Простой 30с завершает операцию ошибкой;
* при восстановлении пути/простое приём повторяет REQUEST с непрерывного полученного offset.
* Собственное временное назначение передаёт вызывающий: файл открывается с усечением,
* в том числе до возможного отказа запуска. При ошибке/cancel файл не удаляется и отбрасывается caller.
*/
#ifndef FILE_TRANSFER_H
#define FILE_TRANSFER_H
@@ -15,21 +19,33 @@ struct UTUN_INSTANCE;
struct file_transfer;
struct file_transfer_op;
/* Вызывается для каждого REQUEST, включая повторы: проверить права group/peer/object/size.
* 0 — записан NUL-терминированный путь (<cap байт) неизменяемого файла; иначе отказ.
* Файл сохранять доступным до завершения выдачи; перед отзывом отменить операции объекта. */
typedef int (*file_transfer_lookup_fn)(void* arg, uint64_t group, uint64_t peer,
const uint8_t object[16], uint64_t size, char* path, size_t cap);
typedef void (*file_transfer_done_fn)(void* arg, int error);
/* 0 в done означает точный приём size байт и закрытие файла, а не custody ACK.
* Возврат NULL означает отказ запуска, done не вызывается.
* cancel синхронно вызывает done(-2); ошибка fclose заменяет результат на -1, в том числе при отмене.
* Handle перестаёт существовать перед вызовом done, его нужно обнулить в callback. */
/* Один ft на instance; inst/его ua/router/группы живут дольше ft. lookup обязателен, arg заимствован.
* Создаёт router binding; возвращает owner либо NULL при ошибке. */
struct file_transfer* file_transfer_create(struct UTUN_INSTANCE* inst, file_transfer_lookup_fn lookup, void* arg);
/* Снимает binding и синхронно завершает активные pull как cancel; NULL допустим. */
void file_transfer_destroy(struct file_transfer* ft);
/* 0 в done означает точный приём size байт и закрытие файла, а не custody ACK.
* group/peer ненулевые, object указывает на 16 байт, size=1..INT64_MAX; done обязателен.
* destination — собственный временный путь; строка не сохраняется, файл открывается до возврата.
* Возвращает handle операции; done/arg заимствованы до завершения/отмены.
* Возврат NULL означает отказ запуска, done не вызывается.
* Ошибка приёма/таймаут даёт done(-1); успешный приём не включает fsync или проверку хеша.
* Handle перестаёт существовать перед вызовом done, его нужно обнулить в callback. */
struct file_transfer_op* file_transfer_pull(struct file_transfer* ft, uint64_t group, uint64_t peer,
const uint8_t object[16], uint64_t size, const char* destination,
file_transfer_done_fn done, void* arg);
/* Синхронный done(-2), ошибка fclose заменяет результат на -1. NULL допустим; прежний handle больше не использовать. */
void file_transfer_cancel(struct file_transfer_op* op);
/* Отозвать все операции объекта (TTL/delete); callbacks приёма вызываются синхронно с -2, при ошибке fclose — с -1. */
/* Отозвать все операции объекта во всех группах (TTL/delete); ft=NULL допустим, object обязателен.
* callbacks приёма вызываются синхронно с -2, при ошибке fclose — с -1. */
void file_transfer_cancel_object(struct file_transfer* ft, const uint8_t object[16]);
#endif