Make streaming signature initialization and cleanup safe
This commit is contained in:
1 parent
4824f3523d
commit
5d11f90cd4
2 files changed
+68
-29
No files matched your search
@@ -397,42 +397,57 @@ uint64_t sc_derive_node_id_from_pubkey(const uint8_t *public_key) {
|
||||
return node_id;
|
||||
}
|
||||
|
||||
// Создаёт ресурсы подписи локально; публикует state только после полной инициализации SHA-512.
|
||||
sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state) {
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->initialized || !ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "ctx not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
memset(state, 0, sizeof(*state));
|
||||
if (sc_derive_ed25519_key(ctx->pk->private_key, (EVP_PKEY**)&state->pkey) != SC_OK) return SC_ERR_CRYPTO;
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestInit_ex failed");
|
||||
EVP_MD_CTX_free(md_ctx); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO;
|
||||
if (state->initialized) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream sign init: state=%p already active, mode=%s", (void *)state, state->is_sign ? "sign" : "verify");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
state->md_ctx = md_ctx;
|
||||
state->is_sign = 1;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "ok");
|
||||
EVP_PKEY *pkey = NULL;
|
||||
EVP_MD_CTX *md_ctx = NULL;
|
||||
if (sc_derive_ed25519_key(ctx->pk->private_key, &pkey) != SC_OK) return SC_ERR_CRYPTO;
|
||||
md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream sign init: EVP_MD_CTX_new failed"); goto fail; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream sign init: EVP_DigestInit_ex failed");
|
||||
goto fail;
|
||||
}
|
||||
*state = (struct sc_stream_sign_state){.md_ctx = md_ctx, .pkey = pkey, .initialized = 1, .is_sign = 1};
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CRYPTO, "stream sign init: state=%p mode=sign", (void *)state);
|
||||
return SC_OK;
|
||||
fail:
|
||||
EVP_MD_CTX_free(md_ctx);
|
||||
EVP_PKEY_free(pkey);
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
|
||||
// Создаёт ресурсы проверки локально; при отказе пустой state не меняется.
|
||||
sc_status_t sc_stream_sign_verify_init(struct sc_stream_sign_state *state, const uint8_t *ed25519_pubkey) {
|
||||
if (!state || !ed25519_pubkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
if (!state->pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestInit_ex failed");
|
||||
EVP_MD_CTX_free(md_ctx); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO;
|
||||
if (state->initialized) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream verify init: state=%p already active, mode=%s", (void *)state, state->is_sign ? "sign" : "verify");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
state->md_ctx = md_ctx;
|
||||
state->is_sign = 0;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "ok");
|
||||
EVP_PKEY *pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
if (!pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream verify init: EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream verify init: EVP_MD_CTX_new failed"); goto fail; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream verify init: EVP_DigestInit_ex failed");
|
||||
goto fail;
|
||||
}
|
||||
*state = (struct sc_stream_sign_state){.md_ctx = md_ctx, .pkey = pkey, .initialized = 1, .is_sign = 0};
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CRYPTO, "stream verify init: state=%p mode=verify", (void *)state);
|
||||
return SC_OK;
|
||||
fail:
|
||||
EVP_MD_CTX_free(md_ctx);
|
||||
EVP_PKEY_free(pkey);
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
|
||||
// Добавляет фрагмент в SHA-512; нулевая длина ничего не меняет.
|
||||
sc_status_t sc_stream_sign_update(struct sc_stream_sign_state *state, const uint8_t *data, size_t len) {
|
||||
if (len == 0) return SC_OK;
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
@@ -444,9 +459,14 @@ sc_status_t sc_stream_sign_update(struct sc_stream_sign_state *state, const uint
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
// Проверяет выходной буфер до финализации; начатая финализация всегда освобождает state.
|
||||
sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *sig_out, size_t *sig_len) {
|
||||
if (!state || !sig_out || !sig_len) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || !state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "not initialized for sign"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (*sig_len < SC_SIGN_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream sign final: capacity=%zu requires=%u", *sig_len, (unsigned)SC_SIGN_SIZE);
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t hash[EVP_MAX_MD_SIZE]; unsigned hash_len = sizeof(hash);
|
||||
if (EVP_DigestFinal_ex((EVP_MD_CTX *)state->md_ctx, hash, &hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestFinal_ex failed");
|
||||
@@ -464,6 +484,7 @@ sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *si
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
// Проверяет подпись накопленного хеша; после начала финализации освобождает state при любом результате.
|
||||
sc_status_t sc_stream_sign_verify(struct sc_stream_sign_state *state, const uint8_t *sig, size_t sig_len) {
|
||||
if (!state || !sig) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "not initialized for verify"); return SC_ERR_NOT_INITIALIZED; }
|
||||
@@ -484,11 +505,16 @@ sc_status_t sc_stream_sign_verify(struct sc_stream_sign_state *state, const uint
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
// Освобождает принадлежащие state ресурсы и возвращает его в полностью пустое состояние.
|
||||
void sc_stream_sign_cleanup(struct sc_stream_sign_state *state) {
|
||||
if (!state) return;
|
||||
if (state->md_ctx) { EVP_MD_CTX_free((EVP_MD_CTX *)state->md_ctx); state->md_ctx = NULL; }
|
||||
if (state->pkey) { EVP_PKEY_free((EVP_PKEY *)state->pkey); state->pkey = NULL; }
|
||||
state->initialized = 0;
|
||||
if (state->md_ctx || state->pkey) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CRYPTO, "stream sign cleanup: state=%p active=%u mode=%s",
|
||||
(void *)state, (unsigned)state->initialized, state->is_sign ? "sign" : "verify");
|
||||
}
|
||||
EVP_MD_CTX_free((EVP_MD_CTX *)state->md_ctx);
|
||||
EVP_PKEY_free((EVP_PKEY *)state->pkey);
|
||||
*state = (struct sc_stream_sign_state){0};
|
||||
}
|
||||
|
||||
// ── One-shot Ed25519 helpers (raw keys, any context) ──
|
||||
|
||||
@@ -104,22 +104,35 @@ void sc_stream_cleanup(struct sc_stream_state *state);
|
||||
// --- Streaming Ed25519 signature ---
|
||||
// SHA-512 инкрементально (EVP_DigestUpdate) + Ed25519 подпись хеша (one-shot EVP_DigestSign).
|
||||
// Память O(1), не зависит от размера данных.
|
||||
// Перед первым использованием state = {0}; активный state имеет одного владельца и не копируется.
|
||||
|
||||
#define SC_SIGN_SIZE 64 // Ed25519 signature size
|
||||
|
||||
struct sc_stream_sign_state {
|
||||
void *md_ctx; // EVP_MD_CTX* — SHA-512 аккумулятор
|
||||
void *pkey; // EVP_PKEY* — Ed25519 ключ
|
||||
uint8_t initialized;
|
||||
uint8_t is_sign; // 1=sign, 0=verify
|
||||
uint8_t initialized; // 1 — ресурсы готовы; 0 — пустое состояние
|
||||
uint8_t is_sign; // В активном состоянии 1=sign, 0=verify; после cleanup обнуляется
|
||||
};
|
||||
|
||||
/* init — только для пустого state. Ошибка оставляет его пустым; повторный init активного state
|
||||
* возвращает SC_ERR_INVALID_ARG без изменения ресурсов и хеша. Оба init: SC_OK / отрицательный SC_ERR_*.
|
||||
* Подпись использует ключ из initialized ctx->pk; peer/ECDH не требуются, ctx нужен только на время init. */
|
||||
sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state);
|
||||
/* ed25519_pubkey — обязательный бинарный ключ[32], копируется OpenSSL во внутренний ресурс state. */
|
||||
sc_status_t sc_stream_sign_verify_init(struct sc_stream_sign_state *state, const uint8_t *ed25519_pubkey);
|
||||
/* Добавляет data[len] в активный state; len=0 — no-op, допускает NULL. SC_OK / отрицательный SC_ERR_*.
|
||||
* При ошибке EVP update владелец должен вызвать cleanup; продолжение хеширования не гарантируется. */
|
||||
sc_status_t sc_stream_sign_update(struct sc_stream_sign_state *state, const uint8_t *data, size_t len);
|
||||
/* sig_len задаёт capacity sig_out (не менее 64), при успехе получает длину 64; завершение освобождает sign state. */
|
||||
/* sig_len задаёт capacity sig_out (не менее 64), при успехе получает длину 64. SC_OK / отрицательный SC_ERR_*.
|
||||
* NULL аргументы, недостаточная capacity и неверный режим оставляют state и выходные буферы без изменений.
|
||||
* После начала финализации освобождает sign state при любом результате; sig_out/sig_len действительны при SC_OK. */
|
||||
sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *sig_out, size_t *sig_len);
|
||||
/* Проверяет sig[sig_len] для накопленного хеша в режиме verify; обычная подпись имеет длину 64.
|
||||
* SC_OK / отрицательный SC_ERR_*; NULL аргументы/неверный режим сохраняют state,
|
||||
* после начала финализации state освобождается при любом результате, включая неверную подпись. */
|
||||
sc_status_t sc_stream_sign_verify(struct sc_stream_sign_state *state, const uint8_t *sig, size_t sig_len);
|
||||
/* Освобождает ресурсы и обнуляет все поля; допускает NULL, пустой state и повторные вызовы. */
|
||||
void sc_stream_sign_cleanup(struct sc_stream_sign_state *state);
|
||||
|
||||
sc_status_t sc_ed25519_sign(const uint8_t privkey[32], const uint8_t* msg, size_t msg_len, uint8_t sig_out[64]);
|
||||
|
||||
Reference in new issue
Block a user