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");