Skip to content

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

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions