From: Viktor Dukhovni Date: Sat, 19 Mar 2016 02:09:41 +0000 (-0400) Subject: Move peer chain security checks into x509_vfy.c X-Git-Tag: OpenSSL_1_1_0-pre5~154 X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=fbb82a60dcbe820714a246ab3e7617eaf3a7b656 Move peer chain security checks into x509_vfy.c A new X509_VERIFY_PARAM_set_auth_level() function sets the authentication security level. For verification of SSL peers, this is automatically set from the SSL security level. Otherwise, for now, the authentication security level remains at (effectively) 0 by default. The new "-auth_level" verify(1) option is available in all the command-line tools that support the standard verify(1) options. New verify(1) tests added to check enforcement of chain signature and public key security levels. Also added new tests of enforcement of the verify_depth limit. Updated documentation. Reviewed-by: Dr. Stephen Henson --- diff --git a/apps/apps.h b/apps/apps.h index 434ca54b7d..a310dd2b78 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -180,6 +180,7 @@ void wait_for_async(SSL *s); OPT_V_POLICY_PRINT, OPT_V_CHECK_SS_SIG, OPT_V_TRUSTED_FIRST, \ OPT_V_SUITEB_128_ONLY, OPT_V_SUITEB_128, OPT_V_SUITEB_192, \ OPT_V_PARTIAL_CHAIN, OPT_V_NO_ALT_CHAINS, OPT_V_NO_CHECK_TIME, \ + OPT_V_VERIFY_AUTH_LEVEL, \ OPT_V__LAST # define OPT_V_OPTIONS \ @@ -187,8 +188,10 @@ void wait_for_async(SSL *s); { "purpose", OPT_V_PURPOSE, 's', \ "certificate chain purpose"}, \ { "verify_name", OPT_V_VERIFY_NAME, 's', "verification policy name"}, \ - { "verify_depth", OPT_V_VERIFY_DEPTH, 'p', \ - "chain depth limit"}, \ + { "verify_depth", OPT_V_VERIFY_DEPTH, 'n', \ + "chain depth limit" }, \ + { "auth_level", OPT_V_VERIFY_AUTH_LEVEL, 'n', \ + "chain authentication security level" }, \ { "attime", OPT_V_ATTIME, 'M', "verification epoch time" }, \ { "verify_hostname", OPT_V_VERIFY_HOSTNAME, 's', \ "expected peer hostname" }, \ @@ -235,6 +238,7 @@ void wait_for_async(SSL *s); case OPT_V_PURPOSE: \ case OPT_V_VERIFY_NAME: \ case OPT_V_VERIFY_DEPTH: \ + case OPT_V_VERIFY_AUTH_LEVEL: \ case OPT_V_ATTIME: \ case OPT_V_VERIFY_HOSTNAME: \ case OPT_V_VERIFY_EMAIL: \ diff --git a/apps/opt.c b/apps/opt.c index af994bb743..462894a938 100644 --- a/apps/opt.c +++ b/apps/opt.c @@ -526,6 +526,11 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm) if (i >= 0) X509_VERIFY_PARAM_set_depth(vpm, i); break; + case OPT_V_VERIFY_AUTH_LEVEL: + i = atoi(opt_arg()); + if (i >= 0) + X509_VERIFY_PARAM_set_auth_level(vpm, i); + break; case OPT_V_ATTIME: if (!opt_imax(opt_arg(), &t)) return 0; diff --git a/crypto/x509/x509_lcl.h b/crypto/x509/x509_lcl.h index 0726201e8f..603c17737f 100644 --- a/crypto/x509/x509_lcl.h +++ b/crypto/x509/x509_lcl.h @@ -70,6 +70,7 @@ struct X509_VERIFY_PARAM_st { int purpose; /* purpose to check untrusted certificates */ int trust; /* trust setting to check */ int depth; /* Verify depth */ + int auth_level; /* Security level for chain verification */ STACK_OF(ASN1_OBJECT) *policies; /* Permissible policies */ /* Peer identity details */ STACK_OF(OPENSSL_STRING) *hosts; /* Set of acceptable names */ diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c index f7f27e97ef..8a9a7f0444 100644 --- a/crypto/x509/x509_txt.c +++ b/crypto/x509/x509_txt.c @@ -203,6 +203,12 @@ const char *X509_verify_cert_error_string(long n) return ("IP address mismatch"); case X509_V_ERR_DANE_NO_MATCH: return ("No matching DANE TLSA records"); + case X509_V_ERR_EE_KEY_TOO_SMALL: + return ("EE certificate key too weak"); + case X509_V_ERR_CA_KEY_TOO_SMALL: + return ("CA certificate key too weak"); + case X509_V_ERR_CA_MD_TOO_WEAK: + return ("CA signature digest algorithm too weak"); default: /* Printing an error number into a static buffer is not thread-safe */ diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 92f1c5c447..10fbeeff6d 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -126,6 +126,8 @@ static int check_cert(X509_STORE_CTX *ctx); static int check_policy(X509_STORE_CTX *ctx); static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); static int check_dane_issuer(X509_STORE_CTX *ctx, int depth); +static int check_key_level(X509_STORE_CTX *ctx, X509 *cert); +static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert); static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, unsigned int *preasons, X509_CRL *crl, X509 *x); @@ -221,6 +223,35 @@ static int verify_cb_crl(X509_STORE_CTX *ctx, int err) return ctx->verify_cb(0, ctx); } +static int check_auth_level(X509_STORE_CTX *ctx) +{ + int i; + int num = sk_X509_num(ctx->chain); + + if (ctx->param->auth_level <= 0) + return 1; + + for (i = 0; i < num; ++i) { + X509 *cert = sk_X509_value(ctx->chain, i); + + /* + * We've already checked the security of the leaf key, so here we only + * check the security of issuer keys. + */ + if (i > 0 && !check_key_level(ctx, cert) && + verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_KEY_TOO_SMALL) == 0) + return 0; + /* + * We also check the signature algorithm security of all certificates + * except those of the trust anchor at index num-1. + */ + if (i < num - 1 && !check_sig_level(ctx, cert) && + verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_MD_TOO_WEAK) == 0) + return 0; + } + return 1; +} + static int verify_chain(X509_STORE_CTX *ctx) { int err; @@ -232,6 +263,7 @@ static int verify_chain(X509_STORE_CTX *ctx) */ if ((ok = build_chain(ctx)) == 0 || (ok = check_chain_extensions(ctx)) == 0 || + (ok = check_auth_level(ctx)) == 0 || (ok = check_name_constraints(ctx)) == 0 || (ok = check_id(ctx)) == 0 || 1) X509_get_pubkey_parameters(NULL, ctx->chain); @@ -294,6 +326,11 @@ int X509_verify_cert(X509_STORE_CTX *ctx) X509_up_ref(ctx->cert); ctx->num_untrusted = 1; + /* If the peer's public key is too weak, we can stop early. */ + if (!check_key_level(ctx, ctx->cert) && + !verify_cb_cert(ctx, ctx->cert, 0, X509_V_ERR_EE_KEY_TOO_SMALL)) + return 0; + /* * If dane->trecs is an empty stack, we'll fail, since the user enabled * DANE. If none of the TLSA records were usable, and it makes sense to @@ -308,20 +345,19 @@ int X509_verify_cert(X509_STORE_CTX *ctx) /* * Given a STACK_OF(X509) find the issuer of cert (if any) */ - static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x) { int i; - X509 *issuer, *rv = NULL;; + for (i = 0; i < sk_X509_num(sk); i++) { - issuer = sk_X509_value(sk, i); - if (ctx->check_issued(ctx, x, issuer)) { - rv = issuer; - if (x509_check_cert_time(ctx, rv, -1)) - break; - } + X509 *issuer = sk_X509_value(sk, i); + + if (!ctx->check_issued(ctx, x, issuer)) + continue; + if (x509_check_cert_time(ctx, issuer, -1)) + return issuer; } - return rv; + return NULL; } /* Given a possible certificate and issuer check them */ @@ -2656,6 +2692,19 @@ static int dane_verify(X509_STORE_CTX *ctx) return verify_chain(ctx); } +/* Get issuer, without duplicate suppression */ +static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *cert) +{ + STACK_OF(X509) *saved_chain = ctx->chain; + int ok; + + ctx->chain = NULL; + ok = ctx->get_issuer(issuer, ctx, cert); + ctx->chain = saved_chain; + + return ok; +} + static int build_chain(X509_STORE_CTX *ctx) { struct dane_st *dane = (struct dane_st *)ctx->dane; @@ -2735,12 +2784,19 @@ static int build_chain(X509_STORE_CTX *ctx) /* * Look in the trust store if enabled for first lookup, or we've run - * out of untrusted issuers and search here is not disabled. When - * we exceed the depth limit, we simulate absence of a match. + * out of untrusted issuers and search here is not disabled. When we + * reach the depth limit, we stop extending the chain, if by that point + * we've not found a trust-anchor, any trusted chain would be too long. + * + * The error reported to the application verify callback is at the + * maximal valid depth with the current certificate equal to the last + * not ultimately-trusted issuer. For example, with verify_depth = 0, + * the callback will report errors at depth=1 when the immediate issuer + * of the leaf certificate is not a trust anchor. No attempt will be + * made to locate an issuer for that certificate, since such a chain + * would be a-priori too long. */ if ((search & S_DOTRUSTED) != 0) { - STACK_OF(X509) *hide = ctx->chain; - i = num = sk_X509_num(ctx->chain); if ((search & S_DOALTERNATE) != 0) { /* @@ -2762,10 +2818,7 @@ static int build_chain(X509_STORE_CTX *ctx) } x = sk_X509_value(ctx->chain, i-1); - /* Suppress duplicate suppression */ - ctx->chain = NULL; - ok = (depth < num) ? 0 : ctx->get_issuer(&xtmp, ctx, x); - ctx->chain = hide; + ok = (depth < num) ? 0 : get_issuer(&xtmp, ctx, x); if (ok < 0) { trust = X509_TRUST_REJECTED; @@ -2892,12 +2945,12 @@ static int build_chain(X509_STORE_CTX *ctx) num = sk_X509_num(ctx->chain); OPENSSL_assert(num == ctx->num_untrusted); x = sk_X509_value(ctx->chain, num-1); - xtmp = (depth < num) ? NULL : find_issuer(ctx, sktmp, x); /* * Once we run out of untrusted issuers, we stop looking for more * and start looking only in the trust store if enabled. */ + xtmp = (ss || depth < num) ? NULL : find_issuer(ctx, sktmp, x); if (xtmp == NULL) { search &= ~S_DOUNTRUSTED; if (may_trusted) @@ -2905,22 +2958,20 @@ static int build_chain(X509_STORE_CTX *ctx) continue; } - if (!sk_X509_push(ctx->chain, x = xtmp)) { + /* Drop this issuer from future consideration */ + (void) sk_X509_delete_ptr(sktmp, xtmp); + + if (!sk_X509_push(ctx->chain, xtmp)) { X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); trust = X509_TRUST_REJECTED; search = 0; continue; } - X509_up_ref(x); + + X509_up_ref(x = xtmp); ++ctx->num_untrusted; ss = cert_self_signed(xtmp); - /* - * Not strictly necessary, but saves cycles looking at the same - * certificates over and over. - */ - (void) sk_X509_delete_ptr(sktmp, x); - /* * Check for DANE-TA trust of the topmost untrusted certificate. */ @@ -2974,3 +3025,60 @@ static int build_chain(X509_STORE_CTX *ctx) X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); } } + +static const int minbits_table[] = { 80, 112, 128, 192, 256 }; +static const int NUM_AUTH_LEVELS = OSSL_NELEM(minbits_table); + +/* + * Check whether the public key of ``cert`` meets the security level of + * ``ctx``. + * + * Returns 1 on success, 0 otherwise. + */ +static int check_key_level(X509_STORE_CTX *ctx, X509 *cert) +{ + EVP_PKEY *pkey = X509_get0_pubkey(cert); + int level = ctx->param->auth_level; + + /* Unsupported or malformed keys are not secure */ + if (pkey == NULL) + return 0; + + if (level <= 0) + return 1; + if (level > NUM_AUTH_LEVELS) + level = NUM_AUTH_LEVELS; + + return EVP_PKEY_security_bits(pkey) >= minbits_table[level - 1]; +} + +/* + * Check whether the signature digest algorithm of ``cert`` meets the security + * level of ``ctx``. Should not be checked for trust anchors (whether + * self-signed or otherwise). + * + * Returns 1 on success, 0 otherwise. + */ +static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert) +{ + int nid = X509_get_signature_nid(cert); + int mdnid = NID_undef; + int secbits = -1; + int level = ctx->param->auth_level; + + if (level <= 0) + return 1; + if (level > NUM_AUTH_LEVELS) + level = NUM_AUTH_LEVELS; + + /* Lookup signature algorithm digest */ + if (nid && OBJ_find_sigid_algs(nid, &mdnid, NULL)) { + const EVP_MD *md; + + /* Assume 4 bits of collision resistance for each hash octet */ + if (mdnid != NID_undef && (md = EVP_get_digestbynid(mdnid)) != NULL) + secbits = EVP_MD_size(md) * 4; + } + + return secbits >= minbits_table[level - 1]; +} diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index 41b0fde4a5..4a0bed021c 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -140,6 +140,7 @@ static void x509_verify_param_zero(X509_VERIFY_PARAM *param) param->inh_flags = 0; param->flags = 0; param->depth = -1; + param->auth_level = -1; /* -1 means unset, 0 is explicit */ sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); param->policies = NULL; sk_OPENSSL_STRING_pop_free(param->hosts, str_free); @@ -245,6 +246,7 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, x509_verify_param_copy(purpose, 0); x509_verify_param_copy(trust, X509_TRUST_DEFAULT); x509_verify_param_copy(depth, -1); + x509_verify_param_copy(auth_level, -1); /* If overwrite or check time not set, copy across */ @@ -368,6 +370,11 @@ void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth) param->depth = depth; } +void X509_VERIFY_PARAM_set_auth_level(X509_VERIFY_PARAM *param, int auth_level) +{ + param->auth_level = auth_level; +} + void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t) { param->check_time = t; @@ -493,6 +500,11 @@ int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param) return param->depth; } +int X509_VERIFY_PARAM_get_auth_level(const X509_VERIFY_PARAM *param) +{ + return param->auth_level; +} + const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param) { return param->name; @@ -515,6 +527,7 @@ static const X509_VERIFY_PARAM default_table[] = { 0, /* purpose */ 0, /* trust */ 100, /* depth */ + -1, /* auth_level */ NULL, /* policies */ vpm_empty_id}, { @@ -525,6 +538,7 @@ static const X509_VERIFY_PARAM default_table[] = { X509_PURPOSE_SMIME_SIGN, /* purpose */ X509_TRUST_EMAIL, /* trust */ -1, /* depth */ + -1, /* auth_level */ NULL, /* policies */ vpm_empty_id}, { @@ -535,6 +549,7 @@ static const X509_VERIFY_PARAM default_table[] = { X509_PURPOSE_SMIME_SIGN, /* purpose */ X509_TRUST_EMAIL, /* trust */ -1, /* depth */ + -1, /* auth_level */ NULL, /* policies */ vpm_empty_id}, { @@ -545,6 +560,7 @@ static const X509_VERIFY_PARAM default_table[] = { X509_PURPOSE_SSL_CLIENT, /* purpose */ X509_TRUST_SSL_CLIENT, /* trust */ -1, /* depth */ + -1, /* auth_level */ NULL, /* policies */ vpm_empty_id}, { @@ -555,6 +571,7 @@ static const X509_VERIFY_PARAM default_table[] = { X509_PURPOSE_SSL_SERVER, /* purpose */ X509_TRUST_SSL_SERVER, /* trust */ -1, /* depth */ + -1, /* auth_level */ NULL, /* policies */ vpm_empty_id} }; diff --git a/doc/apps/cms.pod b/doc/apps/cms.pod index 36e6b3ca3a..42c351489c 100644 --- a/doc/apps/cms.pod +++ b/doc/apps/cms.pod @@ -58,6 +58,7 @@ B B [B<-trusted_first>] [B<-no_alt_chains>] [B<-use_deltas>] +[B<-auth_level num>] [B<-verify_depth num>] [B<-verify_email email>] [B<-verify_hostname hostname>] @@ -475,8 +476,8 @@ B<-explicit_policy>, B<-extended_crl>, B<-ignore_critical>, B<-inhibit_any>, B<-inhibit_map>, B<-no_alt_chains>, B<-partial_chain>, B<-policy>, B<-policy_check>, B<-policy_print>, B<-purpose>, B<-suiteB_128>, B<-suiteB_128_only>, B<-suiteB_192>, B<-trusted_first>, B<-use_deltas>, -B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, B<-verify_ip>, -B<-verify_name>, B<-x509_strict> +B<-auth_level>, B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, +B<-verify_ip>, B<-verify_name>, B<-x509_strict> Set various certificate chain validation options. See the L manual page for details. diff --git a/doc/apps/ocsp.pod b/doc/apps/ocsp.pod index be195bcb30..c796fd5966 100644 --- a/doc/apps/ocsp.pod +++ b/doc/apps/ocsp.pod @@ -53,6 +53,7 @@ B B [B<-trusted_first>] [B<-no_alt_chains>] [B<-use_deltas>] +[B<-auth_level num>] [B<-verify_depth num>] [B<-verify_email email>] [B<-verify_hostname hostname>] @@ -197,11 +198,11 @@ B<-explicit_policy>, B<-extended_crl>, B<-ignore_critical>, B<-inhibit_any>, B<-inhibit_map>, B<-no_alt_chains>, B<-partial_chain>, B<-policy>, B<-policy_check>, B<-policy_print>, B<-purpose>, B<-suiteB_128>, B<-suiteB_128_only>, B<-suiteB_192>, B<-trusted_first>, B<-use_deltas>, -B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, B<-verify_ip>, -B<-verify_name>, B<-x509_strict> +B<-auth_level>, B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, +B<-verify_ip>, B<-verify_name>, B<-x509_strict> Set different certificate verification options. -See L|verify(1)> manual page for details. +See L manual page for details. =item B<-verify_other file> diff --git a/doc/apps/s_client.pod b/doc/apps/s_client.pod index 1873293ea8..881fbcfefe 100644 --- a/doc/apps/s_client.pod +++ b/doc/apps/s_client.pod @@ -45,6 +45,7 @@ B B [B<-trusted_first>] [B<-no_alt_chains>] [B<-use_deltas>] +[B<-auth_level num>] [B<-verify_depth num>] [B<-verify_email email>] [B<-verify_hostname hostname>] @@ -229,8 +230,8 @@ B<-explicit_policy>, B<-extended_crl>, B<-ignore_critical>, B<-inhibit_any>, B<-inhibit_map>, B<-no_alt_chains>, B<-partial_chain>, B<-policy>, B<-policy_check>, B<-policy_print>, B<-purpose>, B<-suiteB_128>, B<-suiteB_128_only>, B<-suiteB_192>, B<-trusted_first>, B<-use_deltas>, -B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, B<-verify_ip>, -B<-verify_name>, B<-x509_strict> +B<-auth_level>, B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, +B<-verify_ip>, B<-verify_name>, B<-x509_strict> Set various certificate chain validation options. See the L manual page for details. diff --git a/doc/apps/s_server.pod b/doc/apps/s_server.pod index 25e544468a..08554f4530 100644 --- a/doc/apps/s_server.pod +++ b/doc/apps/s_server.pod @@ -55,6 +55,7 @@ B B [B<-trusted_first>] [B<-no_alt_chains>] [B<-use_deltas>] +[B<-auth_level num>] [B<-verify_depth num>] [B<-verify_return_error>] [B<-verify_email email>] @@ -234,8 +235,8 @@ B<-explicit_policy>, B<-extended_crl>, B<-ignore_critical>, B<-inhibit_any>, B<-inhibit_map>, B<-no_alt_chains>, B<-partial_chain>, B<-policy>, B<-policy_check>, B<-policy_print>, B<-purpose>, B<-suiteB_128>, B<-suiteB_128_only>, B<-suiteB_192>, B<-trusted_first>, B<-use_deltas>, -B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, B<-verify_ip>, -B<-verify_name>, B<-x509_strict> +B<-auth_level>, B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, +B<-verify_ip>, B<-verify_name>, B<-x509_strict> Set different peer certificate verification options. See the L manual page for details. diff --git a/doc/apps/smime.pod b/doc/apps/smime.pod index 418d8faa2d..e6323ad0b0 100644 --- a/doc/apps/smime.pod +++ b/doc/apps/smime.pod @@ -40,6 +40,7 @@ B B [B<-trusted_first>] [B<-no_alt_chains>] [B<-use_deltas>] +[B<-auth_level num>] [B<-verify_depth num>] [B<-verify_email email>] [B<-verify_hostname hostname>] @@ -307,8 +308,8 @@ B<-explicit_policy>, B<-extended_crl>, B<-ignore_critical>, B<-inhibit_any>, B<-inhibit_map>, B<-no_alt_chains>, B<-partial_chain>, B<-policy>, B<-policy_check>, B<-policy_print>, B<-purpose>, B<-suiteB_128>, B<-suiteB_128_only>, B<-suiteB_192>, B<-trusted_first>, B<-use_deltas>, -B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, B<-verify_ip>, -B<-verify_name>, B<-x509_strict> +B<-auth_level>, B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, +B<-verify_ip>, B<-verify_name>, B<-x509_strict> Set various options of certificate chain verification. See L manual page for details. diff --git a/doc/apps/ts.pod b/doc/apps/ts.pod index 93ea9e059a..e64e5fcf34 100644 --- a/doc/apps/ts.pod +++ b/doc/apps/ts.pod @@ -73,6 +73,7 @@ I [-suiteB_192] [-trusted_first] [-use_deltas] +[-auth_level num] [-verify_depth num] [-verify_email email] [-verify_hostname hostname] @@ -371,17 +372,15 @@ all intermediate CA certificates unless the response includes them. =item I -The options [-attime timestamp], [-check_ss_sig], [-crl_check], -[-crl_check_all], [-explicit_policy], [-extended_crl], -[-ignore_critical], [-inhibit_any], [-inhibit_map], -[-issuer_checks], [-no_alt_chains], [-no_check_time], -[-partial_chain], [-policy arg], [-policy_check], -[-policy_print], [-purpose purpose], [-suiteB_128], -[-suiteB_128_only], [-suiteB_192], [-trusted_first], -[-use_deltas], [-verify_depth num], [-verify_email email], -[-verify_hostname hostname], [-verify_ip ip], [-verify_name name], -and [-x509_strict] can be used to control timestamp verification. -See L. +The options B<-attime timestamp>, B<-check_ss_sig>, B<-crl_check>, +B<-crl_check_all>, B<-explicit_policy>, B<-extended_crl>, B<-ignore_critical>, +B<-inhibit_any>, B<-inhibit_map>, B<-issuer_checks>, B<-no_alt_chains>, +B<-no_check_time>, B<-partial_chain>, B<-policy>, B<-policy_check>, +B<-policy_print>, B<-purpose>, B<-suiteB_128>, B<-suiteB_128_only>, +B<-suiteB_192>, B<-trusted_first>, B<-use_deltas>, B<-auth_level>, +B<-verify_depth>, B<-verify_email>, B<-verify_hostname>, B<-verify_ip>, +B<-verify_name>, and B<-x509_strict> can be used to control timestamp +verification. See L. =back diff --git a/doc/apps/verify.pod b/doc/apps/verify.pod index ecde35fe8a..96d6be4a4d 100644 --- a/doc/apps/verify.pod +++ b/doc/apps/verify.pod @@ -38,6 +38,7 @@ B B [B<-trusted file>] [B<-use_deltas>] [B<-verbose>] +[B<-auth_level level>] [B<-verify_depth num>] [B<-verify_email email>] [B<-verify_hostname hostname>] @@ -227,9 +228,30 @@ Enable support for delta CRLs. Print extra information about the operations being performed. +=item B<-auth_level level> + +Set the certificate chain authentication security level to B. +The authentication security level determines the acceptable signature and +public key strength when verifying certificate chains. +For a certificate chain to validate, the public keys of all the certificates +must meet the specified security B. +The signature algorithm security level is enforced for all the certificates in +the chain except for the chain's I, which is either directly +trusted or validated by means other than its signature. +See L for the definitions of the available +levels. +The default security level is -1, or "not set". +At security level 0 or lower all algorithms are acceptable. +Security level 1 requires at least 80-bit-equivalent security and is broadly +interoperable, though it will, for example, reject MD5 signatures or RSA keys +shorter than 1024 bits. + =item B<-verify_depth num> -Limit the maximum depth of the certificate chain to B certificates. +Limit the certificate chain to B intermediate CA certificates. +A maximal depth chain can have up to B certificates, since neither the +end-entity certificate nor the trust-anchor certificate count against the +B<-verify_depth> limit. =item B<-verify_email email> diff --git a/doc/crypto/X509_VERIFY_PARAM_set_flags.pod b/doc/crypto/X509_VERIFY_PARAM_set_flags.pod index 6fb33edd91..04f521506f 100644 --- a/doc/crypto/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/crypto/X509_VERIFY_PARAM_set_flags.pod @@ -2,15 +2,16 @@ =head1 NAME -X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_get_flags, X509_VERIFY_PARAM_set_purpose, X509_VERIFY_PARAM_set_trust, X509_VERIFY_PARAM_set_depth, X509_VERIFY_PARAM_get_depth, X509_VERIFY_PARAM_set_time, X509_VERIFY_PARAM_add0_policy, X509_VERIFY_PARAM_set1_policies, X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_add1_host, X509_VERIFY_PARAM_set_hostflags, X509_VERIFY_PARAM_get0_peername, X509_VERIFY_PARAM_set1_email, X509_VERIFY_PARAM_set1_ip, X509_VERIFY_PARAM_set1_ip_asc - X509 verification parameters +X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_get_flags, X509_VERIFY_PARAM_set_purpose, X509_VERIFY_PARAM_set_trust, X509_VERIFY_PARAM_set_depth, X509_VERIFY_PARAM_get_depth, X509_VERIFY_PARAM_set_auth_level, X509_VERIFY_PARAM_get_auth_level, X509_VERIFY_PARAM_set_time, X509_VERIFY_PARAM_add0_policy, X509_VERIFY_PARAM_set1_policies, X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_add1_host, X509_VERIFY_PARAM_set_hostflags, X509_VERIFY_PARAM_get0_peername, X509_VERIFY_PARAM_set1_email, X509_VERIFY_PARAM_set1_ip, X509_VERIFY_PARAM_set1_ip_asc - X509 verification parameters =head1 SYNOPSIS #include - int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags); + int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, + unsigned long flags); int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, - unsigned long flags); + unsigned long flags); unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param); int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose); @@ -19,13 +20,17 @@ X509_VERIFY_PARAM_set_flags, X509_VERIFY_PARAM_clear_flags, X509_VERIFY_PARAM_ge void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t); int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, - ASN1_OBJECT *policy); + ASN1_OBJECT *policy); int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, STACK_OF(ASN1_OBJECT) *policies); void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth); int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param); + void X509_VERIFY_PARAM_set_auth_level(X509_VERIFY_PARAM *param, + int auth_level); + int X509_VERIFY_PARAM_get_auth_level(const X509_VERIFY_PARAM *param); + int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, const char *name, size_t namelen); int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param, @@ -71,8 +76,32 @@ policy set is cleared. The B parameter can be B to clear an existing policy set. X509_VERIFY_PARAM_set_depth() sets the maximum verification depth to B. -That is the maximum number of untrusted CA certificates that can appear in a +That is the maximum number of intermediate CA certificates that can appear in a chain. +A maximal depth chain contains 2 more certificates than the limit, since +neither the end-entity ceritificate nor the trust-anchor count against this +limit. +Thus a B limit of 0 only allows the end-entity certificate to be signed +directly by the trust-anchor, while with a B limit of 1 there can be one +intermediate CA certificate between the trust-anchor and the end-entity +certificate. + +X509_VERIFY_PARAM_set_auth_level() sets the authentication security level to +B. +The authentication security level determines the acceptable signature and public +key strength when verifying certificate chains. +For a certificate chain to validate, the public keys of all the certificates +must meet the specified security level. +The signature algorithm security level is not enforced for the chain's I certificate, which is either directly trusted or validated by means other +than its signature. +See L for the definitions of the available +levels. +The default security level is -1, or "not set". +At security level 0 or lower all algorithms are acceptable. +Security level 1 requires at least 80-bit-equivalent security and is broadly +interoperable, though it will, for example, reject MD5 signatures or RSA keys +shorter than 1024 bits. X509_VERIFY_PARAM_set1_host() sets the expected DNS hostname to B clearing any previously specified host name or names. If @@ -139,6 +168,9 @@ values. X509_VERIFY_PARAM_get_depth() returns the current verification depth. +X509_VERIFY_PARAM_get_auth_level() returns the current authentication security +level. + =head1 VERIFICATION FLAGS The verification flags consists of zero or more of the following flags diff --git a/include/openssl/x509_vfy.h b/include/openssl/x509_vfy.h index e883349d34..093b0f3d0a 100644 --- a/include/openssl/x509_vfy.h +++ b/include/openssl/x509_vfy.h @@ -355,7 +355,10 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); # define X509_V_ERR_IP_ADDRESS_MISMATCH 64 /* DANE TLSA errors */ # define X509_V_ERR_DANE_NO_MATCH 65 - +/* security level errors */ +# define X509_V_ERR_EE_KEY_TOO_SMALL 66 +# define X509_V_ERR_CA_KEY_TOO_SMALL 67 +# define X509_V_ERR_CA_MD_TOO_WEAK 68 /* Certificate verify flags */ @@ -552,6 +555,7 @@ unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param); int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose); int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust); void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth); +void X509_VERIFY_PARAM_set_auth_level(X509_VERIFY_PARAM *param, int auth_level); void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t); int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, ASN1_OBJECT *policy); @@ -574,6 +578,7 @@ int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc); int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param); +int X509_VERIFY_PARAM_get_auth_level(const X509_VERIFY_PARAM *param); const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param); int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param); diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 4081ebe4ff..24ac352d1d 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -494,6 +494,12 @@ int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) return (0); } param = X509_STORE_CTX_get0_param(&ctx); + /* + * XXX: Separate @AUTHSECLEVEL and @TLSSECLEVEL would be useful at some + * point, for now a single @SECLEVEL sets the same policy for TLS crypto + * and PKI authentication. + */ + X509_VERIFY_PARAM_set_auth_level(param, SSL_get_security_level(s)); /* Set suite B flags if needed */ X509_STORE_CTX_set_flags(&ctx, tls1_suiteb(s)); @@ -520,17 +526,8 @@ int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) if (s->ctx->app_verify_callback != NULL) i = s->ctx->app_verify_callback(&ctx, s->ctx->app_verify_arg); - else { + else i = X509_verify_cert(&ctx); -# if 0 - /* Dummy error calls so mkerr generates them */ - SSLerr(SSL_F_SSL_VERIFY_CERT_CHAIN, SSL_R_EE_KEY_TOO_SMALL); - SSLerr(SSL_F_SSL_VERIFY_CERT_CHAIN, SSL_R_CA_KEY_TOO_SMALL); - SSLerr(SSL_F_SSL_VERIFY_CERT_CHAIN, SSL_R_CA_MD_TOO_WEAK); -# endif - if (i > 0) - i = ssl_security_cert_chain(s, ctx.chain, NULL, 1); - } s->verify_result = ctx.error; sk_X509_pop_free(s->verified_chain, X509_free); @@ -894,12 +891,18 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) * ignore the error return from this call. We're not actually verifying * the cert - we're just building as much of the chain as we can */ - X509_verify_cert(&xs_ctx); + (void) X509_verify_cert(&xs_ctx); /* Don't leave errors in the queue */ ERR_clear_error(); i = ssl_security_cert_chain(s, xs_ctx.chain, NULL, 0); if (i != 1) { X509_STORE_CTX_cleanup(&xs_ctx); +#if 0 + /* Dummy error calls so mkerr generates them */ + SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_EE_KEY_TOO_SMALL); + SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_KEY_TOO_SMALL); + SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_MD_TOO_WEAK); +#endif SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i); return 0; } diff --git a/test/certs/ca-cert-768.pem b/test/certs/ca-cert-768.pem new file mode 100644 index 0000000000..0c8ff29440 --- /dev/null +++ b/test/certs/ca-cert-768.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICRDCCASygAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTE2MDMyMDA2MjcyN1oYDzIxMTYwMzIxMDYyNzI3WjANMQswCQYDVQQD +DAJDQTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQC3wNLc1A9gAjz1H94ozPrLOhE2 +R8c6RQjkUIALCOuw8xbZV+AEDSqP11Bw8MVzvmpksR9s1idJhLOugwMNTHfTXJjV +DWoQh9ofR51J5sOph4yDhQBXRmiuvqMDj+a81UkCAwEAAaNQME4wHQYDVR0OBBYE +FKrzei/LKJop6yShiJupKskW0ZQcMB8GA1UdIwQYMBaAFI71Ja8em2uEPXyAmslT +nE1y96NSMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFr4hjVtLuZz +gxLILAOREEtanckfnapUrhTLukog9Q8uzqMUE+YDEhkcP4YAVjcab6HaXrbcxXsn +zn+v+GPszD9G3doGbUjuwEEAHz+k/9sjsn8QAGw/XslYhd5dktaRRCqaTNiWT+Ks +xKntAsgXcgWNIpvGikzTB/W7IrjIV8/S1JjLABtoY88tFUX81Ohr3bFFsRc9EHVS +MtGnEwfoBOSlCUjaTWBNHHi1HstK9sG2SNT/nhN1HATk/aiCiQRKr/bm6ezPC2If +6mRidaNiQN8+vzvtn86BqtRJOEi8jj5CBax6IqwfE+lDZIwT7H9C9Cu8Yp4mTM0x +wwzRDnFVisM= +-----END CERTIFICATE----- diff --git a/test/certs/ca-cert-768i.pem b/test/certs/ca-cert-768i.pem new file mode 100644 index 0000000000..acc432fadd --- /dev/null +++ b/test/certs/ca-cert-768i.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAdSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTE2MDMyMDA2MjcyN1oYDzIxMTYwMzIxMDYyNzI3WjANMQswCQYDVQQD +DAJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJadpD0ASxxfxsvd +j9IxsogVzMSGLFziaYuE9KejU9+R479RifvwfBANO62sNWJ19X//9G5UjwWmkiOz +n1k50DkYsBBA3mJzik6wjt/c58lBIlSEgAgpvDU8ht8w3t20JP9+YqXAeugqFj/W +l9rFQtsvaWSRywjXVlp5fxuEQelNnXcJEKhsKTNExsBUZebo4/J1BWpklWzA9P0l +YW5INvDAAwcF1nzlEf0Y6Eot03IMNyg2MTE4hehxjdgCSci8GYnFirE/ojXqqpAc +ZGh7r2dqWgZUD1Dh+bT2vjrUzj8eTH3GdzI+oljt29102JIUaqj3yzRYkah8FLF9 +CLNNsUcCAwEAAaNQME4wHQYDVR0OBBYEFLQRM/HX4l73U54gIhBPhga/H8leMB8G +A1UdIwQYMBaAFFjzE/eu8wvKwzb2aODw52C+0gLVMAwGA1UdEwQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADYQCZM1sSpIyjyuGirBYvezFryUq5EyZiME3HIHJ7AbmquPtE +LcoE8lwxEYXl7OTbLZHxIKkt6+WX2TL/0yshJLq/42nh5DZwyug7fIITmkzmzidF +rbnl7fIop7OJX/kELbY= +-----END CERTIFICATE----- diff --git a/test/certs/ca-cert-md5-any.pem b/test/certs/ca-cert-md5-any.pem new file mode 100644 index 0000000000..7c2b53f5da --- /dev/null +++ b/test/certs/ca-cert-md5-any.pem @@ -0,0 +1,18 @@ +-----BEGIN TRUSTED CERTIFICATE----- +MIIC7DCCAdSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTE2MDMyMDA2MjcyN1oYDzIxMTYwMzIxMDYyNzI3WjANMQswCQYDVQQD +DAJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJadpD0ASxxfxsvd +j9IxsogVzMSGLFziaYuE9KejU9+R479RifvwfBANO62sNWJ19X//9G5UjwWmkiOz +n1k50DkYsBBA3mJzik6wjt/c58lBIlSEgAgpvDU8ht8w3t20JP9+YqXAeugqFj/W +l9rFQtsvaWSRywjXVlp5fxuEQelNnXcJEKhsKTNExsBUZebo4/J1BWpklWzA9P0l +YW5INvDAAwcF1nzlEf0Y6Eot03IMNyg2MTE4hehxjdgCSci8GYnFirE/ojXqqpAc +ZGh7r2dqWgZUD1Dh+bT2vjrUzj8eTH3GdzI+oljt29102JIUaqj3yzRYkah8FLF9 +CLNNsUcCAwEAAaNQME4wHQYDVR0OBBYEFLQRM/HX4l73U54gIhBPhga/H8leMB8G +A1UdIwQYMBaAFI71Ja8em2uEPXyAmslTnE1y96NSMAwGA1UdEwQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBACTmLO0KOkXFNjj6hXozC9GzQYMXdCfNmgMuetk8xdVm +TqkF/qIGK2FBWn91IH0/9ydZbL83EKjPjqjwqzXqExJ0Un+fy7XbYMKtjGJ21egJ +x97jzKey5phEwRD/4fJ+PCml9eE/SNzBV0xKSDq4qQYvSJ3GF6KCATVlr0bDzQJZ +yTY3FeNoy+K7Mb0rHtsGru60C/Ft1dl9uiJ+yKXMiCxPcDjYb+95mA9QJ1kXfR8J +JVfeKhEEK+QIVpz/37aQ4jx/zbGblFsruALK22aLnpgrfUzrsYQ8W8T/DV2dV1ra +4wHz/QtlE4isInOaK2+pvXwyGar+1/s3+VxXEiPlZ7IwCDAGBgRVHSUA +-----END TRUSTED CERTIFICATE----- diff --git a/test/certs/ca-cert-md5.pem b/test/certs/ca-cert-md5.pem new file mode 100644 index 0000000000..be564ddd10 --- /dev/null +++ b/test/certs/ca-cert-md5.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7DCCAdSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTE2MDMyMDA2MjcyN1oYDzIxMTYwMzIxMDYyNzI3WjANMQswCQYDVQQD +DAJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJadpD0ASxxfxsvd +j9IxsogVzMSGLFziaYuE9KejU9+R479RifvwfBANO62sNWJ19X//9G5UjwWmkiOz +n1k50DkYsBBA3mJzik6wjt/c58lBIlSEgAgpvDU8ht8w3t20JP9+YqXAeugqFj/W +l9rFQtsvaWSRywjXVlp5fxuEQelNnXcJEKhsKTNExsBUZebo4/J1BWpklWzA9P0l +YW5INvDAAwcF1nzlEf0Y6Eot03IMNyg2MTE4hehxjdgCSci8GYnFirE/ojXqqpAc +ZGh7r2dqWgZUD1Dh+bT2vjrUzj8eTH3GdzI+oljt29102JIUaqj3yzRYkah8FLF9 +CLNNsUcCAwEAAaNQME4wHQYDVR0OBBYEFLQRM/HX4l73U54gIhBPhga/H8leMB8G +A1UdIwQYMBaAFI71Ja8em2uEPXyAmslTnE1y96NSMAwGA1UdEwQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBACTmLO0KOkXFNjj6hXozC9GzQYMXdCfNmgMuetk8xdVm +TqkF/qIGK2FBWn91IH0/9ydZbL83EKjPjqjwqzXqExJ0Un+fy7XbYMKtjGJ21egJ +x97jzKey5phEwRD/4fJ+PCml9eE/SNzBV0xKSDq4qQYvSJ3GF6KCATVlr0bDzQJZ +yTY3FeNoy+K7Mb0rHtsGru60C/Ft1dl9uiJ+yKXMiCxPcDjYb+95mA9QJ1kXfR8J +JVfeKhEEK+QIVpz/37aQ4jx/zbGblFsruALK22aLnpgrfUzrsYQ8W8T/DV2dV1ra +4wHz/QtlE4isInOaK2+pvXwyGar+1/s3+VxXEiPlZ7I= +-----END CERTIFICATE----- diff --git a/test/certs/ca-key-768.pem b/test/certs/ca-key-768.pem new file mode 100644 index 0000000000..7aea5eda13 --- /dev/null +++ b/test/certs/ca-key-768.pem @@ -0,0 +1,13 @@ +-----BEGIN PRIVATE KEY----- +MIIB5QIBADANBgkqhkiG9w0BAQEFAASCAc8wggHLAgEAAmEAt8DS3NQPYAI89R/e +KMz6yzoRNkfHOkUI5FCACwjrsPMW2VfgBA0qj9dQcPDFc75qZLEfbNYnSYSzroMD +DUx301yY1Q1qEIfaH0edSebDqYeMg4UAV0Zorr6jA4/mvNVJAgMBAAECYQCJAsu3 +QJ9eNQ0CsQpTXdO6aMegs5CHkCX7J1Lx52rl+7uTv4QXQUH1EtS2AbEYhmdGzMFN +ZlBrg1vDsW/yn02NZzvT6xT/kvzFhQVw1i8B0YyB8wPao3f2ZxPkAfeoAAECMQDa +6VkNYlHgPOlTtwU1WYUirFczpipQsuk/lIf7B3+rVRUHoAE4nbeIRJgkKZaJEAEC +MQDW4pYsyN79HEqFpOFlfsrERw3y4hLRXGeHxbfJFdAe7SUfNj28ZI2EPFE0DJhX +RUkCMA39M2+jhM/rlI2A+Jg8LEHW+YuXZsTZagZiG35zMDlmqn1eQDW5/mx61a4Z +6kDAAQIwIlbZWtTK1bX0rsC3iEmny4/zSbIZAb37iXXuNcM3nAmXmhJH8Vg8STp+ +W4v7uE6JAjEAwiB9wCVwG4UhvKNQ4Wd2mfJiKZQNF4rL4ID0g+Wk6kX67c7u2hfH +sSaluw9nM91s +-----END PRIVATE KEY----- diff --git a/test/certs/ee-cert-768.pem b/test/certs/ee-cert-768.pem new file mode 100644 index 0000000000..794f93c012 --- /dev/null +++ b/test/certs/ee-cert-768.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICeDCCAWCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJDQTAg +Fw0xNjAzMjAwNjI3MjdaGA8yMTE2MDMyMTA2MjcyN1owGTEXMBUGA1UEAwwOc2Vy +dmVyLmV4YW1wbGUwfDANBgkqhkiG9w0BAQEFAANrADBoAmEAwCvrPAynx+7VtpFz +4cWZW3/n3/nMwK4fxkWSB0kbVUhQaYiaQGWEfB4JpRz5rPt8NW5m2aVGT7mMjScu +8YyFa3IDdpBeQL1n8VQUH3FLySgQHC1bkkzwyzQM8JirCdl/AgMBAAGjfTB7MB0G +A1UdDgQWBBSRBasp1P/UDCesreviw4Lwz8tFBDAfBgNVHSMEGDAWgBS0ETPx1+Je +91OeICIQT4YGvx/JXjAJBgNVHRMEAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBkG +A1UdEQQSMBCCDnNlcnZlci5leGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQB5xled +do7U++n86KmJDGnXd4XMpr1QbTFVSO7fhSiObeGm961re/TI7AhuLlsZYP601YhZ +pRe9B7tiEuzu3iCD4kKB0yxgUCSsF0u1KbHSUNe2H5bBJC21c2eLZh6U54y014nL +gFSDOsA8M1301+Hlh5AS+4iTR0Ra02RaZb3L5HCR2wtkJubh3rSj8eBzb6fx+Lhw +JoeRg34lhycGC4bBVwkRT8bo73Nrs71JUP2A6/PjdsIfF2rtVMEuIq8AMQ5wInZ+ +2mIxJ4MwCClwLCq3VxI1bzdf1TYsPNxYTUS1POb2VgNofG0mBTHNUYUO20aF0ct8 +PCQqIqxUIegfS3f5 +-----END CERTIFICATE----- diff --git a/test/certs/ee-cert-768i.pem b/test/certs/ee-cert-768i.pem new file mode 100644 index 0000000000..d6532fb5d2 --- /dev/null +++ b/test/certs/ee-cert-768i.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICfjCCAgigAwIBAgIBAjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJDQTAg +Fw0xNjAzMjAwNjI3MjdaGA8yMTE2MDMyMTA2MjcyN1owGTEXMBUGA1UEAwwOc2Vy +dmVyLmV4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo/4lY +YYWu3tssD9Vz++K3qBt6dWAr1H08c3a1rt6TL38kkG3JHPSKOM2fooAWVsu0LLuT +5Rcf/w3GQ/4xNPgo2HXpo7uIgu+jcuJTYgVFTeAxl++qnRDSWA2eBp4yuxsIVl1l +Dz9mjsI2oBH/wFk1/Ukc3RxCMwZ4rgQ4I+XndWfTlK1aqUAfrFkQ9QzBZK1KxMY1 +U7OWaoIbFYvRmavknm+UqtKW5Vf7jJFkijwkFsbSGb6CYBM7YrDtPh2zyvlr3zG5 +ep5LR2inKcc/SuIiJ7TvkGPX79ByST5brbkb1Ctvhmjd1XMSuEPJ3EEPoqNGT4tn +iIQPYf55NB9KiR+3AgMBAAGjfTB7MB0GA1UdDgQWBBTnm+IqrYpsOst2UeWOB5gi +l+FzojAfBgNVHSMEGDAWgBSq83ovyyiaKeskoYibqSrJFtGUHDAJBgNVHRMEAjAA +MBMGA1UdJQQMMAoGCCsGAQUFBwMBMBkGA1UdEQQSMBCCDnNlcnZlci5leGFtcGxl +MA0GCSqGSIb3DQEBCwUAA2EASAwDwXsYGnhQDyWixI9eKZwXAA9E4rEIdmKNvVjU +jWkMh1oC0FZl4TTHU+sAaXmv2QItZOcG2QEHoTIZDPYiy+7eZC7pPQY25dkxeSZ9 +TIlMnfePzYTc3BnfxZj82Mny +-----END CERTIFICATE----- diff --git a/test/certs/ee-cert-md5.pem b/test/certs/ee-cert-md5.pem new file mode 100644 index 0000000000..8c26422fca --- /dev/null +++ b/test/certs/ee-cert-md5.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBAjANBgkqhkiG9w0BAQQFADANMQswCQYDVQQDDAJDQTAg +Fw0xNjAzMjAwNjI3MjdaGA8yMTE2MDMyMTA2MjcyN1owGTEXMBUGA1UEAwwOc2Vy +dmVyLmV4YW1wbGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo/4lY +YYWu3tssD9Vz++K3qBt6dWAr1H08c3a1rt6TL38kkG3JHPSKOM2fooAWVsu0LLuT +5Rcf/w3GQ/4xNPgo2HXpo7uIgu+jcuJTYgVFTeAxl++qnRDSWA2eBp4yuxsIVl1l +Dz9mjsI2oBH/wFk1/Ukc3RxCMwZ4rgQ4I+XndWfTlK1aqUAfrFkQ9QzBZK1KxMY1 +U7OWaoIbFYvRmavknm+UqtKW5Vf7jJFkijwkFsbSGb6CYBM7YrDtPh2zyvlr3zG5 +ep5LR2inKcc/SuIiJ7TvkGPX79ByST5brbkb1Ctvhmjd1XMSuEPJ3EEPoqNGT4tn +iIQPYf55NB9KiR+3AgMBAAGjfTB7MB0GA1UdDgQWBBTnm+IqrYpsOst2UeWOB5gi +l+FzojAfBgNVHSMEGDAWgBS0ETPx1+Je91OeICIQT4YGvx/JXjAJBgNVHRMEAjAA +MBMGA1UdJQQMMAoGCCsGAQUFBwMBMBkGA1UdEQQSMBCCDnNlcnZlci5leGFtcGxl +MA0GCSqGSIb3DQEBBAUAA4IBAQBqCPfIEZOVUiq2exiRFoxVOvq668Y55lJZ9+4j +E5Ncq9mdbuD7GIxJSKByf899yBJUG32ZIbmwnSHfBkPolc/LjQhUDxJtSBE8vFaA +8AZ1rsOcaWapPQ94gYIgncBS15t7RjTX1l04fY0NPqVsWmTji+ummA5e7iCj6l6t +CqRGhMeSZWa1mc+Plurmz7oWEqkUK5cfTrlDnXeQNOI8EK8lc636elqqdnw0amO4 +yKJlaXRlm/I1nQdUQ0G5Bk2Tp/QGoJCtJ25XsoIbnCs0tIbpQllTdLsRQmOussAP +NvdwbKtAAolgMAxH9pl1Mc6OIo2e8405EWs1jvGEMgE0IFAY +-----END CERTIFICATE----- diff --git a/test/certs/ee-key-768.pem b/test/certs/ee-key-768.pem new file mode 100644 index 0000000000..0d44f85529 --- /dev/null +++ b/test/certs/ee-key-768.pem @@ -0,0 +1,13 @@ +-----BEGIN PRIVATE KEY----- +MIIB5QIBADANBgkqhkiG9w0BAQEFAASCAc8wggHLAgEAAmEAwCvrPAynx+7VtpFz +4cWZW3/n3/nMwK4fxkWSB0kbVUhQaYiaQGWEfB4JpRz5rPt8NW5m2aVGT7mMjScu +8YyFa3IDdpBeQL1n8VQUH3FLySgQHC1bkkzwyzQM8JirCdl/AgMBAAECYQCzO0MW +qqcBrhvdPyPZerZhxJW7K/xv6PbxsYlVCjZYAC4ff6x+SzCZolpUiQXE9Hdyhlyk +alcqn2vT5TagWk64YUmIMP7BCT2Ps/IW0nQl07k27c2BNq3IzdRnBz5SbQECMQDg +9UxISqFOG6sLdZIKA88Q+M2HE/MdzwiJby/bSUXhn5aluZqjR60nGPqAb2S/r98C +MQDasGzUTXqEYOPsAL4irzKMMiMdqbj6dNHsmo1GIYKx8PuN193i/cNd5XDv78Gm +imECMQC10IvewbKtVl9f2540ye9JYE18pvsPVI0pxtt++DGqsTkoqGH7JasktmN/ ++ogLBTECMBf9/xKTpXtcfeTod/OqMOt8nKmmcyrXIijJE/K7vnDzNUXshuVeXc6x +W2CXdzFkQQIweyLLA6etAJGsmCRwIgnfp1ubmVdfPou68byHSnzAf4/GxBriSd5b +EQcYwjE7SDI7 +-----END PRIVATE KEY----- diff --git a/test/certs/mkcert.sh b/test/certs/mkcert.sh index 99e7d2a342..ec2e374038 100755 --- a/test/certs/mkcert.sh +++ b/test/certs/mkcert.sh @@ -10,6 +10,10 @@ # DAYS=36525 +if [ -z "$OPENSSL_SIGALG" ]; then + OPENSSL_SIGALG=sha256 +fi + stderr_onerror() { ( err=$("$@" >&3 2>&1) || { @@ -53,7 +57,7 @@ req() { local errs stderr_onerror \ - openssl req -new -sha256 -key "${key}.pem" \ + openssl req -new -"${OPENSSL_SIGALG}" -key "${key}.pem" \ -config <(printf "[req]\n%s\n%s\n[dn]\nCN=%s\n" \ "prompt = no" "distinguished_name = dn" "${cn}") } @@ -63,7 +67,7 @@ req_nocn() { key "$key" stderr_onerror \ - openssl req -new -sha256 -subj / -key "${key}.pem" \ + openssl req -new -"${OPENSSL_SIGALG}" -subj / -key "${key}.pem" \ -config <(printf "[req]\n%s\n[dn]\nCN_default =\n" \ "distinguished_name = dn") } @@ -73,7 +77,7 @@ cert() { local exts=$1; shift stderr_onerror \ - openssl x509 -req -sha256 -out "${cert}.pem" \ + openssl x509 -req -"${OPENSSL_SIGALG}" -out "${cert}.pem" \ -extfile <(printf "%s\n" "$exts") "$@" } diff --git a/test/certs/root-cert-768.pem b/test/certs/root-cert-768.pem new file mode 100644 index 0000000000..4392ef0e48 --- /dev/null +++ b/test/certs/root-cert-768.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBpzCCATGgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTE2MDMyMDA2MjcyN1oYDzIxMTYwMzIxMDYyNzI3WjASMRAwDgYDVQQD +DAdSb290IENBMHwwDQYJKoZIhvcNAQEBBQADawAwaAJhALntqSk2YVnhNalAikA2 +tuSOvHUKVSJlqjKmzlUPI+gQFyBWxtyQdwepI87tl8EW1in2IiOeN49W+OtVOlBi +Mxwqi/BcBltTbbSrlRpoSKOH6V7zIXvfsqjwWsDi37V1xQIDAQABo1AwTjAdBgNV +HQ4EFgQUWPMT967zC8rDNvZo4PDnYL7SAtUwHwYDVR0jBBgwFoAUWPMT967zC8rD +NvZo4PDnYL7SAtUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAANhAFDU7FyF +Ma6EG0OBS4IYws2US9t3IQwlI5noQwm9R3Nk/3AIUrdPG8ydRyV1N4GuRhRpprh0 +sEbX3ZO9/E54DbPYfS5kqfZZtohUNy+Wmx8XY9OSv4SWUrrMSIRFXS63MA== +-----END CERTIFICATE----- diff --git a/test/certs/root-cert-md5.pem b/test/certs/root-cert-md5.pem new file mode 100644 index 0000000000..b6ed10c62f --- /dev/null +++ b/test/certs/root-cert-md5.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQQFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTE2MDMyMDA2MjcyN1oYDzIxMTYwMzIxMDYyNzI3WjASMRAwDgYDVQQD +DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eYA9Qa8 +oEY4eQ8/HnEZE20C3yubdmv8rLAh7daRCEI7pWM17FJboKJKxdYAlAOXWj25ZyjS +feMhXKTtxjyNjoTRnVTDPdl0opZ2Z3H5xhpQd7P9eO5b4OOMiSPCmiLsPtQ3ngfN +wCtVERc6NEIcaQ06GLDtFZRexv2eh8Yc55QaksBfBcFzQ+UD3gmRySTO2I6Lfi7g +MUjRhipqVSZ66As2Tpex4KTJ2lxpSwOACFaDox+yKrjBTP7FsU3UwAGq7b7OJb3u +aa32B81uK6GJVPVo65gJ7clgZsszYkoDsGjWDqtfwTVVfv1G7rrr3Laio+2Ff3ff +tWgiQ35mJCOvxQIDAQABo1AwTjAdBgNVHQ4EFgQUjvUlrx6ba4Q9fICayVOcTXL3 +o1IwHwYDVR0jBBgwFoAUjvUlrx6ba4Q9fICayVOcTXL3o1IwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQQFAAOCAQEAwSTZo97psLqiNmgvCC/Z51F3S9bFKPjGK4dc +Kqh8pMJsb8DnfGlPnsYXq/0oPcBThTRGZDqTeZa0ms8G+g4GS21TPF7lmvVJUJhz +GRLJxX7TYB8xriSJ15DwZgGmEGPfzmoIq27nwrO4TRAi0TCLdw01XZwiq2V7anl+ +jrIpJPDuaT3oBqnGTMZ5IoaQq2TX8PS/ZW6icJiRmXLMp/HUycKpDUshiuARR5Mi +UOzX8IHwn76Zj6z1R8xW9j1WcEycFYevTMaRuS6hnYagiSaAytIQU8hgMR4AWodM +NFYv5t9rguJnimGUGMMBIYXnPNE2kaoq9qCVgjuC14gWU0kq6Q== +-----END CERTIFICATE----- diff --git a/test/certs/root-key-768.pem b/test/certs/root-key-768.pem new file mode 100644 index 0000000000..4ecdcd3fdb --- /dev/null +++ b/test/certs/root-key-768.pem @@ -0,0 +1,13 @@ +-----BEGIN PRIVATE KEY----- +MIIB5AIBADANBgkqhkiG9w0BAQEFAASCAc4wggHKAgEAAmEAue2pKTZhWeE1qUCK +QDa25I68dQpVImWqMqbOVQ8j6BAXIFbG3JB3B6kjzu2XwRbWKfYiI543j1b461U6 +UGIzHCqL8FwGW1NttKuVGmhIo4fpXvMhe9+yqPBawOLftXXFAgMBAAECYH1FP4Bg +/16Lepg6v+tb8gY0lY1WFN5EGVRfRw3QUaT9kldboEjjnQ8wSswVEPYr56IHZ8mH +Or8LtJVrB3fjriq5vNOt7lRscuV7IcVtOyVWu5+MoJmO67Q2vRJXLWTdAQIxANtp +AiqObXo8vyT+EDcOEW104PfKNVh/4fhyrDwAk/yTcxkv4dcnuTykeLPvkXq4cQIx +ANjvQa+9LubMy3N1uXIbWWsiEBi4BdNK+xuppJ2puckaiQU42Mfmw/Nj4LMEJLfc +lQIwCYcv3uU8f9hvfI3D6oAj5Zrzwg737hXvnDhunlRwGMHWd7uKlStWcfm6fCXl +LW0hAjEAneK0egVEp3IR+PyLdcL194UZFgSJKNj/nYiAaMdokjcf1o8jJ4qKvw/I +MEIpvy9pAjAzaFHKRugCN01V2dgXYYGL8+zkcwG4ehDXH1XEs4v8r3WtHBPPKED6 +AemfAQJLvh8= +-----END PRIVATE KEY----- diff --git a/test/certs/setup.sh b/test/certs/setup.sh index 9606c77bb7..f34104613f 100755 --- a/test/certs/setup.sh +++ b/test/certs/setup.sh @@ -32,6 +32,14 @@ openssl x509 -in root-nonca.pem -trustout \ openssl x509 -in root-nonca.pem -trustout \ -addtrust anyExtendedKeyUsage -out nroot+anyEKU.pem +# Root CA security level variants: +# MD5 self-signature +OPENSSL_SIGALG=md5 \ +./mkcert.sh genroot "Root CA" root-key root-cert-md5 +# 768-bit key +OPENSSL_KEYBITS=768 \ +./mkcert.sh genroot "Root CA" root-key-768 root-cert-768 + # primary client-EKU root: croot-cert # trust variants: +serverAuth -serverAuth +clientAuth +anyEKU -anyEKU # @@ -97,6 +105,18 @@ openssl x509 -in ca-nonca.pem -trustout \ openssl x509 -in ca-nonca.pem -trustout \ -addtrust serverAuth -out nca+anyEKU.pem +# Intermediate CA security variants: +# MD5 issuer signature, +OPENSSL_SIGALG=md5 \ +./mkcert.sh genca "CA" ca-key ca-cert-md5 root-key root-cert +openssl x509 -in ca-cert-md5.pem -trustout \ + -addtrust anyExtendedKeyUsage -out ca-cert-md5-any.pem +# Issuer has 768-bit key +./mkcert.sh genca "CA" ca-key ca-cert-768i root-key-768 root-cert-768 +# CA has 768-bit key +OPENSSL_KEYBITS=768 \ +./mkcert.sh genca "CA" ca-key-768 ca-cert-768 root-key root-cert + # client intermediate ca: cca-cert # trust variants: +serverAuth, -serverAuth, +clientAuth, -clientAuth # @@ -152,3 +172,13 @@ openssl x509 -in ee-client.pem -trustout \ -addtrust clientAuth -out ee+clientAuth.pem openssl x509 -in ee-client.pem -trustout \ -addreject clientAuth -out ee-clientAuth.pem + +# Leaf cert security level variants +# MD5 issuer signature +OPENSSL_SIGALG=md5 \ +./mkcert.sh genee server.example ee-key ee-cert-md5 ca-key ca-cert +# 768-bit issuer key +./mkcert.sh genee server.example ee-key ee-cert-768i ca-key-768 ca-cert-768 +# 768-bit leaf key +OPENSSL_KEYBITS=768 \ +./mkcert.sh genee server.example ee-key-768 ee-cert-768 ca-key ca-cert diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t index d4131ccd3e..e025739368 100644 --- a/test/recipes/25-test_verify.t +++ b/test/recipes/25-test_verify.t @@ -10,7 +10,7 @@ setup("test_verify"); sub verify { my ($cert, $purpose, $trusted, $untrusted, @opts) = @_; - my @args = qw(openssl verify -purpose); + my @args = qw(openssl verify -auth_level 1 -purpose); my @path = qw(test certs); push(@args, "$purpose", @opts); for (@$trusted) { push(@args, "-trusted", srctop_file(@path, "$_.pem")) } @@ -19,7 +19,7 @@ sub verify { run(app([@args])); } -plan tests => 83; +plan tests => 101; # Canonical success ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]), @@ -214,3 +214,47 @@ ok(verify("ee-client", "sslclient", [qw(ee+clientAuth)], [], "-partial_chain"), "accept direct match with client trust"); ok(!verify("ee-client", "sslclient", [qw(ee-clientAuth)], [], "-partial_chain"), "reject direct match with client mistrust"); + +# Security level tests +ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "2"), + "accept RSA 2048 chain at auth level 2"); +ok(!verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "3"), + "reject RSA 2048 root at auth level 3"); +ok(verify("ee-cert", "sslserver", ["root-cert-768"], ["ca-cert-768i"], "-auth_level", "0"), + "accept RSA 768 root at auth level 0"); +ok(!verify("ee-cert", "sslserver", ["root-cert-768"], ["ca-cert-768i"]), + "reject RSA 768 root at auth level 1"); +ok(verify("ee-cert-768i", "sslserver", ["root-cert"], ["ca-cert-768"], "-auth_level", "0"), + "accept RSA 768 intermediate at auth level 0"); +ok(!verify("ee-cert-768i", "sslserver", ["root-cert"], ["ca-cert-768"]), + "reject RSA 768 intermediate at auth level 1"); +ok(verify("ee-cert-768", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "0"), + "accept RSA 768 leaf at auth level 0"); +ok(!verify("ee-cert-768", "sslserver", ["root-cert"], ["ca-cert"]), + "reject RSA 768 leaf at auth level 1"); +# +ok(verify("ee-cert", "sslserver", ["root-cert-md5"], ["ca-cert"], "-auth_level", "2"), + "accept md5 self-signed TA at auth level 2"); +ok(verify("ee-cert", "sslserver", ["ca-cert-md5-any"], [], "-auth_level", "2"), + "accept md5 intermediate TA at auth level 2"); +ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert-md5"], "-auth_level", "0"), + "accept md5 intermediate at auth level 0"); +ok(!verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert-md5"]), + "reject md5 intermediate at auth level 1"); +ok(verify("ee-cert-md5", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "0"), + "accept md5 leaf at auth level 0"); +ok(!verify("ee-cert-md5", "sslserver", ["root-cert"], ["ca-cert"]), + "reject md5 leaf at auth level 1"); + +# Depth tests, note the depth limit bounds the number of CA certificates +# between the trust-anchor and the leaf, so, for example, with a root->ca->leaf +# chain, depth = 1 is sufficient, but depth == 0 is not. +# +ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"], "-verify_depth", "2"), + "accept chain with verify_depth 2"); +ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"], "-verify_depth", "1"), + "accept chain with verify_depth 1"); +ok(!verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"], "-verify_depth", "0"), + "accept chain with verify_depth 0"); +ok(verify("ee-cert", "sslserver", ["ca-cert-md5-any"], [], "-verify_depth", "0"), + "accept md5 intermediate TA with verify_depth 0");