summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Orton <joe@manyfish.uk>2021-03-08 13:40:27 +0000
committerJoe Orton <joe@manyfish.uk>2021-03-21 15:03:17 +0000
commit4d4fbb43b155e6c541e9bf74967a4ed6aee512ca (patch)
tree7f0caec1593d35691c643f4616606633528341a0
parentaca4f7fb70fc85c55da8703183402d08716a833d (diff)
downloadneon-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--NEWS1
-rw-r--r--src/ne_gnutls.c45
-rw-r--r--src/ne_openssl.c42
-rw-r--r--src/ne_ssl.h6
-rw-r--r--src/ne_stubssl.c5
-rw-r--r--src/neon.vers1
-rw-r--r--test/ssl.c47
-rw-r--r--test/stubs.c1
8 files changed, 130 insertions, 18 deletions
diff --git a/NEWS b/NEWS
index 873a73a..b74f364 100644
--- a/NEWS
+++ b/NEWS
@@ -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;
};
diff --git a/test/ssl.c b/test/ssl.c
index ee18105..7c1f034 100644
--- a/test/ssl.c
+++ b/test/ssl.c
@@ -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);