Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: ossl_digest.c,v 1.2 2026/05/09 18:49:23 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	ossl_digest 3
      6 /* SUMMARY
      7 /*	OpenSSL message digest wrapper
      8 /* SYNOPSIS
      9 /*	#define USE_TLS
     10 /*
     11 /*	#include <ossl_digest.h>
     12 /*
     13 /*	OSSL_DGST *ossl_digest_new(
     14 /*	const char *alg_name)
     15 /*
     16 /*	int	ossl_digest_data(
     17 /*	OSSL_DGST *dgst,
     18 /*	const void *data,
     19 /*	ssize_t	data_len,
     20 /*	VSTRING *out);
     21 /*
     22 /*	ARGV	*ossl_digest_get_errors(void)
     23 /*
     24 /*	ARGV	*ossl_digest_log_errors(
     25 /*	void	(*logger)(const char *,...))
     26 /*
     27 /*	ssize_t	ossl_digest_get_size(
     28 /*	OSSL_DGST *dgst)
     29 /*
     30 /*	void	ossl_digest_free(
     31 /*	OSSL_DGST *dgst)
     32 /* DESCRIPTION
     33 /*	ossl_digest_new() allocates a wrapper for the named message
     34 /*	digest algorithm. This wrapper can be used in multiple successive
     35 /*	calls to compute a digest, and can be disposed of with
     36 /*	ossl_digest_free().
     37 /*
     38 /*	ossl_digest_data() uses the specified message digest wrapper to
     39 /*	compute a digest over the specified data.
     40 /*
     41 /*	ossl_digest_get_errors() dumps and clears the OpenSSL error stack.
     42 /*	Each stack entry is copied to one ARGV element. NOTE: The caller
     43 /*	should be prepared for the call to return an empty result,
     44 /*	and always report their own error info.
     45 /*
     46 /*	ossl_digest_log_errors() logs and clears the OpenSSL error stack.
     47 /*	Each stack entry is logged by the specified function. NOTE:
     48 /*	The caller should be prepared for the call to return an empty
     49 /*	result, and log their own error message.
     50 /*
     51 /*	ossl_digest_get_size() returns the output byte count for the
     52 /*	specified message digest wrapper.
     53 /*
     54 /*	ossl_digest_free() releases storage allocated for or by the
     55 /*	specified message wrapper.
     56 /* DIAGNOSTICS
     57 /*	Panic: ossl_digest_data() was called with an invalid data_len
     58 /*	argument; an ossl_digest_free() argument was not created with
     59 /*	ossl_digest_new().
     60 /*
     61 /*	ossl_digest_new() returns NULL after error. ossl_digest_data()
     62 /*	returns 0 after success, -1 after error.
     63 /* LICENSE
     64 /* .ad
     65 /* .fi
     66 /*	The Secure Mailer license must be distributed with this software.
     67 /* AUTHOR(S)
     68 /*	Wietse Venema
     69 /*	porcupine.org
     70 /*--*/
     71 
     72 #ifdef USE_TLS
     73 
     74  /*
     75   * System library.
     76   */
     77 #include <sys_defs.h>
     78 
     79  /*
     80   * OpenSSL library.
     81   */
     82 #include <openssl/err.h>
     83 #include <openssl/evp.h>
     84 
     85  /*
     86   * Utility library.
     87   */
     88 #include <msg.h>
     89 #include <mymalloc.h>
     90 #include <ossl_digest.h>
     91 #include <vstring.h>
     92 
     93 #ifndef OPENSSL_VERSION_PREREQ
     94 #define OPENSSL_VERSION_PREREQ(m,n) 0
     95 #endif
     96 
     97  /*
     98   * OpenSSL 1.1.1 compatibility crutches. Note: EVP_get_digestbyname()
     99   * returns const EVP_MD * which can't be passed to EVP_MD_free(EVP_MD *).
    100   */
    101 #if !OPENSSL_VERSION_PREREQ(3,0)
    102 #define EVP_MD_fetch(ct, alg_name, pr)	EVP_get_digestbyname(alg_name)
    103 #define BC_CONST			const
    104 #define EVP_MD_free(m)			/* */
    105 #define EVP_MD_get_size			EVP_MD_size
    106 #else
    107 #define BC_CONST			/* */
    108 #endif
    109 
    110  /*
    111   * Opaque object.
    112   */
    113 struct OSSL_DGST {
    114     EVP_MD_CTX *mdctx;
    115     BC_CONST EVP_MD *dgst_alg;
    116 };
    117 
    118  /*
    119   * SLMs.
    120   */
    121 #define STR(x)	vstring_str(x)
    122 #define LEN(x)	VSTRING_LEN(x)
    123 
    124 /* ossl_digest_new - create OpenSSL digest wrapper */
    125 
    126 OSSL_DGST *ossl_digest_new(const char *alg_name)
    127 {
    128     OSSL_DGST *dgst = (OSSL_DGST *) mymalloc(sizeof(*dgst));
    129 
    130     /*
    131      * https://docs.openssl.org/3.3/man7/ossl-guide-libcrypto-introduction
    132      * "If you perform the same operation many times with the same algorithm
    133      * then it is recommended to use a single explicit fetch of the algorithm
    134      * and then reuse the explicitly fetched algorithm each subsequent time.
    135      * This will typically be faster than implicitly fetching the algorithm
    136      * every time you use it".
    137      *
    138      * That same text mentions that calling, for example, EVP_sha256(3), uses
    139      * implicit fetching.
    140      */
    141     if ((dgst->dgst_alg = EVP_MD_fetch(NULL, alg_name, NULL)) != 0) {
    142 	if ((dgst->mdctx = EVP_MD_CTX_new()) != 0) {
    143 	    /* Success. */
    144 	    return (dgst);
    145 	}
    146 	EVP_MD_free(dgst->dgst_alg);
    147     }
    148     /* Failure. */
    149     myfree(dgst);
    150     return (0);
    151 }
    152 
    153 /* ossl_digest_data - digest one data buffer */
    154 
    155 int     ossl_digest_data(OSSL_DGST *dgst, const void *data,
    156 			         ssize_t data_len, VSTRING *out)
    157 {
    158     unsigned int out_len;
    159 
    160     if (data_len < 0)
    161 	msg_panic("ossl_digest_data: bad data_len %ld", (long) data_len);
    162 
    163     VSTRING_RESET(out);
    164     VSTRING_SPACE(out, EVP_MD_get_size(dgst->dgst_alg));
    165     if (EVP_DigestInit_ex(dgst->mdctx, dgst->dgst_alg, 0) != 1
    166 	|| EVP_DigestUpdate(dgst->mdctx, data, data_len) != 1
    167 	|| EVP_DigestFinal_ex(dgst->mdctx, (void *) STR(out),
    168 			      &out_len) != 1)
    169 	return (-1);
    170     vstring_set_payload_size(out, out_len);
    171     return (0);
    172 }
    173 
    174 /* ossl_digest_get_size - determine digest output byte count */
    175 
    176 ssize_t ossl_digest_get_size(OSSL_DGST *dgst)
    177 {
    178     return (EVP_MD_get_size(dgst->dgst_alg));
    179 }
    180 
    181 /* ossl_digest_get_errors - export and clear OpenSSL error stack */
    182 
    183 ARGV   *ossl_digest_get_errors(void)
    184 {
    185     ARGV   *argv = argv_alloc(1);
    186     VSTRING *tmp = vstring_alloc(100);
    187     unsigned long err;
    188     char    buffer[1024];		/* XXX */
    189     const char *file;
    190     const char *data;
    191     int     line;
    192     int     flags;
    193 
    194     /*
    195      * Shamelessly copied from Postfix TLS library.
    196      */
    197 #if OPENSSL_VERSION_PREREQ(3,0)
    198 /* XXX: We're ignoring the function name, do we want to log it? */
    199 #define ERRGET(fi, l, d, fl) ERR_get_error_all(fi, l, 0, d, fl)
    200 #else
    201 #define ERRGET(fi, l, d, fl) ERR_get_error_line_data(fi, l, d, fl)
    202 #endif
    203 
    204     while ((err = ERRGET(&file, &line, &data, &flags)) != 0) {
    205 	ERR_error_string_n(err, buffer, sizeof(buffer));
    206 	if (flags & ERR_TXT_STRING)
    207 	    vstring_sprintf(tmp, "%s:%s:%d:%s:",
    208 			    buffer, file, line, data);
    209 	else
    210 	    vstring_sprintf(tmp, "%s:%s:%d:", buffer, file, line);
    211 	argv_add(argv, STR(tmp), (char *) 0);
    212     }
    213     vstring_free(tmp);
    214     return (argv);
    215 }
    216 
    217 /* ossl_digest_log_errors - log and clear OpenSSL error stack */
    218 
    219 void    ossl_digest_log_errors(void (*logger) (const char *,...))
    220 {
    221     unsigned long err;
    222     char    buffer[1024];		/* XXX */
    223     const char *file;
    224     const char *data;
    225     int     line;
    226     int     flags;
    227 
    228     while ((err = ERRGET(&file, &line, &data, &flags)) != 0) {
    229 	ERR_error_string_n(err, buffer, sizeof(buffer));
    230 	if (flags & ERR_TXT_STRING)
    231 	    logger("%s:%s:%d:%s:", buffer, file, line, data);
    232 	else
    233 	    logger("%s:%s:%d:", buffer, file, line);
    234     }
    235 }
    236 
    237 /* ossl_digest_free - dispose of digest wrapper */
    238 
    239 void    ossl_digest_free(OSSL_DGST *dgst)
    240 {
    241     EVP_MD_CTX_destroy(dgst->mdctx);
    242     EVP_MD_free(dgst->dgst_alg);
    243     myfree(dgst);
    244 }
    245 
    246 #endif					/* USE_TLS */
    247