montana/Монтана-Протокол/Код/crates/mt-crypto-native/csrc/mt_crypto.c

458 lines
12 KiB
C
Raw Normal View History

#include "mt_crypto.h"
#include <openssl/core_names.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/params.h>
#include <string.h>
static int extract_octet_param(
EVP_PKEY* pkey,
const char* param_name,
uint8_t* out,
size_t expected_len
) {
size_t actual_len = 0;
if (EVP_PKEY_get_octet_string_param(pkey, param_name, NULL, 0, &actual_len) != 1) {
return MT_ERR_PARAM_QUERY_FAILED;
}
if (actual_len != expected_len) {
return MT_ERR_PARAM_SIZE_MISMATCH;
}
if (EVP_PKEY_get_octet_string_param(pkey, param_name, out, expected_len, &actual_len) != 1) {
return MT_ERR_PARAM_FETCH_FAILED;
}
if (actual_len != expected_len) {
return MT_ERR_PARAM_SIZE_MISMATCH;
}
return MT_OK;
}
static int keypair_from_seed_generic(
const char* alg_name,
const char* seed_param_name,
const uint8_t* seed,
size_t seed_len,
uint8_t* pk_out,
size_t pk_len,
uint8_t* sk_out,
size_t sk_len
) {
if (seed == NULL || pk_out == NULL || sk_out == NULL) {
return MT_ERR_INVALID_INPUT;
}
int rc = MT_ERR_KEYGEN_FAILED;
EVP_PKEY_CTX* ctx = NULL;
EVP_PKEY* pkey = NULL;
OSSL_PARAM params[2];
ctx = EVP_PKEY_CTX_new_from_name(NULL, alg_name, NULL);
if (ctx == NULL) {
rc = MT_ERR_OPENSSL_INIT;
goto cleanup;
}
if (EVP_PKEY_keygen_init(ctx) != 1) {
goto cleanup;
}
/*
* OpenSSL EVP convention: OSSL_PARAM_construct_octet_string takes (void*)
* для backwards compat с pre-const-correct C API; OSSL_PARAM_set_*
* семантически НЕ модифицирует input bytes только читаются для
* передачи в KeyGen / Sign / fromdata. Cast (void*)seed convention,
* не actual mutation. Same pattern на всех 4 octet_string call sites.
*/
params[0] = OSSL_PARAM_construct_octet_string(
seed_param_name, (void*)seed, seed_len
);
params[1] = OSSL_PARAM_construct_end();
if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
goto cleanup;
}
if (EVP_PKEY_generate(ctx, &pkey) != 1) {
goto cleanup;
}
if (pkey == NULL) {
goto cleanup;
}
rc = extract_octet_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pk_out, pk_len);
if (rc != MT_OK) {
goto cleanup;
}
rc = extract_octet_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, sk_out, sk_len);
if (rc != MT_OK) {
goto cleanup;
}
rc = MT_OK;
cleanup:
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
if (ctx != NULL) {
EVP_PKEY_CTX_free(ctx);
}
return rc;
}
int mt_keypair_from_seed_mldsa(
const uint8_t seed[MT_MLDSA65_SEED_SIZE],
uint8_t pk_out[MT_MLDSA65_PUBKEY_SIZE],
uint8_t sk_out[MT_MLDSA65_SECRETKEY_SIZE]
) {
return keypair_from_seed_generic(
"ML-DSA-65",
OSSL_PKEY_PARAM_ML_DSA_SEED,
seed,
MT_MLDSA65_SEED_SIZE,
pk_out,
MT_MLDSA65_PUBKEY_SIZE,
sk_out,
MT_MLDSA65_SECRETKEY_SIZE
);
}
int mt_keypair_from_seed_mlkem(
const uint8_t seed[MT_MLKEM768_SEED_SIZE],
uint8_t pk_out[MT_MLKEM768_PUBKEY_SIZE],
uint8_t sk_out[MT_MLKEM768_SECRETKEY_SIZE]
) {
return keypair_from_seed_generic(
"ML-KEM-768",
OSSL_PKEY_PARAM_ML_KEM_SEED,
seed,
MT_MLKEM768_SEED_SIZE,
pk_out,
MT_MLKEM768_PUBKEY_SIZE,
sk_out,
MT_MLKEM768_SECRETKEY_SIZE
);
}
static EVP_PKEY* mldsa_pkey_from_secret(
const uint8_t* sk,
size_t sk_len
) {
EVP_PKEY_CTX* ctx = NULL;
EVP_PKEY* pkey = NULL;
OSSL_PARAM params[2];
ctx = EVP_PKEY_CTX_new_from_name(NULL, "ML-DSA-65", NULL);
if (ctx == NULL) {
return NULL;
}
if (EVP_PKEY_fromdata_init(ctx) != 1) {
goto fail;
}
/* OpenSSL EVP convention: (void*)sk — backwards compat cast, не mutation
* (см. extended comment в keypair_from_seed_generic). */
params[0] = OSSL_PARAM_construct_octet_string(
OSSL_PKEY_PARAM_PRIV_KEY, (void*)sk, sk_len
);
params[1] = OSSL_PARAM_construct_end();
if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) != 1) {
pkey = NULL;
goto fail;
}
fail:
EVP_PKEY_CTX_free(ctx);
return pkey;
}
static EVP_PKEY* mldsa_pkey_from_public(
const uint8_t* pk,
size_t pk_len
) {
EVP_PKEY_CTX* ctx = NULL;
EVP_PKEY* pkey = NULL;
OSSL_PARAM params[2];
ctx = EVP_PKEY_CTX_new_from_name(NULL, "ML-DSA-65", NULL);
if (ctx == NULL) {
return NULL;
}
if (EVP_PKEY_fromdata_init(ctx) != 1) {
goto fail;
}
/* OpenSSL EVP convention: (void*)pk — backwards compat cast, не mutation
* (см. extended comment в keypair_from_seed_generic). */
params[0] = OSSL_PARAM_construct_octet_string(
OSSL_PKEY_PARAM_PUB_KEY, (void*)pk, pk_len
);
params[1] = OSSL_PARAM_construct_end();
if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) != 1) {
pkey = NULL;
goto fail;
}
fail:
EVP_PKEY_CTX_free(ctx);
return pkey;
}
int mt_sign_mldsa(
const uint8_t sk[MT_MLDSA65_SECRETKEY_SIZE],
const uint8_t* msg,
size_t msg_len,
uint8_t sig_out[MT_MLDSA65_SIGNATURE_SIZE]
) {
if (sk == NULL || sig_out == NULL || (msg == NULL && msg_len != 0)) {
return MT_ERR_INVALID_INPUT;
}
int rc = MT_ERR_SIGN_FAILED;
EVP_PKEY* pkey = NULL;
EVP_MD_CTX* md_ctx = NULL;
OSSL_PARAM sig_params[2];
int deterministic = 1;
pkey = mldsa_pkey_from_secret(sk, MT_MLDSA65_SECRETKEY_SIZE);
if (pkey == NULL) {
rc = MT_ERR_INVALID_SECRET_KEY;
goto cleanup;
}
md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL) {
rc = MT_ERR_OPENSSL_INIT;
goto cleanup;
}
sig_params[0] = OSSL_PARAM_construct_int(
OSSL_SIGNATURE_PARAM_DETERMINISTIC, &deterministic
);
sig_params[1] = OSSL_PARAM_construct_end();
if (EVP_DigestSignInit_ex(md_ctx, NULL, NULL, NULL, NULL, pkey, sig_params) != 1) {
rc = MT_ERR_SIGN_FAILED;
goto cleanup;
}
size_t sig_len = MT_MLDSA65_SIGNATURE_SIZE;
if (EVP_DigestSign(md_ctx, sig_out, &sig_len, msg, msg_len) != 1) {
rc = MT_ERR_SIGN_FAILED;
goto cleanup;
}
if (sig_len != MT_MLDSA65_SIGNATURE_SIZE) {
rc = MT_ERR_SIGN_LENGTH_MISMATCH;
goto cleanup;
}
rc = MT_OK;
cleanup:
if (md_ctx != NULL) {
EVP_MD_CTX_free(md_ctx);
}
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return rc;
}
int mt_sign_mldsa_ctx(
const uint8_t sk[MT_MLDSA65_SECRETKEY_SIZE],
const uint8_t* msg, size_t msg_len,
const uint8_t* ctx, size_t ctx_len,
uint8_t sig_out[MT_MLDSA65_SIGNATURE_SIZE]
) {
if (sk == NULL || sig_out == NULL || (msg == NULL && msg_len != 0)
|| (ctx == NULL && ctx_len != 0)) {
return MT_ERR_INVALID_INPUT;
}
/* FIPS 204: context length max 255 bytes. */
if (ctx_len > 255) {
return MT_ERR_INVALID_INPUT;
}
int rc = MT_ERR_SIGN_FAILED;
EVP_PKEY* pkey = NULL;
EVP_MD_CTX* md_ctx = NULL;
OSSL_PARAM sig_params[3];
int deterministic = 1;
pkey = mldsa_pkey_from_secret(sk, MT_MLDSA65_SECRETKEY_SIZE);
if (pkey == NULL) {
rc = MT_ERR_INVALID_SECRET_KEY;
goto cleanup;
}
md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL) {
rc = MT_ERR_OPENSSL_INIT;
goto cleanup;
}
sig_params[0] = OSSL_PARAM_construct_int(
OSSL_SIGNATURE_PARAM_DETERMINISTIC, &deterministic
);
/* OpenSSL EVP convention: (void*)ctx — backwards compat cast, не mutation
* (см. extended comment в keypair_from_seed_generic). FIPS 204 ctx_len 255
* проверен выше (line 268-270). */
sig_params[1] = OSSL_PARAM_construct_octet_string(
OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (void*)ctx, ctx_len
);
sig_params[2] = OSSL_PARAM_construct_end();
if (EVP_DigestSignInit_ex(md_ctx, NULL, NULL, NULL, NULL, pkey, sig_params) != 1) {
rc = MT_ERR_SIGN_FAILED;
goto cleanup;
}
size_t sig_len = MT_MLDSA65_SIGNATURE_SIZE;
if (EVP_DigestSign(md_ctx, sig_out, &sig_len, msg, msg_len) != 1) {
rc = MT_ERR_SIGN_FAILED;
goto cleanup;
}
if (sig_len != MT_MLDSA65_SIGNATURE_SIZE) {
rc = MT_ERR_SIGN_LENGTH_MISMATCH;
goto cleanup;
}
rc = MT_OK;
cleanup:
if (md_ctx != NULL) {
EVP_MD_CTX_free(md_ctx);
}
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return rc;
}
int mt_verify_mldsa(
const uint8_t pk[MT_MLDSA65_PUBKEY_SIZE],
const uint8_t* msg,
size_t msg_len,
const uint8_t sig[MT_MLDSA65_SIGNATURE_SIZE]
) {
if (pk == NULL || sig == NULL || (msg == NULL && msg_len != 0)) {
return MT_ERR_INVALID_INPUT;
}
int rc = MT_ERR_VERIFY_FAILED;
EVP_PKEY* pkey = NULL;
EVP_MD_CTX* md_ctx = NULL;
pkey = mldsa_pkey_from_public(pk, MT_MLDSA65_PUBKEY_SIZE);
if (pkey == NULL) {
rc = MT_ERR_INVALID_PUBLIC_KEY;
goto cleanup;
}
md_ctx = EVP_MD_CTX_new();
if (md_ctx == NULL) {
rc = MT_ERR_OPENSSL_INIT;
goto cleanup;
}
if (EVP_DigestVerifyInit_ex(md_ctx, NULL, NULL, NULL, NULL, pkey, NULL) != 1) {
rc = MT_ERR_VERIFY_FAILED;
goto cleanup;
}
int verify_rc = EVP_DigestVerify(md_ctx, sig, MT_MLDSA65_SIGNATURE_SIZE, msg, msg_len);
if (verify_rc == 1) {
rc = MT_OK;
} else {
rc = MT_ERR_VERIFY_FAILED;
}
cleanup:
if (md_ctx != NULL) {
EVP_MD_CTX_free(md_ctx);
}
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return rc;
}
int mt_self_test(void) {
uint8_t mldsa_seed[MT_MLDSA65_SEED_SIZE] = {0};
uint8_t mldsa_pk[MT_MLDSA65_PUBKEY_SIZE];
uint8_t mldsa_sk[MT_MLDSA65_SECRETKEY_SIZE];
uint8_t mldsa_pk2[MT_MLDSA65_PUBKEY_SIZE];
uint8_t mldsa_sk2[MT_MLDSA65_SECRETKEY_SIZE];
int rc = mt_keypair_from_seed_mldsa(mldsa_seed, mldsa_pk, mldsa_sk);
if (rc != MT_OK) {
return rc;
}
rc = mt_keypair_from_seed_mldsa(mldsa_seed, mldsa_pk2, mldsa_sk2);
if (rc != MT_OK) {
return rc;
}
if (memcmp(mldsa_pk, mldsa_pk2, MT_MLDSA65_PUBKEY_SIZE) != 0) {
return MT_ERR_KAT_MISMATCH;
}
if (memcmp(mldsa_sk, mldsa_sk2, MT_MLDSA65_SECRETKEY_SIZE) != 0) {
return MT_ERR_KAT_MISMATCH;
}
static const uint8_t test_msg[] = {0x01, 0x02, 0x03, 0x04};
uint8_t sig[MT_MLDSA65_SIGNATURE_SIZE];
rc = mt_sign_mldsa(mldsa_sk, test_msg, sizeof(test_msg), sig);
if (rc != MT_OK) {
return rc;
}
rc = mt_verify_mldsa(mldsa_pk, test_msg, sizeof(test_msg), sig);
if (rc != MT_OK) {
return rc;
}
uint8_t sig2[MT_MLDSA65_SIGNATURE_SIZE];
rc = mt_sign_mldsa(mldsa_sk, test_msg, sizeof(test_msg), sig2);
if (rc != MT_OK) {
return rc;
}
if (memcmp(sig, sig2, MT_MLDSA65_SIGNATURE_SIZE) != 0) {
return MT_ERR_KAT_MISMATCH;
}
static const uint8_t bad_msg[] = {0x01, 0x02, 0x03, 0x05};
rc = mt_verify_mldsa(mldsa_pk, bad_msg, sizeof(bad_msg), sig);
if (rc == MT_OK) {
return MT_ERR_KAT_MISMATCH;
}
uint8_t mlkem_seed[MT_MLKEM768_SEED_SIZE] = {0};
uint8_t mlkem_pk[MT_MLKEM768_PUBKEY_SIZE];
uint8_t mlkem_sk[MT_MLKEM768_SECRETKEY_SIZE];
uint8_t mlkem_pk2[MT_MLKEM768_PUBKEY_SIZE];
uint8_t mlkem_sk2[MT_MLKEM768_SECRETKEY_SIZE];
rc = mt_keypair_from_seed_mlkem(mlkem_seed, mlkem_pk, mlkem_sk);
if (rc != MT_OK) {
return rc;
}
rc = mt_keypair_from_seed_mlkem(mlkem_seed, mlkem_pk2, mlkem_sk2);
if (rc != MT_OK) {
return rc;
}
if (memcmp(mlkem_pk, mlkem_pk2, MT_MLKEM768_PUBKEY_SIZE) != 0) {
return MT_ERR_KAT_MISMATCH;
}
if (memcmp(mlkem_sk, mlkem_sk2, MT_MLKEM768_SECRETKEY_SIZE) != 0) {
return MT_ERR_KAT_MISMATCH;
}
return MT_OK;
}