Line data Source code
1 : #include "fd_x509_verify.h"
2 : #include "fd_der.h"
3 : #include "../ed25519/fd_ed25519.h"
4 : #include "../secp256r1/fd_secp256r1.h"
5 : #include "../secp384r1/fd_secp384r1.h"
6 : #include <string.h>
7 :
8 : /* fd_x509_verify_sig verifies a certificate's signature given the
9 : issuer's public key. Returns 0 on success, non-zero on failure. */
10 :
11 : static int
12 : fd_x509_verify_sig( fd_x509_cert_info_t const * cert,
13 : uchar const * issuer_pubkey,
14 : ulong issuer_pubkey_len,
15 2010 : uchar issuer_key_type ) {
16 :
17 2010 : switch( cert->sig_alg ) {
18 :
19 1344 : case FD_X509_SIG_ED25519: {
20 1344 : if( FD_UNLIKELY( issuer_key_type != FD_X509_KEY_ED25519 ) ) return -1;
21 1344 : if( FD_UNLIKELY( issuer_pubkey_len != 32 ) ) return -1;
22 1344 : if( FD_UNLIKELY( cert->sig_len != 64 ) ) return -1;
23 :
24 1344 : fd_sha512_t sha512[1];
25 1344 : int err = fd_ed25519_verify( cert->tbs, cert->tbs_len, cert->sig, issuer_pubkey, sha512 );
26 1344 : return ( err == FD_ED25519_SUCCESS ) ? 0 : -1;
27 1344 : }
28 :
29 465 : case FD_X509_SIG_ECDSA_SHA256: {
30 465 : if( FD_UNLIKELY( issuer_key_type != FD_X509_KEY_ECDSA_P256 ) ) return -1;
31 465 : if( FD_UNLIKELY( issuer_pubkey_len != 65 ) ) return -1;
32 :
33 465 : uchar raw_sig[64];
34 465 : if( FD_UNLIKELY( fd_x509_decode_ecdsa_sig( cert->sig, cert->sig_len, raw_sig, 32 ) ) )
35 0 : return -1;
36 :
37 465 : uchar compressed_pk[33];
38 465 : if( FD_UNLIKELY( fd_x509_ec_point_compress( issuer_pubkey, 32, compressed_pk ) ) )
39 0 : return -1;
40 :
41 465 : fd_sha256_t sha256[1];
42 465 : int err = fd_secp256r1_verify_allow_high_s( cert->tbs, cert->tbs_len, raw_sig, compressed_pk, sha256 );
43 465 : return ( err == FD_SECP256R1_SUCCESS ) ? 0 : -1;
44 465 : }
45 :
46 162 : case FD_X509_SIG_ECDSA_SHA384: {
47 162 : if( FD_UNLIKELY( issuer_key_type != FD_X509_KEY_ECDSA_P384 ) ) return -1;
48 162 : if( FD_UNLIKELY( issuer_pubkey_len != 97 ) ) return -1;
49 :
50 162 : uchar raw_sig[96];
51 162 : if( FD_UNLIKELY( fd_x509_decode_ecdsa_sig( cert->sig, cert->sig_len, raw_sig, 48 ) ) )
52 0 : return -1;
53 :
54 162 : uchar compressed_pk[49];
55 162 : if( FD_UNLIKELY( fd_x509_ec_point_compress( issuer_pubkey, 48, compressed_pk ) ) )
56 0 : return -1;
57 :
58 162 : fd_sha512_t sha384[1];
59 162 : int err = fd_secp384r1_verify_allow_high_s( cert->tbs, cert->tbs_len, raw_sig, compressed_pk, sha384 );
60 162 : return ( err == FD_SECP384R1_SUCCESS ) ? 0 : -1;
61 162 : }
62 :
63 18 : case FD_X509_SIG_RSA_SHA256:
64 24 : case FD_X509_SIG_RSA_SHA384:
65 39 : case FD_X509_SIG_RSA_SHA512: {
66 : #if !FD_HAS_INT128
67 : return 1; /* no fd_rsa */
68 : #else
69 39 : if( FD_UNLIKELY( issuer_key_type != FD_X509_KEY_RSA ) ) return -1;
70 :
71 39 : fd_rsa_pubkey_t key[1];
72 39 : if( FD_UNLIKELY( fd_x509_decode_rsa_pubkey( issuer_pubkey, issuer_pubkey_len, key ) ) )
73 0 : return -1;
74 :
75 39 : int hash = cert->sig_alg==FD_X509_SIG_RSA_SHA256 ? FD_RSA_HASH_SHA256 :
76 39 : cert->sig_alg==FD_X509_SIG_RSA_SHA384 ? FD_RSA_HASH_SHA384 :
77 21 : FD_RSA_HASH_SHA512;
78 39 : int err = fd_rsa_verify_pkcs1_v15( key, cert->sig, cert->sig_len, cert->tbs, cert->tbs_len, hash );
79 39 : return ( err == FD_RSA_SUCCESS ) ? 0 : -1;
80 39 : #endif
81 39 : }
82 :
83 0 : default:
84 0 : return 1; /* unsupported sig algorithm */
85 2010 : }
86 2010 : }
87 :
88 : /* fd_x509_check_validity returns FD_X509_VERIFY_OK if cert is within its
89 : validity period at unix_seconds, otherwise the error to report. */
90 :
91 : static int
92 : fd_x509_check_validity( fd_x509_cert_info_t const * cert,
93 2439 : long unix_seconds ) {
94 2439 : if( FD_UNLIKELY( cert->not_before_unix==FD_X509_TIME_INVALID ||
95 2439 : cert->not_after_unix ==FD_X509_TIME_INVALID ) )
96 6 : return FD_X509_VERIFY_ERR_TIME_PARSE;
97 2433 : if( FD_UNLIKELY( unix_seconds < cert->not_before_unix ) )
98 21 : return FD_X509_VERIFY_ERR_NOT_YET_VALID;
99 2412 : if( FD_UNLIKELY( unix_seconds > cert->not_after_unix ) )
100 33 : return FD_X509_VERIFY_ERR_EXPIRED;
101 2379 : return FD_X509_VERIFY_OK;
102 2412 : }
103 :
104 : /* fd_x509_check_eku returns OK if cert may be used for TLS server
105 : authentication. An absent extKeyUsage is unconstrained. */
106 :
107 : static int
108 2346 : fd_x509_check_eku( fd_x509_cert_info_t const * cert ) {
109 2346 : if( FD_UNLIKELY(
110 2346 : cert->has_ext_key_usage &&
111 2346 : !( cert->ext_key_usage & ( FD_X509_EKU_SERVER_AUTH|FD_X509_EKU_ANY ) ) ) ) {
112 21 : return FD_X509_VERIFY_ERR_EXT_KEY_USAGE;
113 21 : }
114 2325 : return FD_X509_VERIFY_OK;
115 2346 : }
116 :
117 : /* fd_x509_check_leaf_usage enforces the TLS 1.3 server authentication
118 : usage policy on the leaf. */
119 :
120 : static int
121 1548 : fd_x509_check_leaf_usage( fd_x509_cert_info_t const * leaf ) {
122 1548 : if( FD_UNLIKELY(
123 1548 : leaf->has_key_usage &&
124 1548 : !( leaf->key_usage & FD_X509_KU_DIGITAL_SIGNATURE ) ) ) {
125 15 : return FD_X509_VERIFY_ERR_KEY_USAGE;
126 15 : }
127 1533 : return fd_x509_check_eku( leaf );
128 1548 : }
129 :
130 :
131 : static int
132 : fd_x509_dns_constraint_matches( uchar const * constraint,
133 : ulong constraint_len,
134 : uchar const * name,
135 : ulong name_len,
136 90 : int excluded ) {
137 : /* Excluded subtrees reject any overlapping wildcard expansion. */
138 90 : if( excluded && name_len>2UL && name[0]=='*' && name[1]=='.' ) {
139 27 : uchar const * dot = memchr( constraint, '.', constraint_len );
140 27 : if( dot ) {
141 24 : ulong tail_len = constraint_len-(ulong)( dot+1-constraint );
142 24 : if( tail_len==name_len-2UL &&
143 24 : fd_x509_dns_eq_ci( (char const *)name+2, (char const *)dot+1, tail_len ) ) return 1;
144 24 : }
145 27 : }
146 :
147 81 : int subdomains_only = constraint[0]=='.';
148 81 : if( subdomains_only ) {
149 18 : if( name_len<=constraint_len ) return 0;
150 9 : return fd_x509_dns_eq_ci( (char const *)name+name_len-constraint_len, (char const *)constraint, constraint_len );
151 18 : }
152 :
153 63 : if( name_len<constraint_len ) return 0;
154 39 : if( !fd_x509_dns_eq_ci( (char const *)name+name_len-constraint_len, (char const *)constraint, constraint_len ) ) return 0;
155 33 : return name_len==constraint_len || name[name_len-constraint_len-1UL]=='.';
156 39 : }
157 :
158 : /* iPAddress subtree base is address||mask (8 or 32 bytes). */
159 :
160 : static int
161 : fd_x509_ip_constraint_matches( uchar const * constraint,
162 : ulong constraint_len,
163 : uchar const * ip,
164 24 : ulong ip_len ) {
165 24 : if( constraint_len!=2UL*ip_len ) return 0;
166 18 : uchar const * mask = constraint+ip_len;
167 102 : for( ulong i=0UL; i<ip_len; i++ ) {
168 90 : if( (ip[i] & mask[i]) != (constraint[i] & mask[i]) ) return 0;
169 90 : }
170 12 : return 1;
171 18 : }
172 :
173 : /* fd_x509_subtrees_match matches name against the subtrees of the same
174 : name form (tag). Returns:
175 : 1 a subtree matches
176 : 0 subtrees of this form exist, none match
177 : -1 no subtree of this form (form is unconstrained, RFC 5280 4.2.1.10)
178 : -2 malformed, or a subtree of a form this verifier cannot match */
179 :
180 : static int
181 : fd_x509_subtrees_match( uchar const * trees,
182 : ulong trees_len,
183 : int tag,
184 : uchar const * name,
185 : ulong name_len,
186 333 : int excluded ) {
187 333 : int found = 0;
188 333 : fd_der_cursor_t c = { .p=trees, .end=trees+trees_len };
189 645 : while( FD_DER_HAS_MORE( c ) ) {
190 408 : int tree_tag; ulong tree_len;
191 408 : if( FD_UNLIKELY( fd_der_read_tl( &c, &tree_tag, &tree_len ) ||
192 408 : tree_tag!=(int)FD_DER_TAG_SEQUENCE ) ) return -2;
193 408 : fd_der_cursor_t t = { .p=c.p, .end=c.p+tree_len };
194 408 : c.p += tree_len;
195 408 : int base_tag; ulong base_len;
196 408 : if( FD_UNLIKELY( fd_der_read_tl( &t, &base_tag, &base_len ) ) ) return -2;
197 408 : if( base_tag!=tag ) continue;
198 168 : found = 1;
199 168 : int match;
200 168 : switch( tag ) {
201 90 : case FD_DER_TAG_CONTEXT_PRIM(2):
202 90 : match = fd_x509_dns_constraint_matches( t.p, base_len, name, name_len, excluded );
203 90 : break;
204 24 : case FD_DER_TAG_CONTEXT_PRIM(7):
205 24 : match = fd_x509_ip_constraint_matches( t.p, base_len, name, name_len );
206 24 : break;
207 54 : case FD_DER_TAG_CONTEXT(4):
208 54 : match = fd_x509_name_prefix( t.p, base_len, name, name_len );
209 54 : break;
210 0 : default:
211 : /* nameConstraints is critical: a form we cannot match must not
212 : be accepted (RFC 5280 Section 4.2). */
213 0 : return -2;
214 168 : }
215 168 : if( match ) return 1;
216 168 : }
217 237 : return found ? 0 : -1;
218 333 : }
219 :
220 : /* fd_x509_name_constrained checks one name against a CA's permitted
221 : and excluded GeneralSubtrees. */
222 :
223 : static int
224 : fd_x509_name_constrained( uchar const * permitted,
225 : ulong permitted_len,
226 : uchar const * excluded,
227 : ulong excluded_len,
228 : int tag,
229 : uchar const * name,
230 279 : ulong name_len ) {
231 279 : if( excluded_len ) {
232 144 : int match = fd_x509_subtrees_match( excluded, excluded_len, tag, name, name_len, 1 );
233 144 : if( match==1 || match==-2 ) return FD_X509_VERIFY_ERR_NAME_CONSTRAINT;
234 144 : }
235 243 : if( permitted_len ) {
236 189 : int match = fd_x509_subtrees_match( permitted, permitted_len, tag, name, name_len, 0 );
237 189 : if( match==0 || match==-2 ) return FD_X509_VERIFY_ERR_NAME_CONSTRAINT;
238 189 : }
239 198 : return FD_X509_VERIFY_OK;
240 243 : }
241 :
242 : /* fd_x509_check_name_constraints checks the subject DN (against
243 : directoryName subtrees, RFC 5280 Section 6.1.4 (g)) and each SAN of
244 : cert against a CA's permitted and excluded GeneralSubtrees. An
245 : empty subject is skipped, as in OpenSSL and BoringSSL. Subject
246 : emailAddress attributes are not checked against rfc822Name
247 : subtrees. */
248 :
249 : static int
250 : fd_x509_check_name_constraints( int has_name_constraints,
251 : uchar const * permitted,
252 : ulong permitted_len,
253 : uchar const * excluded,
254 : ulong excluded_len,
255 2835 : fd_x509_cert_info_t const * cert ) {
256 2835 : if( !has_name_constraints ) return FD_X509_VERIFY_OK;
257 :
258 159 : if( cert->subject_len>2UL ) {
259 153 : int err = fd_x509_name_constrained( permitted, permitted_len, excluded, excluded_len,
260 153 : FD_DER_TAG_CONTEXT(4), cert->subject, cert->subject_len );
261 153 : if( FD_UNLIKELY( err ) ) return err;
262 153 : }
263 :
264 141 : if( !cert->has_subject_alt_name ) return FD_X509_VERIFY_OK;
265 :
266 126 : fd_der_cursor_t san = { .p=cert->san_general_names,
267 126 : .end=cert->san_general_names+cert->san_general_names_len };
268 189 : while( FD_DER_HAS_MORE( san ) ) {
269 126 : int tag; ulong name_len;
270 126 : if( FD_UNLIKELY( fd_der_read_tl( &san, &tag, &name_len ) ) )
271 0 : return FD_X509_VERIFY_ERR_NAME_CONSTRAINT;
272 126 : uchar const * name = san.p;
273 126 : san.p += name_len;
274 :
275 126 : if( tag==(int)FD_DER_TAG_CONTEXT_PRIM(2) ) {
276 93 : int wildcard = name_len>2UL && name[0]=='*' && name[1]=='.';
277 93 : if( FD_UNLIKELY( wildcard ? !fd_x509_dns_name_valid( (char const *)name+2, name_len-2UL )
278 93 : : !fd_x509_dns_name_valid( (char const *)name, name_len ) ) )
279 0 : return FD_X509_VERIFY_ERR_NAME_CONSTRAINT;
280 93 : } else if( tag==(int)FD_DER_TAG_CONTEXT_PRIM(7) ) {
281 24 : if( FD_UNLIKELY( name_len!=4UL && name_len!=16UL ) )
282 0 : return FD_X509_VERIFY_ERR_NAME_CONSTRAINT;
283 24 : }
284 :
285 126 : int err = fd_x509_name_constrained( permitted, permitted_len, excluded, excluded_len,
286 126 : tag, name, name_len );
287 126 : if( FD_UNLIKELY( err ) ) return err;
288 126 : }
289 63 : return FD_X509_VERIFY_OK;
290 126 : }
291 :
292 : /* fd_x509_check_path_name_constraints applies a CA's name constraints
293 : to every cert in certs[0,cnt). Name constraints do not apply to
294 : non-final self-issued certs (RFC 5280 Section 6.1.4 (a)). */
295 :
296 : static int
297 : fd_x509_check_path_name_constraints( int has_name_constraints,
298 : uchar const * permitted,
299 : ulong permitted_len,
300 : uchar const * excluded,
301 : ulong excluded_len,
302 : fd_x509_cert_info_t const * certs,
303 2064 : ulong cnt ) {
304 4902 : for( ulong j=0UL; j<cnt; j++ ) {
305 2919 : if( j && fd_x509_name_equal( certs[j].issuer, certs[j].issuer_len,
306 855 : certs[j].subject, certs[j].subject_len ) ) continue;
307 2835 : int nc_err = fd_x509_check_name_constraints( has_name_constraints,
308 2835 : permitted, permitted_len,
309 2835 : excluded, excluded_len, &certs[j] );
310 2835 : if( FD_UNLIKELY( nc_err ) ) return nc_err;
311 2835 : }
312 1983 : return FD_X509_VERIFY_OK;
313 2064 : }
314 :
315 : /* fd_x509_check_anchor applies the trust anchor's own constraints to
316 : the path certs[0,cnt) that it terminates: its pathLenConstraint
317 : against the non_self_issued_ca_cnt intermediate CAs on the path, and
318 : its name constraints against every cert. fd_x509_ca_store_load
319 : already rejected anchors whose extKeyUsage excludes serverAuth. */
320 :
321 : static int
322 : fd_x509_check_anchor( fd_x509_ca_entry_t const * ca,
323 : fd_x509_cert_info_t const * certs,
324 : ulong cnt,
325 1266 : ulong non_self_issued_ca_cnt ) {
326 1266 : if( ca->has_path_len_constraint && non_self_issued_ca_cnt>ca->path_len_constraint )
327 12 : return FD_X509_VERIFY_ERR_PATH_LEN;
328 1254 : return fd_x509_check_path_name_constraints(
329 1254 : ca->has_name_constraints,
330 1254 : ca->name_constraints, ca->name_constraints_permitted_len,
331 1254 : ca->name_constraints+ca->name_constraints_permitted_len, ca->name_constraints_excluded_len,
332 1254 : certs, cnt );
333 1266 : }
334 :
335 : /* Implemented as specified by RFC 5280 Section 6.1.3. */
336 : int
337 : fd_x509_verify_chain( uchar const * const * chain_der,
338 : ulong const * chain_der_sz,
339 : ulong chain_cnt,
340 : fd_x509_ca_store_t const * ca_store,
341 : char const * hostname,
342 : ulong hostname_len,
343 1620 : long unix_seconds ) {
344 :
345 1620 : if( FD_UNLIKELY( chain_cnt == 0 ) ) return FD_X509_VERIFY_ERR_CHAIN_BREAK;
346 1617 : if( FD_UNLIKELY( chain_cnt > FD_X509_CHAIN_MAX ) ) return FD_X509_VERIFY_ERR_CHAIN_TOO_LONG;
347 :
348 4440 : for( ulong i=0UL; i<chain_cnt; i++ )
349 2832 : if( FD_UNLIKELY( chain_der_sz[i] > FD_X509_CERT_SZ_MAX ) )
350 3 : return FD_X509_VERIFY_ERR_CERT_TOO_LARGE;
351 :
352 1608 : fd_x509_cert_info_t certs[ FD_X509_CHAIN_MAX ] = {0};
353 :
354 1608 : if( FD_UNLIKELY( fd_x509_cert_parse( chain_der[0], chain_der_sz[0], &certs[0] ) ) )
355 9 : return FD_X509_VERIFY_ERR_PARSE;
356 1599 : if( FD_UNLIKELY( certs[0].key_type==FD_X509_KEY_UNKNOWN ) )
357 0 : return FD_X509_VERIFY_ERR_UNSUPPORTED;
358 :
359 1599 : int time_err = fd_x509_check_validity( &certs[0], unix_seconds );
360 1599 : if( FD_UNLIKELY( time_err ) ) return time_err;
361 :
362 1548 : int usage_err = fd_x509_check_leaf_usage( &certs[0] );
363 1548 : if( FD_UNLIKELY( usage_err ) ) return usage_err;
364 :
365 1515 : if( hostname && hostname_len ) {
366 1224 : if( FD_UNLIKELY( !fd_x509_san_matches( &certs[0], hostname, hostname_len ) ) )
367 48 : return FD_X509_VERIFY_ERR_HOSTNAME;
368 1224 : }
369 :
370 : /* The presented list is leaf first; the issuers after it are in any
371 : order (RFC 8446 Section 4.4.2 tells clients to expect that). path
372 : is built by picking, at each step, an unused presented cert whose
373 : subject names the current issuer and whose key checks out. A
374 : branch that dead-ends short of a trust anchor is backed out of and
375 : the next candidate at that level tried (a cross-signed CA is
376 : presented twice under one subject). The first dead end's error is
377 : reported if no branch works out. Presented certs are parsed
378 : lazily. sig_budget bounds the work on a chain of mutually valid
379 : same-subject CAs; an honest chain needs one verify per cert. */
380 :
381 1467 : FD_STATIC_ASSERT( FD_X509_CHAIN_MAX<=64UL, bitset );
382 1467 : fd_x509_cert_info_t path[ FD_X509_CHAIN_MAX ];
383 1467 : ulong parsed = 1UL; /* bit j: certs[j] is parsed */
384 1467 : ulong used = 1UL; /* bit j: certs[j] is on path */
385 1467 : path[0] = certs[0];
386 :
387 1467 : ulong depth = 0UL;
388 1467 : ulong j_start = 1UL;
389 1467 : ulong non_self_issued_ca_cnt = 0UL;
390 1467 : ulong sig_budget = 4UL*FD_X509_CHAIN_MAX;
391 1467 : int first_err = FD_X509_VERIFY_OK;
392 2277 : for(;;) {
393 2277 : fd_x509_cert_info_t const * cur = &path[ depth ];
394 :
395 : /* A trust anchor for this cert's issuer completes the path. Peers
396 : routinely append cross-signatures leading up to some older root,
397 : so the certs beyond this point are not ours to walk: they chain to
398 : an anchor we do not need and may not even hold. Skipped when
399 : resuming after a backtrack: the anchors already failed here. */
400 :
401 2277 : int anchored = 0;
402 2277 : int anchor_err = FD_X509_VERIFY_OK;
403 2277 : if( j_start==1UL ) {
404 2190 : ulong idx = 0UL;
405 2190 : for( fd_x509_ca_entry_t const * ca;
406 2217 : !!( ca = fd_x509_ca_store_find_next( ca_store, cur->issuer, cur->issuer_len, &idx ) ); ) {
407 1278 : anchored = 1;
408 :
409 : /* A name match is not a key match, so keep trying the remaining
410 : anchors sharing this subject. */
411 :
412 1278 : if( FD_UNLIKELY( !sig_budget-- ) ) return first_err ? first_err : FD_X509_VERIFY_ERR_CHAIN_BREAK;
413 1278 : int sig_rc = fd_x509_verify_sig( cur, ca->pubkey, ca->pubkey_len, ca->key_type );
414 1278 : if( FD_UNLIKELY( sig_rc > 0 ) ) { anchor_err = FD_X509_VERIFY_ERR_UNSUPPORTED; continue; }
415 1278 : if( sig_rc ) continue;
416 :
417 1266 : anchor_err = fd_x509_check_anchor( ca, path, depth+1UL, non_self_issued_ca_cnt );
418 1266 : if( !anchor_err ) return FD_X509_VERIFY_OK;
419 1266 : }
420 2190 : }
421 :
422 : /* Not anchored here, so find the issuer among the presented certs.
423 : Candidates that name-match but fail a check are passed over in
424 : favour of a later candidate (cross-signed CAs share a subject);
425 : the first such failure is reported if none of them work out. */
426 :
427 1026 : int cand_err = FD_X509_VERIFY_OK;
428 1026 : int any_left = 0;
429 1026 : ulong pick = ULONG_MAX;
430 1569 : for( ulong j = j_start; j < chain_cnt; j++ ) {
431 1275 : if( used & (1UL<<j) ) continue;
432 939 : any_left = 1;
433 :
434 939 : if( !( parsed & (1UL<<j) ) ) {
435 864 : if( FD_UNLIKELY( fd_x509_cert_parse( chain_der[j], chain_der_sz[j], &certs[j] ) ) )
436 9 : return FD_X509_VERIFY_ERR_PARSE;
437 855 : parsed |= 1UL<<j;
438 855 : }
439 930 : fd_x509_cert_info_t const * cand = &certs[j];
440 :
441 930 : if( !fd_x509_name_equal( cur->issuer, cur->issuer_len, cand->subject, cand->subject_len ) )
442 90 : continue;
443 :
444 840 : int err = fd_x509_check_validity( cand, unix_seconds );
445 840 : if( !err && !cand->is_ca ) err = FD_X509_VERIFY_ERR_CA_FLAG;
446 :
447 : /* pathLenConstraint counts non-self-issued intermediate CA certs
448 : between this issuer and the leaf. The leaf itself never counts. */
449 840 : if( !err && cand->has_path_len_constraint &&
450 840 : non_self_issued_ca_cnt>cand->path_len_constraint )
451 9 : err = FD_X509_VERIFY_ERR_PATH_LEN;
452 :
453 840 : if( !err && cand->has_key_usage && !( cand->key_usage & FD_X509_KU_KEY_CERT_SIGN ) )
454 6 : err = FD_X509_VERIFY_ERR_KEY_USAGE;
455 :
456 840 : if( !err ) err = fd_x509_check_eku( cand );
457 :
458 840 : if( !err ) err = fd_x509_check_path_name_constraints(
459 810 : cand->has_name_constraints,
460 810 : cand->name_constraints_permitted, cand->name_constraints_permitted_len,
461 810 : cand->name_constraints_excluded, cand->name_constraints_excluded_len,
462 810 : path, depth+1UL );
463 :
464 840 : if( !err ) {
465 732 : if( FD_UNLIKELY( !sig_budget-- ) ) return first_err ? first_err : FD_X509_VERIFY_ERR_CHAIN_BREAK;
466 732 : int sig_rc = fd_x509_verify_sig( cur, cand->pubkey, cand->pubkey_len, cand->key_type );
467 732 : if( sig_rc < 0 ) err = FD_X509_VERIFY_ERR_SIG;
468 732 : if( sig_rc > 0 ) err = FD_X509_VERIFY_ERR_UNSUPPORTED;
469 732 : }
470 :
471 840 : if( !err ) { pick = j; break; }
472 117 : if( !cand_err ) cand_err = err;
473 117 : }
474 :
475 1017 : if( pick != ULONG_MAX ) {
476 723 : used |= 1UL<<pick;
477 723 : path[ depth+1 ] = certs[ pick ];
478 :
479 : /* A self-issued rollover CA does not consume path length budget. */
480 723 : if( !fd_x509_name_equal( certs[pick].issuer, certs[pick].issuer_len,
481 723 : certs[pick].subject, certs[pick].subject_len ) )
482 684 : non_self_issued_ca_cnt++;
483 :
484 723 : depth++;
485 723 : j_start = 1UL;
486 723 : continue;
487 723 : }
488 :
489 294 : if( !first_err ) {
490 213 : if( cand_err ) first_err = cand_err;
491 102 : else if( anchor_err ) first_err = anchor_err;
492 87 : else if( anchored ) first_err = FD_X509_VERIFY_ERR_SIG;
493 78 : else first_err = any_left ? FD_X509_VERIFY_ERR_CHAIN_BREAK : FD_X509_VERIFY_ERR_NO_TRUST_ANCHOR;
494 213 : }
495 294 : if( !depth ) return first_err;
496 :
497 : /* Back up one level and release the cert picked there. path holds
498 : copies, so the pick is the used cert whose tbs pointer matches. */
499 :
500 87 : depth--;
501 87 : ulong prev = 1UL;
502 168 : while( !( used & (1UL<<prev) ) || certs[prev].tbs!=path[depth+1].tbs ) prev++;
503 87 : used &= ~(1UL<<prev);
504 87 : if( !fd_x509_name_equal( certs[prev].issuer, certs[prev].issuer_len,
505 87 : certs[prev].subject, certs[prev].subject_len ) )
506 57 : non_self_issued_ca_cnt--;
507 87 : j_start = prev+1UL;
508 87 : }
509 1467 : }
510 :
511 : int
512 : fd_x509_verify_tls_cert_msg( uchar const * cert_msg,
513 : ulong cert_msg_sz,
514 : fd_x509_ca_store_t const * ca_store,
515 : char const * hostname,
516 : ulong hostname_len,
517 1137 : long unix_seconds ) {
518 :
519 1137 : if( FD_UNLIKELY( !cert_msg ) ) return FD_X509_VERIFY_ERR_PARSE;
520 :
521 1137 : uchar const * p = cert_msg;
522 1137 : uchar const * end = cert_msg + cert_msg_sz;
523 :
524 : /* A server Certificate sent for the main handshake must have an empty
525 : certificate_request_context (RFC 8446 Section 4.4.2). */
526 1137 : if( FD_UNLIKELY( (ulong)(end-p)<1UL ) ) return FD_X509_VERIFY_ERR_PARSE;
527 1134 : ulong ctx_len = *p++;
528 1134 : if( FD_UNLIKELY( ctx_len ) ) return FD_X509_VERIFY_ERR_PARSE;
529 :
530 : /* certificate_list<0..2^24-1> */
531 1128 : if( FD_UNLIKELY( (ulong)(end-p)<3UL ) ) return FD_X509_VERIFY_ERR_PARSE;
532 1119 : ulong list_len = ( (ulong)p[0]<<16 ) | ( (ulong)p[1]<<8 ) | (ulong)p[2];
533 1119 : p += 3;
534 1119 : if( FD_UNLIKELY( list_len != (ulong)( end-p ) ) ) return FD_X509_VERIFY_ERR_PARSE;
535 1116 : uchar const * list_end = p + list_len;
536 :
537 1116 : uchar const * chain_der [ FD_X509_CHAIN_MAX ];
538 1116 : ulong chain_der_sz[ FD_X509_CHAIN_MAX ];
539 1116 : ulong chain_cnt = 0UL;
540 :
541 3006 : while( p < list_end ) {
542 1905 : if( FD_UNLIKELY( chain_cnt >= FD_X509_CHAIN_MAX ) ) return FD_X509_VERIFY_ERR_CHAIN_TOO_LONG;
543 :
544 : /* cert_data<1..2^24-1> */
545 1902 : if( FD_UNLIKELY( (ulong)(list_end-p)<3UL ) ) return FD_X509_VERIFY_ERR_PARSE;
546 1902 : ulong cert_len = ( (ulong)p[0]<<16 ) | ( (ulong)p[1]<<8 ) | (ulong)p[2];
547 1902 : p += 3;
548 1902 : if( FD_UNLIKELY( !cert_len ) )
549 0 : return FD_X509_VERIFY_ERR_PARSE;
550 1902 : if( FD_UNLIKELY( cert_len > FD_X509_CERT_SZ_MAX ) )
551 3 : return FD_X509_VERIFY_ERR_CERT_TOO_LARGE;
552 1899 : if( FD_UNLIKELY( (ulong)(list_end-p)<cert_len ) )
553 0 : return FD_X509_VERIFY_ERR_PARSE;
554 :
555 1899 : chain_der [ chain_cnt ] = p;
556 1899 : chain_der_sz[ chain_cnt ] = cert_len;
557 1899 : chain_cnt++;
558 1899 : p += cert_len;
559 :
560 : /* extensions<0..2^16-1> */
561 1899 : if( FD_UNLIKELY( (ulong)(list_end-p)<2UL ) ) return FD_X509_VERIFY_ERR_PARSE;
562 1896 : ulong ext_len = ( (ulong)p[0]<<8 ) | (ulong)p[1];
563 1896 : p += 2;
564 1896 : if( FD_UNLIKELY( (ulong)(list_end-p)<ext_len ) ) return FD_X509_VERIFY_ERR_PARSE;
565 1893 : uchar const * ext_end = p + ext_len;
566 :
567 1896 : while( p < ext_end ) {
568 6 : if( FD_UNLIKELY( (ulong)(ext_end-p)<4UL ) ) return FD_X509_VERIFY_ERR_PARSE;
569 3 : p += 2; /* ExtensionType */
570 3 : ulong ext_data_len = ( (ulong)p[0]<<8 ) | (ulong)p[1];
571 3 : p += 2;
572 3 : if( FD_UNLIKELY( (ulong)(ext_end-p)<ext_data_len ) ) return FD_X509_VERIFY_ERR_PARSE;
573 3 : p += ext_data_len;
574 3 : }
575 1893 : }
576 :
577 1101 : if( FD_UNLIKELY( !chain_cnt ) ) return FD_X509_VERIFY_ERR_PARSE;
578 :
579 1098 : return fd_x509_verify_chain( chain_der, chain_der_sz, chain_cnt,
580 1098 : ca_store, hostname, hostname_len, unix_seconds );
581 1101 : }
|