diff options
author | Joe Orton <joe@manyfish.uk> | 2021-03-08 13:40:27 +0000 |
---|---|---|
committer | Joe Orton <joe@manyfish.uk> | 2021-03-21 15:03:17 +0000 |
commit | 4d4fbb43b155e6c541e9bf74967a4ed6aee512ca (patch) | |
tree | 7f0caec1593d35691c643f4616606633528341a0 | |
parent | aca4f7fb70fc85c55da8703183402d08716a833d (diff) | |
download | neon-git-cert-hdigest.tar.gz |
Add ne_ssl_cert_hdigest, alternative to ne_ssl_cert_digest.cert-hdigest
* src/ne_openssl.c (ne_ssl_cert_hdigest): New function.
(hash_to_md): Factor out of ne_vstrhash.
(ne_vstrhash): Use it.
* test/ssl.c (cert_hdigests): New test.
* test/stubs.c (stub_ssl): Test for ne_ssl_cert_hdigest.
* src/ne_gnutls.c (ne_ssl_cert_hdigest, hash_to_alg): New functions.
(ne_vstrhash): Use hash_to_alg.
* src/neon.vers: Add to library versioning.
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/ne_gnutls.c | 45 | ||||
-rw-r--r-- | src/ne_openssl.c | 42 | ||||
-rw-r--r-- | src/ne_ssl.h | 6 | ||||
-rw-r--r-- | src/ne_stubssl.c | 5 | ||||
-rw-r--r-- | src/neon.vers | 1 | ||||
-rw-r--r-- | test/ssl.c | 47 | ||||
-rw-r--r-- | test/stubs.c | 1 |
8 files changed, 130 insertions, 18 deletions
@@ -17,6 +17,7 @@ Changes in release 0.32.0: accepts (only) UTF-8 usernames, uses a larger password buffer, and has different/improved attempt counter semantics. - RFC 7617 scoping rules are now applied for Basic authentication. + - ne_ssl.h: add ne_ssl_cert_hdigest() Changes in release 0.31.2: * Fix ne_md5_read_ctx() with OpenSSL on big-endian architectures. diff --git a/src/ne_gnutls.c b/src/ne_gnutls.c index 12f01b2..cf9ed3c 100644 --- a/src/ne_gnutls.c +++ b/src/ne_gnutls.c @@ -1463,6 +1463,41 @@ char *ne_ssl_cert_export(const ne_ssl_certificate *cert) return ret; } +static gnutls_digest_algorithm_t hash_to_alg(unsigned int flags) +{ + switch (flags & NE_HASH_ALGMASK) { + case NE_HASH_MD5: return GNUTLS_DIG_MD5; break; + case NE_HASH_SHA256: return GNUTLS_DIG_SHA256; break; + case NE_HASH_SHA512: return GNUTLS_DIG_SHA512; break; + default: break; + } + return GNUTLS_DIG_UNKNOWN; +} + +char *ne_ssl_cert_hdigest(const ne_ssl_certificate *cert, unsigned int flags) +{ + gnutls_digest_algorithm_t alg = hash_to_alg(flags); + unsigned char *dig; + size_t len; + char *rv; + + if (alg == GNUTLS_DIG_UNKNOWN) return NULL; + + if (gnutls_x509_crt_get_fingerprint(cert->subject, alg, NULL, &len) != GNUTLS_E_SHORT_MEMORY_BUFFER) { + return NULL; + } + + dig = ne_malloc(len); + if (gnutls_x509_crt_get_fingerprint(cert->subject, alg, dig, &len) < 0) { + ne_free(dig); + return NULL; + } + + rv = ne__strhash2hex(dig, len, flags); + ne_free(dig); + return rv; +} + int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest) { char sha1[20], *p; @@ -1509,19 +1544,15 @@ void ne__ssl_exit(void) char *ne_vstrhash(unsigned int flags, va_list ap) { - gnutls_digest_algorithm_t alg; + gnutls_digest_algorithm_t alg = hash_to_alg(flags); gnutls_hash_hd_t hd; unsigned char *out; const char *arg; unsigned len; char *rv; - switch (flags & NE_HASH_ALGMASK) { - case NE_HASH_MD5: alg = GNUTLS_DIG_MD5; break; - case NE_HASH_SHA256: alg = GNUTLS_DIG_SHA256; break; - case NE_HASH_SHA512: alg = GNUTLS_DIG_SHA512; break; - default: return NULL; - } + if (alg == GNUTLS_DIG_UNKNOWN) + return NULL; if (gnutls_hash_init(&hd, alg) < 0) return NULL; diff --git a/src/ne_openssl.c b/src/ne_openssl.c index b0ed5fa..41a5c05 100644 --- a/src/ne_openssl.c +++ b/src/ne_openssl.c @@ -1114,10 +1114,40 @@ char *ne_ssl_cert_export(const ne_ssl_certificate *cert) return ret; } +static const EVP_MD *hash_to_md(unsigned int flags) +{ + switch (flags & NE_HASH_ALGMASK) { + case NE_HASH_MD5: return EVP_md5(); + case NE_HASH_SHA256: return EVP_sha256(); +#ifdef HAVE_OPENSSL11 + case NE_HASH_SHA512: return EVP_sha512(); + case NE_HASH_SHA512_256: return EVP_sha512_256(); +#endif + default: break; + } + return NULL; +} + #if SHA_DIGEST_LENGTH != 20 # error SHA digest length is not 20 bytes #endif +char *ne_ssl_cert_hdigest(const ne_ssl_certificate *cert, unsigned int flags) +{ + const EVP_MD *md = hash_to_md(flags); + unsigned char dig[EVP_MAX_MD_SIZE]; + unsigned int len; + + if (!md) return NULL; + + if (!X509_digest(cert->subject, md, dig, &len)) { + ERR_clear_error(); + return NULL; + } + + return ne__strhash2hex(dig, len, flags); +} + int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest) { unsigned char sha1[EVP_MAX_MD_SIZE]; @@ -1142,21 +1172,11 @@ int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest) char *ne_vstrhash(unsigned int flags, va_list ap) { EVP_MD_CTX *ctx; - const EVP_MD *md; + const EVP_MD *md = hash_to_md(flags); unsigned char v[EVP_MAX_MD_SIZE]; unsigned int vlen; const char *arg; - switch (flags & NE_HASH_ALGMASK) { - case NE_HASH_MD5: md = EVP_md5(); break; - case NE_HASH_SHA256: md = EVP_sha256(); break; -#ifdef HAVE_OPENSSL11 - case NE_HASH_SHA512: md = EVP_sha512(); break; - case NE_HASH_SHA512_256: md = EVP_sha512_256(); break; -#endif - default: return NULL; - } - ctx = EVP_MD_CTX_new(); if (!ctx) return NULL; diff --git a/src/ne_ssl.h b/src/ne_ssl.h index c443ff1..4d7eac3 100644 --- a/src/ne_ssl.h +++ b/src/ne_ssl.h @@ -93,6 +93,12 @@ const ne_ssl_dname *ne_ssl_cert_subject(const ne_ssl_certificate *cert); * NE_SSL_DIGESTLEN bytes in length. */ int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest); +/* Calculate the certificate digest ("fingerprint") and format it as a + * NUL-terminated hex string using the hash algorithm and formatting + * flags exactly as if flags was passed to ne_strhash(). Returns NULL + * on error. */ +char *ne_ssl_cert_hdigest(const ne_ssl_certificate *cert, unsigned int flags); + /* Copy the validity times for the certificate 'cert' into 'from' and * 'until' (either may be NULL). If the time cannot be represented by * a time_t value, then (time_t)-1 will be written. */ diff --git a/src/ne_stubssl.c b/src/ne_stubssl.c index 70d809c..0f73f35 100644 --- a/src/ne_stubssl.c +++ b/src/ne_stubssl.c @@ -107,6 +107,11 @@ int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char digest[60]) return -1; } +char *ne_ssl_cert_hdigest(const ne_ssl_certificate *cert, unsigned int flags) +{ + return NULL; +} + void ne_ssl_cert_validity_time(const ne_ssl_certificate *cert, time_t *from, time_t *until) {} diff --git a/src/neon.vers b/src/neon.vers index ba4290c..45f99ee 100644 --- a/src/neon.vers +++ b/src/neon.vers @@ -31,4 +31,5 @@ NEON_0_32 { ne_vstrhash; ne_strparam; ne_add_auth; + ne_ssl_cert_hdigest; }; @@ -1434,6 +1434,52 @@ static int cert_fingerprint(void) return OK; } +static int cert_hdigests(void) +{ + static const struct { + unsigned int flags; + const char *digest; + } ts[] = { + { NE_HASH_MD5|NE_HASH_COLON, "76:26:eb:db:09:e8:53:5c:79:61:0c:30:3d:77:ed:65" }, + { NE_HASH_MD5, "7626ebdb09e8535c79610c303d77ed65" }, + { NE_HASH_SHA256, "ea4a4f4f08a91a83e841e772171a2befa3f6e576b5cd9f5cd6d12e9683fe89b3" }, + { NE_HASH_SHA512, "35373c533f4000ee9b6173a45eedae732f6c953dcf76f5fba5ffb7be380de559893d0679e94051950be2a5917fa7922fbf50ef10222d5be4eea53ba948cf7703" }, + { 0, NULL } + }; + unsigned int n, passed = 0; + char *fn = ne_concat(srcdir, "/notvalid.pem", NULL); + ne_ssl_certificate *cert = ne_ssl_cert_read(fn); + + ONN("could not load notvalid.pem", cert == NULL); + + for (n = 0; ts[n].flags; n++) { + char *dig = ne_ssl_cert_hdigest(cert, ts[n].flags); + + /* Can reasonably for almost any hash (either too modern or + * too old), so what can you do? */ + if (dig == NULL) { + t_warning("failed to htdigest with flags %u", ts[n].flags); + continue; + } + + NE_DEBUG(NE_DBG_SSL, "ssl: hDigest %u got %s, expected %s\n", + ts[n].flags, dig, ts[n].digest); + + ONV(strcmp(dig, ts[n].digest), + ("digest was %s not %s", dig, ts[n].digest)); + + passed++; + ne_free(dig); + } + + ONN("no algorithms supported for ne_ssl_cert_hdigest", passed == 0); + + ne_ssl_cert_free(cert); + ne_free(fn); + + return OK; +} + /* verify that identity of certificate in filename 'fname' is 'identity' */ static int check_identity(const char *fname, const char *identity) { @@ -1883,6 +1929,7 @@ ne_test tests[] = { T(trust_default_ca), T(cert_fingerprint), + T(cert_hdigests), T(cert_identities), T(cert_validity), T(cert_compare), diff --git a/test/stubs.c b/test/stubs.c index e0d1568..c0c0092 100644 --- a/test/stubs.c +++ b/test/stubs.c @@ -123,6 +123,7 @@ static int stub_ssl(void) ne_ssl_cert_issuer(cert))); ONN("this code shouldn't run", ne_ssl_cert_identity(issuer) != NULL); ONN("this code shouldn't run", ne_ssl_cert_export(cert) != NULL); + ONN("this code shouldn't run", ne_ssl_cert_hdigest(cert, NE_HASH_MD5) != NULL); } ONN("this code shouldn't run", ne_ssl_cert_import("foo") != NULL); |