Skip to content

Fix certificate chain verification for GOST signatures (Streebog-256/512) #532

@mikeananev

Description

@mikeananev

Связано с #531 (после патчей в 531 и этом issue проблема уходит)

Описание

Верификация цепочки сертификатов с ГОСТ-подписями (Streebog-256/512)
не работает с provider-based реализациями OpenSSL 3.x по двум причинам:

  1. x509_sig_info_init() (crypto/x509/x509_set.c) — функция, вычисляющая security bits для signature digest, не имеет явных значений для NID_id_GostR3411_2012_256 и NID_id_GostR3411_2012_512. Они попадают в секцию default, где EVP_get_digestbynid(mdnid) возвращает NULL (потому что provider digest-ы не регистрируются в legacy OBJ_NAME). Результат: X509_V_ERR_CA_MD_TOO_WEAK.

  2. ASN1_item_verify_ctx() (crypto/asn1/a_verify.c) — при верификации подписи сертификата вызывает EVP_get_digestbynid(mdnid), что также возвращает NULL для Streebog-256/512. Результат: X509_V_ERR_CERT_SIGNATURE_FAILURE (ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM).

Исправление

x509_set.c — добавлены case-ветки для Streebog NID с явными secbits:

  • NID_id_GostR3411_2012_256 → 128 (256/2 = 128 birthday bound)
  • NID_id_GostR3411_2012_512 → 256 (512/2 = 256 birthday bound)

По аналогии с существующей веткой NID_id_GostR3411_94 (secbits=105).

a_verify.c — при failure EVP_get_digestbynid() добавлен fallback: преобразовать NID → short name через OBJ_nid2sn() → EVP_MD_fetch().
Это работает для любых провайдерских дайджестов, а не только ГОСТ.

Альтернативы

  • SecurityLevel=0 в openssl.cnf — маскирует симптом.
  • EVP_add_digest() — не работает с provider-fetched EVP_MD
    (разная внутренняя структура, potential UB при OBJ_NAME_add).

Патч

diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c
index 3234824807..6954404560 100644
--- a/crypto/x509/x509_set.c
+++ b/crypto/x509/x509_set.c
@@ -278,6 +278,14 @@ static int x509_sig_info_init(X509_SIG_INFO *siginf, const X509_ALGOR *alg,
          */
         siginf->secbits = 105;
         break;
+    case NID_id_GostR3411_2012_256:
+        /* Birthday bound: 256 / 2 = 128 bits */
+        siginf->secbits = 128;
+        break;
+    case NID_id_GostR3411_2012_512:
+        /* Birthday bound: 512 / 2 = 256 bits */
+        siginf->secbits = 256;
+        break;
     default:
         /* Security bits: half number of bits in digest */
         if ((md = EVP_get_digestbynid(mdnid)) == NULL) {
diff --git a/crypto/asn1/a_verify.c b/crypto/asn1/a_verify.c
index 3d882c5f25..9d645e7b59 100644
--- a/crypto/asn1/a_verify.c
+++ b/crypto/asn1/a_verify.c
@@ -177,12 +177,17 @@ int ASN1_item_verify_ctx(const ASN1_ITEM *it, const X509_ALGOR *alg,
             }
 
             if (mdnid != NID_undef) {
-                type = EVP_get_digestbynid(mdnid);
+                if ((type = EVP_get_digestbynid(mdnid)) == NULL) {
+                    const char *md_name = OBJ_nid2sn(mdnid);
+                    if (md_name != NULL)
+                        type = EVP_MD_fetch(NULL, md_name, NULL);
+                }
                 if (type == NULL) {
                     ERR_raise_data(ERR_LIB_ASN1,
                                    ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM,
                                    "nid=0x%x", mdnid);
                     goto err;
                 }
             }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions