Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/x509/clu_cert_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,10 @@ int wolfCLU_certSetup(int argc, char **argv)
keyUsage = 0;
}

wolfCLU_extKeyUsagePrint(out, keyUsage, 0, 1);
if ((ret = wolfCLU_extKeyUsagePrint(out, keyUsage, 0, 1))
!= WOLFCLU_SUCCESS) {
wolfCLU_LogError("Unable to print ext key usage");
}
#else
wolfCLU_LogError("Extended key function not supported by this"
" version of wolfSSL");
Expand Down
94 changes: 47 additions & 47 deletions src/x509/clu_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,67 +143,67 @@ int wolfCLU_printX509PubKey(WOLFSSL_X509* x509, WOLFSSL_BIO* out)
}


/* Print the extended key usages held in 'keyUsage'.
*
* 'keyUsage' is the bit map returned by wolfSSL_X509_get_extended_key_usage(),
* so it must be masked with the XKU_* flags rather than with the
* ExtKeyUsage_Sum OID values, which are not single bit flags.
*
* When 'flag' is set every purpose is listed with a YES/NO verdict, otherwise
* only the purposes present in the certificate are printed.
*/
int wolfCLU_extKeyUsagePrint(WOLFSSL_BIO* bio, unsigned int keyUsage,
int indent, int flag)
{
unsigned int ava;
#if LIBWOLFSSL_VERSION_HEX > 0x05001000
unsigned int i;
char scratch[MAX_TERM_WIDTH];

if (flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s\n", indent, "",
"Certificate Purpose:");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
}

ava = (EKU_ANY_OID & keyUsage);
if (ava | flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
"Any Extended Key Usage",
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
}
static const struct {
unsigned int bit;
const char* name;
} extKeyUsages[] = {
{ XKU_ANYEKU, "Any Extended Key Usage" },
{ XKU_SSL_SERVER, "TLS Web Server Authentication" },
{ XKU_SSL_CLIENT, "TLS Web Client Authentication" },
{ XKU_CODE_SIGN, "Code Signing" },
{ XKU_OCSP_SIGN, "OCSP Signing" },
{ XKU_SMIME, "Email Protect" },
{ XKU_TIMESTAMP, "Time Stamp Signing" },
};

ava = (EKU_SERVER_AUTH_OID & keyUsage);
if (ava | flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
"TLS Web Server Authentication",
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
}

