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

458 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}