Coverage Report

Created: 2026-02-14 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/x509/algorithm.cc
Line
Count
Source
1
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include <openssl/x509.h>
16
17
#include <openssl/asn1.h>
18
#include <openssl/digest.h>
19
#include <openssl/err.h>
20
#include <openssl/evp.h>
21
#include <openssl/obj.h>
22
23
#include "internal.h"
24
25
26
using namespace bssl;
27
28
// TODO(crbug.com/42290422): Rewrite this logic to recognize signature
29
// algorithms without pulling in the OID table. We can enumerate every supported
30
// signature algorithm into a small enum and convert them to/from |EVP_PKEY_CTX|
31
// and |X509_ALGOR|.
32
33
// Restrict the digests that are allowed in X509 certificates
34
0
static int x509_digest_nid_ok(const int digest_nid) {
35
0
  switch (digest_nid) {
36
0
    case NID_md4:
37
0
    case NID_md5:
38
0
      return 0;
39
0
  }
40
0
  return 1;
41
0
}
42
43
0
int bssl::x509_digest_sign_algorithm(EVP_MD_CTX *ctx, X509_ALGOR *algor) {
44
0
  EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx->pctx);
45
0
  if (pkey == nullptr) {
46
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_CONTEXT_NOT_INITIALISED);
47
0
    return 0;
48
0
  }
49
50
0
  if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA ||
51
0
      EVP_PKEY_id(pkey) == EVP_PKEY_RSA_PSS) {
52
0
    int pad_mode;
53
0
    if (!EVP_PKEY_CTX_get_rsa_padding(ctx->pctx, &pad_mode)) {
54
0
      return 0;
55
0
    }
56
    // RSA-PSS has special signature algorithm logic.
57
0
    if (pad_mode == RSA_PKCS1_PSS_PADDING) {
58
0
      return x509_rsa_ctx_to_pss(ctx, algor);
59
0
    }
60
0
  }
61
62
  // Check for signing algorithms with an internal hash.
63
0
  ASN1_OBJECT *algo_obj = nullptr;
64
0
  switch (EVP_PKEY_id(pkey)) {
65
0
    case EVP_PKEY_ED25519:
66
0
      algo_obj = OBJ_nid2obj(NID_ED25519);
67
0
      break;
68
0
    case EVP_PKEY_ML_DSA_44:
69
0
      algo_obj = OBJ_nid2obj(NID_ML_DSA_44);
70
0
      break;
71
0
    case EVP_PKEY_ML_DSA_65:
72
0
      algo_obj = OBJ_nid2obj(NID_ML_DSA_65);
73
0
      break;
74
0
    case EVP_PKEY_ML_DSA_87:
75
0
      algo_obj = OBJ_nid2obj(NID_ML_DSA_87);
76
0
      break;
77
0
  }
78
0
  if (algo_obj != nullptr) {
79
0
    return X509_ALGOR_set0(algor, algo_obj, V_ASN1_UNDEF, nullptr);
80
0
  }
81
82
  // Default behavior: look up the OID for the algorithm/hash pair and encode
83
  // that.
84
0
  const EVP_MD *digest = EVP_MD_CTX_get0_md(ctx);
85
0
  if (digest == nullptr) {
86
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_CONTEXT_NOT_INITIALISED);
87
0
    return 0;
88
0
  }
89
90
0
  const int digest_nid = EVP_MD_type(digest);
91
0
  int sign_nid;
92
0
  if (!x509_digest_nid_ok(digest_nid) ||
93
0
      !OBJ_find_sigid_by_algs(&sign_nid, digest_nid, EVP_PKEY_id(pkey))) {
94
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED);
95
0
    return 0;
96
0
  }
97
98
  // RSA signature algorithms include an explicit NULL parameter. Others omit
99
  // it.
100
0
  int paramtype =
101
0
      (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) ? V_ASN1_NULL : V_ASN1_UNDEF;
102
0
  return X509_ALGOR_set0(algor, OBJ_nid2obj(sign_nid), paramtype, nullptr);
103
0
}
104
105
int bssl::x509_digest_verify_init(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
106
0
                                  EVP_PKEY *pkey) {
107
  // Convert the signature OID into digest and public key OIDs.
108
0
  int sigalg_nid = OBJ_obj2nid(sigalg->algorithm);
109
0
  int digest_nid, pkey_nid;
110
0
  if (!OBJ_find_sigid_algs(sigalg_nid, &digest_nid, &pkey_nid)) {
111
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
112
0
    return 0;
113
0
  }
114
115
  // Check the public key OID matches the public key type.
116
0
  const bool pkey_matches =
117
0
      pkey_nid == EVP_PKEY_id(pkey) ||
118
0
      (sigalg_nid == NID_rsassaPss && EVP_PKEY_id(pkey) == EVP_PKEY_RSA_PSS);
119
0
  if (!pkey_matches) {
120
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_PUBLIC_KEY_TYPE);
121
0
    return 0;
122
0
  }
123
124
  // Check for permitted digest algorithms
125
0
  if (!x509_digest_nid_ok(digest_nid)) {
126
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED);
127
0
    return 0;
128
0
  }
129
130
  // NID_undef signals that there are custom parameters to set.
131
0
  if (digest_nid == NID_undef) {
132
0
    if (sigalg_nid == NID_rsassaPss) {
133
0
      return x509_rsa_pss_to_ctx(ctx, sigalg, pkey);
134
0
    }
135
0
    if (sigalg_nid == NID_ED25519 || sigalg_nid == NID_ML_DSA_44 ||
136
0
        sigalg_nid == NID_ML_DSA_65 || sigalg_nid == NID_ML_DSA_87) {
137
      // These algorithms require that parameters be absent (Ed25519: RFC 8410
138
      // section 3, ML-DSA: RFC 9881 section 2).
139
0
      if (sigalg->parameter != nullptr) {
140
0
        OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PARAMETER);
141
0
        return 0;
142
0
      }
143
0
      return EVP_DigestVerifyInit(ctx, nullptr, nullptr, nullptr, pkey);
144
0
    }
145
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM);
146
0
    return 0;
147
0
  }
148
149
  // The parameter should be an explicit NULL for RSA and omitted for ECDSA. For
150
  // compatibility, we allow either for both algorithms. See b/167375496.
151
  //
152
  // TODO(davidben): Chromium's verifier allows both forms for RSA, but enforces
153
  // ECDSA more strictly. Align with Chromium and add a flag for b/167375496.
154
0
  if (sigalg->parameter != nullptr && sigalg->parameter->type != V_ASN1_NULL) {
155
0
    OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PARAMETER);
156
0
    return 0;
157
0
  }
158
159
  // Otherwise, initialize with the digest from the OID.
160
0
  const EVP_MD *digest = EVP_get_digestbynid(digest_nid);
161
0
  if (digest == nullptr) {
162
0
    OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM);
163
0
    return 0;
164
0
  }
165
166
0
  return EVP_DigestVerifyInit(ctx, nullptr, digest, nullptr, pkey);
167
0
}