ava = (EKU_CLIENT_AUTH_OID & keyUsage);
if (ava | flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
"TLS Web Client Authentication",
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
if (bio == NULL) {
return WOLFCLU_FATAL_ERROR;
}

ava = (EKU_OCSP_SIGN_OID & keyUsage);
if (ava | flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
"OCSP Signing",
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
if (flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s\n", indent, "",
"Certificate Purpose:");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
}

ava = (EKU_EMAILPROTECT_OID & keyUsage);
if (ava | flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
"Email Protect",
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
}
for (i = 0; i < (sizeof(extKeyUsages) / sizeof(extKeyUsages[0])); i++) {
unsigned int ava = (extKeyUsages[i].bit & keyUsage);

ava = (EKU_TIMESTAMP_OID & keyUsage);
if (ava | flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
"Time Stamp Signing",
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
if (ava || flag) {
XSNPRINTF(scratch, MAX_TERM_WIDTH, "%*s%s%s\n", indent, "",
extKeyUsages[i].name,
(flag == 1)? (ava > 0) ? " : YES" : " : NO" : "");
wolfSSL_BIO_write(bio, scratch, (int)XSTRLEN(scratch));
}
}

return WOLFSSL_SUCCESS;
return WOLFCLU_SUCCESS;
#else
(void)bio;
(void)keyUsage;
(void)indent;
(void)flag;

wolfCLU_LogError("Extended key function not supported by this"
" version of wolfSSL");
return NOT_COMPILED_IN;
#endif
}

#endif /* WOLFCLU_NO_FILESYSTEM */
9 changes: 5 additions & 4 deletions tests/x509/expect-purpose.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Certificate Purpose:
Any Extended Key Usage : YES
Any Extended Key Usage : NO
TLS Web Server Authentication : YES
TLS Web Client Authentication : YES
OCSP Signing : YES
Email Protect : YES
Time Stamp Signing : YES
Code Signing : NO
OCSP Signing : NO
Email Protect : NO
Time Stamp Signing : NO
75 changes: 75 additions & 0 deletions tests/x509/x509-process-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,81 @@ def test_3j_purpose(self):
self.assertEqual(r.returncode, 0, r.stderr)
self.assertEqual(r.stdout.strip(), expected)

def test_3j2_purpose_per_cert(self):
"""-purpose must report exactly the EKUs each certificate carries.

The purposes are read from a bit map, so a cert with a single EKU has
to answer NO to every other purpose. Covers the regression where the
bit map was masked with OID sums instead of the XKU_* flags, which
made every purpose read YES (or every one NO) regardless of the cert.
"""
# cert -> the purposes that must report YES; all others must be NO
cases = {
"server-cert.pem": ["TLS Web Server Authentication",
"TLS Web Client Authentication"],
"server-ecc.pem": ["TLS Web Server Authentication"],
"client-int-cert.pem": ["TLS Web Client Authentication",
"Email Protect"],
"ocsp-responder-cert.pem": ["OCSP Signing"],
}
for cert, expected_yes in cases.items():
with self.subTest(cert=cert):
r = run_wolfssl("x509", "-in", os.path.join(CERTS_DIR, cert),
"-purpose", "-noout")
self.assertEqual(r.returncode, 0, r.stderr)

lines = r.stdout.strip().splitlines()
self.assertEqual(lines[0].strip(), "Certificate Purpose:")

got_yes = []
for line in lines[1:]:
name, _, verdict = line.rpartition(" : ")
self.assertIn(verdict, ("YES", "NO"),
"bad purpose line: {}".format(line))
if verdict == "YES":
got_yes.append(name.strip())

self.assertEqual(sorted(got_yes), sorted(expected_yes))

@unittest.skipUnless(HAS_OPENSSL, "openssl is not avaliable")
def test_3j3_purpose_generated_ekus(self):
"""-purpose on EKUs no checked-in certificate carries.

codeSigning, timeStamping and anyExtendedKeyUsage are absent from
certs/, so build a cert with them to cover the rest of the bit map.
"""
key = "test_3j3.key"
cert = "test_3j3.pem"
conf = "test_3j3.cnf"
self._clean(key, cert, conf)

with open(conf, "w") as f:
f.write("[req]\n"
"distinguished_name = dn\n"
"x509_extensions = v3\n"
"prompt = no\n"
"[dn]\n"
"CN = wolfCLU EKU test\n"
"[v3]\n"
"extendedKeyUsage = codeSigning, timeStamping, "
"anyExtendedKeyUsage\n")
# TODO: change this to wolfssl when
# https://github.com/wolfSSL/wolfCLU/pull/285 is merged
subprocess.run(
["openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes",
"-keyout", key, "-out", cert, "-days", "1", "-config", conf],
check=True, capture_output=True, timeout=120)

r = run_wolfssl("x509", "-in", cert, "-purpose", "-noout")
self.assertEqual(r.returncode, 0, r.stderr)

got_yes = [line.rpartition(" : ")[0].strip()
for line in r.stdout.strip().splitlines()[1:]
if line.rstrip().endswith("YES")]
self.assertEqual(sorted(got_yes),
sorted(["Any Extended Key Usage", "Code Signing",
"Time Stamp Signing"]))

def test_3k_hash(self):
expected_file = os.path.join(TESTS_X509_DIR, "expect-hash.txt")
with open(expected_file) as f:
Expand Down
Loading