2024-08-21 12:47:16 +03:00
|
|
|
#include "cryptopro.h"
|
|
|
|
|
|
|
|
|
|
#include "base64.h"
|
|
|
|
|
#include "capi.h"
|
|
|
|
|
#include "library.h"
|
|
|
|
|
#include "logger.h"
|
|
|
|
|
|
2024-12-02 12:02:24 +03:00
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
static capi_function_list_t cp_function_list;
|
|
|
|
|
static library_t libcapi;
|
|
|
|
|
|
|
|
|
|
static int sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign);
|
|
|
|
|
|
|
|
|
|
static int reverse_sign(const str_t *sign, /*out*/ str_t *sign_reversed);
|
|
|
|
|
|
|
|
|
|
static HCERTSTORE cert_open_store();
|
2025-02-07 12:19:45 +03:00
|
|
|
static void cert_close_store(HCERTSTORE hStoreHandle);
|
2024-08-21 12:47:16 +03:00
|
|
|
|
|
|
|
|
static PCCERT_CONTEXT get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle);
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
static int set_password(PCCERT_CONTEXT cert_ctx, const str_t *password);
|
2024-08-21 12:47:16 +03:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
cryptopro_init(const char* cp_file)
|
|
|
|
|
{
|
|
|
|
|
static bool is_initialized = false;
|
|
|
|
|
|
|
|
|
|
if (!is_initialized) {
|
|
|
|
|
library_init(&libcapi, cp_file);
|
|
|
|
|
if (!capi_function_list_init(&libcapi, &cp_function_list)) {
|
|
|
|
|
LOG_ERROR("capi_function_list_init failed");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
is_initialized = true;
|
|
|
|
|
LOG_INFO("Cryptographic Provider initialized");
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cryptopro_sign(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign)
|
|
|
|
|
{
|
|
|
|
|
str_t signed_data = str_t_null;
|
|
|
|
|
str_t sign_reversed = str_t_null;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("cryptopro_sign enter");
|
|
|
|
|
|
|
|
|
|
assert(ctx != NULL);
|
2024-11-14 12:16:55 +03:00
|
|
|
assert(!str_t_is_null(*ctx->cert_thumbprint));
|
|
|
|
|
assert(ctx->password != NULL);
|
2024-08-21 12:47:16 +03:00
|
|
|
assert(data != NULL && !str_t_is_null(*data));
|
|
|
|
|
assert(sign != NULL);
|
|
|
|
|
|
|
|
|
|
if (sign_hash_data(ctx, data, &signed_data)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reverse_sign(&signed_data, &sign_reversed)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sign->len = base64_encoded_length(sign_reversed.len);
|
|
|
|
|
sign->data = calloc(1, sign->len + 1);
|
|
|
|
|
if (sign->data == NULL) {
|
|
|
|
|
LOG_ERROR("Could not allocate memory for sign (%zu bytes)", sign->len + 1);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
encode_base64_url(sign, &sign_reversed);
|
|
|
|
|
|
|
|
|
|
str_t_clear(&signed_data);
|
|
|
|
|
str_t_clear(&sign_reversed);
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("cryptopro_sign exit");
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
str_t_clear(&signed_data);
|
|
|
|
|
str_t_clear(&sign_reversed);
|
2025-01-21 10:00:17 +03:00
|
|
|
LOG_ERROR("cryptopro_sign exit with error (sign_cert_thumbprint = '%.*s')",
|
|
|
|
|
(int) ctx->cert_thumbprint->len, ctx->cert_thumbprint->data);
|
2024-08-21 12:47:16 +03:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-25 13:06:44 +03:00
|
|
|
static void
|
|
|
|
|
free_cert_chain(PCCERT_CHAIN_CONTEXT chain_ctx)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("free_cert_chain enter");
|
|
|
|
|
|
|
|
|
|
if (chain_ctx) {
|
|
|
|
|
cp_function_list.CertFreeCertificateChain(chain_ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("free_cert_chain exit");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PCCERT_CHAIN_CONTEXT
|
|
|
|
|
get_cert_chain(PCCERT_CONTEXT certificate)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("get_cert_chain enter");
|
|
|
|
|
|
|
|
|
|
CERT_CHAIN_PARA chain_para = {0};
|
|
|
|
|
chain_para.cbSize = sizeof(CERT_CHAIN_PARA);
|
|
|
|
|
|
|
|
|
|
PCCERT_CHAIN_CONTEXT chain_ctx = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CertGetCertificateChain(NULL,
|
|
|
|
|
certificate,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&chain_para,
|
|
|
|
|
(CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT),
|
|
|
|
|
NULL,
|
|
|
|
|
&chain_ctx)) {
|
|
|
|
|
LOG_ERROR("CertGetCertificateChain() failed");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chain_ctx->TrustStatus.dwErrorStatus) {
|
|
|
|
|
LOG_WARN("The certificate is not trusted. CERT_TRUST_STATUS: '0x%08x'",
|
|
|
|
|
chain_ctx->TrustStatus.dwErrorStatus);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("get_cert_chain exit");
|
|
|
|
|
return chain_ctx;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
free_cert_chain(chain_ctx);
|
|
|
|
|
LOG_ERROR("get_cert_chain exit with error");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
check_cert_chain_policy(PCCERT_CHAIN_CONTEXT chain_ctx)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("check_cert_chain_policy enter");
|
|
|
|
|
|
|
|
|
|
bool is_valid = false;
|
|
|
|
|
|
|
|
|
|
CERT_CHAIN_POLICY_PARA policy_para = {0};
|
|
|
|
|
policy_para.cbSize = sizeof(policy_para);
|
|
|
|
|
|
|
|
|
|
CERT_CHAIN_POLICY_STATUS status = {0};
|
|
|
|
|
status.cbSize = sizeof(status);
|
|
|
|
|
|
|
|
|
|
CPSIGNATURE_EXTRA_CERT_CHAIN_POLICY_STATUS extraStatus = {0};
|
|
|
|
|
extraStatus.cbSize = sizeof(extraStatus);
|
|
|
|
|
|
|
|
|
|
status.pvExtraPolicyStatus = &extraStatus;
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CertVerifyCertificateChainPolicy(
|
2024-12-28 14:17:09 +03:00
|
|
|
CERT_CHAIN_POLICY_BASE,
|
2024-11-25 13:06:44 +03:00
|
|
|
chain_ctx,
|
|
|
|
|
&policy_para,
|
|
|
|
|
&status)) {
|
|
|
|
|
LOG_ERROR("CertVerifyCertificateChainPolicy() failed. Error code: 0x%08x",
|
|
|
|
|
cp_function_list.GetLastError());
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status.dwError != 0) {
|
|
|
|
|
LOG_WARN("The certificate chain cannot be validated. "
|
|
|
|
|
"CERT_CHAIN_POLICY_STATUS: '0x%08x'", status.dwError);
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (extraStatus.dwError != 0) {
|
|
|
|
|
LOG_WARN("The certificate chain cannot be validated. "
|
|
|
|
|
"CPSIGNATURE_EXTRA_CERT_CHAIN_POLICY_STATUS: '0x%08x'", extraStatus.dwError);
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is_valid = true;
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
LOG_TRACE("check_cert_chain_policy exit");
|
|
|
|
|
return is_valid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2025-02-13 13:15:06 +03:00
|
|
|
verify_cert_chain(PCCERT_CONTEXT certificate, timer_context_t *timer_ctx)
|
2024-11-25 13:06:44 +03:00
|
|
|
{
|
|
|
|
|
bool is_verified = false;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("verify_cert_chain enter");
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_get_cert_chain_enter(timer_ctx);
|
|
|
|
|
|
2024-11-25 13:06:44 +03:00
|
|
|
PCCERT_CHAIN_CONTEXT chain_ctx = get_cert_chain(certificate);
|
|
|
|
|
if (chain_ctx == NULL) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_get_cert_chain_exit(timer_ctx);
|
|
|
|
|
|
2024-11-25 13:06:44 +03:00
|
|
|
if (!check_cert_chain_policy(chain_ctx)) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is_verified = true;
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
free_cert_chain(chain_ctx);
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("cert_chain: %s", is_verified ? "verified" : "failed");
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("verify_cert_chain exit");
|
|
|
|
|
return is_verified;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 16:26:36 +03:00
|
|
|
static bool
|
|
|
|
|
check_cert_key_usage(PCCERT_CONTEXT certificate)
|
2024-12-02 12:02:24 +03:00
|
|
|
{
|
2025-02-14 16:26:36 +03:00
|
|
|
bool is_digital_signature_key_usage = false;
|
|
|
|
|
BYTE key_usage;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("check_cert_key_usage enter");
|
|
|
|
|
|
|
|
|
|
if (cp_function_list.CertGetIntendedKeyUsage(
|
|
|
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
|
|
|
certificate->pCertInfo,
|
|
|
|
|
&key_usage,
|
|
|
|
|
sizeof(key_usage))) {
|
|
|
|
|
if (key_usage & CERT_DIGITAL_SIGNATURE_KEY_USAGE) {
|
|
|
|
|
is_digital_signature_key_usage = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LOG_ERROR("CertGetIntendedKeyUsage failed: 0x%08x", cp_function_list.GetLastError());
|
|
|
|
|
}
|
2024-12-02 12:02:24 +03:00
|
|
|
|
2025-02-14 16:26:36 +03:00
|
|
|
if (is_digital_signature_key_usage) {
|
|
|
|
|
LOG_DEBUG("Certificate KeyUsage contains digitalSignature");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_ERROR("Certificate KeyUsage does not contain digitalSignature");
|
2024-12-02 12:02:24 +03:00
|
|
|
}
|
2025-02-14 16:26:36 +03:00
|
|
|
|
|
|
|
|
LOG_TRACE("check_cert_key_usage exit");
|
|
|
|
|
return is_digital_signature_key_usage;
|
2024-12-02 12:02:24 +03:00
|
|
|
}
|
|
|
|
|
|
2025-02-07 12:19:45 +03:00
|
|
|
int
|
|
|
|
|
open_signer_cert(cryptopro_context_t *ctx)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("open_signer_cert enter");
|
|
|
|
|
|
|
|
|
|
ctx->cert_store = cert_open_store();
|
|
|
|
|
if (ctx->cert_store == NULL) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx->signer_cert = get_signer_cert(ctx, ctx->cert_store);
|
|
|
|
|
if (ctx->signer_cert == NULL) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 16:26:36 +03:00
|
|
|
if (!check_cert_key_usage(ctx->signer_cert)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-02-07 12:19:45 +03:00
|
|
|
LOG_TRACE("open_signer_cert exit");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
LOG_ERROR("open_signer_cert exit with error. Last error code: 0x%08x",
|
|
|
|
|
cp_function_list.GetLastError());
|
|
|
|
|
close_signer_cert(ctx);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
close_signer_cert(cryptopro_context_t *ctx)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("close_signer_cert enter");
|
|
|
|
|
|
|
|
|
|
if (ctx->signer_cert) {
|
|
|
|
|
cp_function_list.CertFreeCertificateContext(ctx->signer_cert);
|
|
|
|
|
ctx->signer_cert = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cert_close_store(ctx->cert_store);
|
|
|
|
|
ctx->cert_store = NULL;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("close_signer_cert exit");
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 16:26:36 +03:00
|
|
|
static void
|
|
|
|
|
log_sign_hash_data_last_error()
|
|
|
|
|
{
|
|
|
|
|
DWORD error = cp_function_list.GetLastError();
|
|
|
|
|
|
|
|
|
|
switch (error) {
|
|
|
|
|
case ERROR_FUNCTION_FAILED:
|
|
|
|
|
LOG_ERROR("sign_hash_data exit with error. Last error code: "
|
|
|
|
|
"license is expired or not yet valid (0x%08x)", error);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SCARD_W_WRONG_CHV:
|
|
|
|
|
LOG_ERROR("sign_hash_data exit with error. Last error code: "
|
|
|
|
|
"the wrong PIN was presented (0x%08x)", error);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
LOG_ERROR("sign_hash_data exit with error. Last error code: 0x%08x", error);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
static int
|
|
|
|
|
sign_hash_data(const cryptopro_context_t *ctx, const str_t *data, /*out*/ str_t *sign)
|
|
|
|
|
{
|
|
|
|
|
int rc = -1;
|
|
|
|
|
BOOL bReleaseContext = FALSE;
|
|
|
|
|
HCRYPTPROV hCryptProv;
|
|
|
|
|
HCRYPTHASH hash = 0;
|
|
|
|
|
BYTE *pbSignedMessageBlob;
|
|
|
|
|
DWORD cbSignedMessageBlob;
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_verify_cert_chain_enter(ctx->timer_ctx);
|
|
|
|
|
|
|
|
|
|
if (!verify_cert_chain(ctx->signer_cert, ctx->timer_ctx)) {
|
2024-11-25 13:06:44 +03:00
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_verify_cert_chain_exit(ctx->timer_ctx);
|
|
|
|
|
|
|
|
|
|
timer_on_acquire_private_key_enter(ctx->timer_ctx);
|
|
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
if (!cp_function_list.CryptAcquireCertificatePrivateKey(
|
2025-02-07 12:19:45 +03:00
|
|
|
ctx->signer_cert,
|
|
|
|
|
CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG,
|
2024-08-21 12:47:16 +03:00
|
|
|
NULL,
|
|
|
|
|
&hCryptProv,
|
|
|
|
|
NULL,
|
|
|
|
|
&bReleaseContext)) {
|
|
|
|
|
LOG_ERROR("Cannot acquire the certificate private key");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_acquire_private_key_exit(ctx->timer_ctx);
|
|
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
if (!cp_function_list.CryptCreateHash(hCryptProv, CALG_GR3411_2012_256, 0, 0, &hash)) {
|
|
|
|
|
LOG_ERROR("CryptCreateHash() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptHashData(hash, (BYTE *)data->data, data->len, 0)) {
|
|
|
|
|
LOG_ERROR("CryptHashData() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
2025-02-13 13:15:06 +03:00
|
|
|
|
|
|
|
|
timer_on_sign_hash_enter(ctx->timer_ctx);
|
2024-08-21 12:47:16 +03:00
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, NULL, &cbSignedMessageBlob)) {
|
|
|
|
|
LOG_ERROR("CryptSignHash() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pbSignedMessageBlob = malloc(cbSignedMessageBlob * sizeof(BYTE));
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, pbSignedMessageBlob, &cbSignedMessageBlob)) {
|
|
|
|
|
LOG_ERROR("CryptSignHash() failed");
|
|
|
|
|
free(pbSignedMessageBlob);
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_sign_hash_exit(ctx->timer_ctx);
|
|
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
sign->data = (char*)pbSignedMessageBlob;
|
|
|
|
|
sign->len = (size_t)cbSignedMessageBlob;
|
|
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
if (hash) {
|
|
|
|
|
cp_function_list.CryptDestroyHash(hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bReleaseContext) {
|
|
|
|
|
cp_function_list.CryptReleaseContext(hCryptProv, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (rc) {
|
2024-12-02 12:02:24 +03:00
|
|
|
log_sign_hash_data_last_error();
|
2024-08-21 12:47:16 +03:00
|
|
|
}
|
2024-12-02 12:02:24 +03:00
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
reverse_sign(const str_t *sign, /*out*/ str_t *sign_reversed)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
sign_reversed->data = malloc(sign->len);
|
|
|
|
|
if (sign_reversed->data == NULL) {
|
|
|
|
|
LOG_ERROR("Could not allocate memory for reversed sign (%zd bytes)", sign->len);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < sign->len; i++) {
|
|
|
|
|
sign_reversed->data[i] = sign->data[sign->len - i - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sign_reversed->len = sign->len;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static HCERTSTORE
|
|
|
|
|
cert_open_store()
|
|
|
|
|
{
|
|
|
|
|
HCERTSTORE hStoreHandle;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("cert_open_store enter");
|
|
|
|
|
|
|
|
|
|
hStoreHandle = cp_function_list.CertOpenStore(CERT_STORE_PROV_SYSTEM,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
2025-02-07 12:19:45 +03:00
|
|
|
CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG,
|
2024-08-21 12:47:16 +03:00
|
|
|
L"MY");
|
|
|
|
|
|
|
|
|
|
if (hStoreHandle == NULL) {
|
|
|
|
|
LOG_ERROR("The store could not be opened");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("The MY store is open");
|
2025-02-07 12:19:45 +03:00
|
|
|
LOG_TRACE("cert_open_store exit");
|
2024-08-21 12:47:16 +03:00
|
|
|
return hStoreHandle;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-07 12:19:45 +03:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
cert_close_store(HCERTSTORE hStoreHandle)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("cert_close_store enter");
|
|
|
|
|
|
|
|
|
|
if (hStoreHandle) {
|
|
|
|
|
if (!cp_function_list.CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) {
|
|
|
|
|
LOG_ERROR("CertCloseStore() failed");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("cert_close_store exit");
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-21 12:47:16 +03:00
|
|
|
static PCCERT_CONTEXT
|
2024-11-14 12:16:55 +03:00
|
|
|
get_cert_by_thumbprint(HCERTSTORE hStoreHandle, const str_t* thumbprint)
|
2024-08-21 12:47:16 +03:00
|
|
|
{
|
2024-11-14 12:16:55 +03:00
|
|
|
str_t thumbprint_bin = str_t_null;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("get_cert_by_thumbprint enter");
|
2024-08-21 12:47:16 +03:00
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
if (hex_to_bin(thumbprint, &thumbprint_bin)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CRYPT_HASH_BLOB hash = {
|
|
|
|
|
.pbData = (BYTE*)thumbprint_bin.data,
|
|
|
|
|
.cbData = thumbprint_bin.len
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PCCERT_CONTEXT cert = cp_function_list.CertFindCertificateInStore(hStoreHandle,
|
2024-08-21 12:47:16 +03:00
|
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
|
|
|
0,
|
2024-11-14 12:16:55 +03:00
|
|
|
CERT_FIND_HASH,
|
|
|
|
|
&hash,
|
2024-08-21 12:47:16 +03:00
|
|
|
NULL);
|
2024-11-14 12:16:55 +03:00
|
|
|
if (cert == NULL) {
|
2024-08-21 12:47:16 +03:00
|
|
|
LOG_ERROR("Could not find certificate in store");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
str_t_clear(&thumbprint_bin);
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("get_cert_by_thumbprint exit");
|
|
|
|
|
|
|
|
|
|
return cert;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
str_t_clear(&thumbprint_bin);
|
|
|
|
|
LOG_ERROR("get_cert_by_thumbprint exit with error");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PCCERT_CONTEXT
|
|
|
|
|
get_signer_cert(const cryptopro_context_t *ctx, HCERTSTORE hStoreHandle)
|
|
|
|
|
{
|
|
|
|
|
PCCERT_CONTEXT cert_ctx = get_cert_by_thumbprint(hStoreHandle, ctx->cert_thumbprint);
|
|
|
|
|
if (cert_ctx == NULL) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 13:18:39 +03:00
|
|
|
if (!str_t_is_null(*ctx->password)) {
|
|
|
|
|
if (set_password(cert_ctx, ctx->password)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
2024-08-21 12:47:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("The signer's certificate was found");
|
2024-11-14 12:16:55 +03:00
|
|
|
return cert_ctx;
|
2024-08-21 12:47:16 +03:00
|
|
|
|
|
|
|
|
error:
|
2024-11-14 12:16:55 +03:00
|
|
|
if (cert_ctx) {
|
|
|
|
|
cp_function_list.CertFreeCertificateContext(cert_ctx);
|
2024-08-21 12:47:16 +03:00
|
|
|
}
|
|
|
|
|
LOG_ERROR("get_signer_cert exit with error");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2024-11-14 12:16:55 +03:00
|
|
|
set_password(PCCERT_CONTEXT cert_ctx, const str_t *password)
|
2024-08-21 12:47:16 +03:00
|
|
|
{
|
|
|
|
|
DWORD dwSize;
|
|
|
|
|
if (!cp_function_list.CertGetCertificateContextProperty(
|
2024-11-14 12:16:55 +03:00
|
|
|
cert_ctx,
|
2024-08-21 12:47:16 +03:00
|
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
|
|
|
NULL,
|
|
|
|
|
&dwSize)) {
|
|
|
|
|
LOG_ERROR("Error getting key property");
|
2024-11-14 12:16:55 +03:00
|
|
|
LOG_ERROR("set_password exit with error. Last error code: 0x%08x", cp_function_list.GetLastError());
|
2024-08-21 12:47:16 +03:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned char pKeyInfoBuffer[dwSize];
|
|
|
|
|
CRYPT_KEY_PROV_INFO* pKeyInfo = (CRYPT_KEY_PROV_INFO*) pKeyInfoBuffer;
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CertGetCertificateContextProperty(
|
2024-11-14 12:16:55 +03:00
|
|
|
cert_ctx,
|
2024-08-21 12:47:16 +03:00
|
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
|
|
|
pKeyInfo,
|
|
|
|
|
&dwSize)){
|
|
|
|
|
LOG_ERROR("The second call to the function failed");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD keyType = (pKeyInfo->dwKeySpec == AT_SIGNATURE) ? PP_SIGNATURE_PIN
|
|
|
|
|
: PP_KEYEXCHANGE_PIN;
|
|
|
|
|
|
|
|
|
|
CRYPT_KEY_PROV_PARAM keyProvParam;
|
|
|
|
|
memset(&keyProvParam, 0, sizeof(CRYPT_KEY_PROV_PARAM));
|
|
|
|
|
keyProvParam.dwFlags = 0;
|
|
|
|
|
keyProvParam.dwParam = keyType;
|
2024-11-14 12:16:55 +03:00
|
|
|
keyProvParam.cbData = (DWORD)(password->len + 1);
|
|
|
|
|
keyProvParam.pbData = (BYTE*) password->data;
|
2024-08-21 12:47:16 +03:00
|
|
|
|
|
|
|
|
pKeyInfo->cProvParam = 1;
|
|
|
|
|
pKeyInfo->rgProvParam = &keyProvParam;
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CertSetCertificateContextProperty(
|
2024-11-14 12:16:55 +03:00
|
|
|
cert_ctx,
|
2024-08-21 12:47:16 +03:00
|
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
|
|
|
0,
|
|
|
|
|
pKeyInfo)) {
|
|
|
|
|
LOG_ERROR("Error setting key property");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
2024-11-14 12:16:55 +03:00
|
|
|
LOG_ERROR("set_password exit with error. Last error code: 0x%08x", cp_function_list.GetLastError());
|
2024-08-21 12:47:16 +03:00
|
|
|
return -1;
|
2024-11-14 12:16:55 +03:00
|
|
|
}
|
|
|
|
|
|
2024-12-24 12:07:21 +03:00
|
|
|
static void
|
|
|
|
|
set_unknown_alg_error(/*out*/ char** verify_error, const str_t* alg)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("set_unknown_alg_error enter");
|
|
|
|
|
|
|
|
|
|
const str_t err_format = str_t_const("Unknown alg: '%.*s'");
|
|
|
|
|
|
|
|
|
|
size_t size = err_format.len + alg->len;
|
|
|
|
|
|
|
|
|
|
*verify_error = malloc(size);
|
|
|
|
|
if (*verify_error == NULL) {
|
|
|
|
|
LOG_ERROR("set_unknown_alg_error failed. Could not allocate memory for error string "
|
|
|
|
|
"(%zd bytes)", size);
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int n = snprintf(*verify_error, size, err_format.data, (int) alg->len, alg->data);
|
|
|
|
|
|
|
|
|
|
if (n < 0 || (size_t)n >= size) {
|
|
|
|
|
LOG_ERROR("set_unknown_alg_error failed. Error occurred in concatenation err_string");
|
|
|
|
|
free(*verify_error);
|
|
|
|
|
*verify_error = NULL;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_ERROR("%s", *verify_error);
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
LOG_TRACE("set_unknown_alg_error exit");
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
static int
|
2024-12-24 12:07:21 +03:00
|
|
|
alg_id_from_str(const str_t* alg, /*out*/ ALG_ID* alg_id, /*out*/ char** verify_error)
|
2024-11-14 12:16:55 +03:00
|
|
|
{
|
|
|
|
|
LOG_TRACE("alg_id_from_str enter");
|
|
|
|
|
|
|
|
|
|
if (strncmp(alg->data, "GOST3410_2012_256", alg->len) == 0) {
|
|
|
|
|
*alg_id = CALG_GR3411_2012_256;
|
|
|
|
|
} else if (strncmp(alg->data, "GOST3410_2012_512", alg->len) == 0) {
|
|
|
|
|
*alg_id = CALG_GR3411_2012_512;
|
|
|
|
|
} else {
|
2024-12-24 12:07:21 +03:00
|
|
|
set_unknown_alg_error(verify_error, alg);
|
2024-11-14 12:16:55 +03:00
|
|
|
LOG_ERROR("alg_id_from_str exit with error");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("alg_id_from_str exit");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_verify_error(char** verify_error)
|
|
|
|
|
{
|
|
|
|
|
LOG_TRACE("get_verify_error enter");
|
|
|
|
|
|
|
|
|
|
DWORD err = cp_function_list.GetLastError();
|
|
|
|
|
|
|
|
|
|
const char* err_string;
|
|
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
|
case NTE_BAD_SIGNATURE:
|
|
|
|
|
err_string = "sign is invalid: bad signature (0x%08x)";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NTE_BAD_ALGID:
|
|
|
|
|
err_string = "sign is invalid: bad alg id (0x%08x)";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
err_string = "sign is invalid. Last error code: 0x%08x";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t size = strlen(err_string) + 4 /*error code*/ + 1 /*terminating null*/;
|
|
|
|
|
|
2024-11-25 13:32:52 +03:00
|
|
|
if (*verify_error != NULL) {
|
|
|
|
|
free(*verify_error); // в ответе отдаем последнюю ошибку
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
*verify_error = malloc(size);
|
|
|
|
|
if (*verify_error == NULL) {
|
|
|
|
|
LOG_ERROR("get_verify_error failed. Could not allocate memory for error string "
|
|
|
|
|
"(%zd bytes)", size);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int n = snprintf(*verify_error, size, err_string, err);
|
|
|
|
|
|
|
|
|
|
if (n < 0 || (size_t)n >= size) {
|
|
|
|
|
LOG_ERROR("get_verify_error failed. Error occurred in concatenation err_string");
|
|
|
|
|
free(*verify_error);
|
|
|
|
|
*verify_error = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("get_verify_error exit");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cryptopro_verify(const str_t* cert_thumbprint, const str_t* alg, const str_t* data,
|
|
|
|
|
const str_t* sign, bool* is_verified, char** verify_error)
|
|
|
|
|
{
|
|
|
|
|
int rc = -1;
|
|
|
|
|
HCERTSTORE hStoreHandle = NULL;
|
|
|
|
|
PCCERT_CONTEXT certificate = NULL;
|
|
|
|
|
HCRYPTPROV hCryptProv = 0;
|
|
|
|
|
HCRYPTHASH hash = 0;
|
|
|
|
|
HCRYPTKEY hPubKey = 0;
|
|
|
|
|
str_t sign_reversed = str_t_null;
|
|
|
|
|
ALG_ID alg_id;
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_context_t timer_ctx = {};
|
|
|
|
|
|
|
|
|
|
init_timers(&timer_ctx);
|
2024-11-14 12:16:55 +03:00
|
|
|
|
|
|
|
|
LOG_TRACE("cryptopro_verify enter");
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_cryptopro_verify_enter(&timer_ctx);
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
*is_verified = false;
|
|
|
|
|
|
2024-12-24 12:07:21 +03:00
|
|
|
if (alg_id_from_str(alg, &alg_id, verify_error)) {
|
2024-11-14 12:16:55 +03:00
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reverse_sign(sign, &sign_reversed)) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hStoreHandle = cert_open_store();
|
|
|
|
|
if (hStoreHandle == NULL) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
certificate = get_cert_by_thumbprint(hStoreHandle, cert_thumbprint);
|
|
|
|
|
if (certificate == NULL) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_verify_cert_chain_enter(&timer_ctx);
|
|
|
|
|
|
|
|
|
|
if (!verify_cert_chain(certificate, &timer_ctx)) {
|
2024-11-25 13:06:44 +03:00
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_verify_cert_chain_exit(&timer_ctx);
|
|
|
|
|
|
2025-01-24 10:41:21 +03:00
|
|
|
if (!cp_function_list.CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_GOST_2012_256,
|
|
|
|
|
CRYPT_VERIFYCONTEXT)) {
|
2024-11-14 12:16:55 +03:00
|
|
|
LOG_ERROR("CryptAcquireContext() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING,
|
|
|
|
|
&certificate->pCertInfo->SubjectPublicKeyInfo,
|
|
|
|
|
&hPubKey)) {
|
|
|
|
|
LOG_ERROR("CryptImportPublicKeyInfo() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptCreateHash(hCryptProv, alg_id, 0, 0, &hash)) {
|
|
|
|
|
LOG_ERROR("CryptCreateHash() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptHashData(hash, (CONST BYTE *) data->data, data->len, 0)) {
|
|
|
|
|
LOG_ERROR("CryptHashData() failed");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cp_function_list.CryptVerifySignature(hash,
|
|
|
|
|
(CONST BYTE *) sign_reversed.data,
|
|
|
|
|
sign_reversed.len,
|
|
|
|
|
hPubKey,
|
|
|
|
|
NULL,
|
|
|
|
|
0)) {
|
|
|
|
|
LOG_DEBUG("sign is valid");
|
|
|
|
|
*is_verified = true;
|
|
|
|
|
} else {
|
|
|
|
|
get_verify_error(verify_error);
|
|
|
|
|
if (*verify_error == NULL) {
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
2024-11-25 13:32:52 +03:00
|
|
|
LOG_WARN("%s, cert_thumbprint: %.*s", *verify_error,
|
|
|
|
|
(int) cert_thumbprint->len, cert_thumbprint->data);
|
2024-11-14 12:16:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit:
|
2024-12-24 12:07:21 +03:00
|
|
|
|
|
|
|
|
if (*is_verified || *verify_error) {
|
|
|
|
|
rc = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
str_t_clear(&sign_reversed);
|
|
|
|
|
|
|
|
|
|
if (hash) {
|
|
|
|
|
cp_function_list.CryptDestroyHash(hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hPubKey) {
|
|
|
|
|
cp_function_list.CryptDestroyKey(hPubKey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hCryptProv) {
|
|
|
|
|
cp_function_list.CryptReleaseContext(hCryptProv, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (certificate) {
|
|
|
|
|
cp_function_list.CertFreeCertificateContext(certificate);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-07 12:19:45 +03:00
|
|
|
cert_close_store(hStoreHandle);
|
2024-11-14 12:16:55 +03:00
|
|
|
|
|
|
|
|
if (rc == 0) {
|
|
|
|
|
LOG_TRACE("cryptopro_verify exit");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_ERROR("cryptopro_verify exit with error. Last error code: 0x%08x",
|
|
|
|
|
cp_function_list.GetLastError());
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 13:15:06 +03:00
|
|
|
timer_on_cryptopro_verify_exit(&timer_ctx);
|
|
|
|
|
timer_log_verify(&timer_ctx);
|
|
|
|
|
|
2024-11-14 12:16:55 +03:00
|
|
|
return rc;
|
|
|
|
|
}
|
2024-12-26 14:43:18 +03:00
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cryptopro_gen_random(unsigned char* data, size_t len)
|
|
|
|
|
{
|
|
|
|
|
HCRYPTPROV hCryptProv = 0;
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("cryptopro_gen_random enter");
|
|
|
|
|
|
2025-01-24 10:41:21 +03:00
|
|
|
if (!cp_function_list.CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_GOST_2012_256,
|
|
|
|
|
CRYPT_VERIFYCONTEXT)) {
|
2024-12-26 14:43:18 +03:00
|
|
|
LOG_ERROR("CryptAcquireContext() failed");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cp_function_list.CryptGenRandom(hCryptProv, len, data)) {
|
|
|
|
|
LOG_ERROR("CryptGenRandom() failed");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cp_function_list.CryptReleaseContext(hCryptProv, 0);
|
|
|
|
|
|
|
|
|
|
LOG_TRACE("cryptopro_gen_random exit");
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
if (hCryptProv) {
|
|
|
|
|
cp_function_list.CryptReleaseContext(hCryptProv, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_ERROR("cryptopro_gen_random exit with error. Last error code: 0x%08x",
|
|
|
|
|
cp_function_list.GetLastError());
|
|
|
|
|
return -1;
|
|
|
|
|
}
|