Home | History | Annotate | Line # | Download | only in src
      1 /* $NetBSD: crypto.c,v 1.7 2025/12/16 12:03:39 nia Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Mateusz Kocielski.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of The NetBSD Foundation nor the names of its
     19  *    contributors may be used to endorse or promote products derived
     20  *    from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     32  * POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 #include <sys/cdefs.h>
     35 __RCSID("$NetBSD: crypto.c,v 1.7 2025/12/16 12:03:39 nia Exp $");
     36 
     37 #include <assert.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 
     42 #include <openssl/bio.h>
     43 #include <openssl/buffer.h>
     44 #include <openssl/evp.h>
     45 #include <openssl/hmac.h>
     46 #include <openssl/md5.h>
     47 #include <openssl/rand.h>
     48 
     49 #include "crypto.h"
     50 
     51 /**
     52  * @brief base64 encode data.
     53  * @param in input data
     54  * @param inlen input data length (in bytes)
     55  * @param out output data
     56  * @param outlen output data length (in bytes)
     57  * @return 0 on success, -1 on failure
     58  */
     59 int
     60 saslc__crypto_encode_base64(const void *in, size_t inlen,
     61     char **out, size_t *outlen)
     62 {
     63 	BIO *bio;
     64 	BIO *b64;
     65 	size_t enclen;
     66 	char *r;
     67 	int n;
     68 
     69 	enclen = (((inlen + 2) / 3)) * 4;
     70 	r = calloc(enclen + 1, sizeof(*r));
     71 	if (r == NULL)
     72 		return -1;
     73 
     74 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
     75 		goto err;
     76 
     77 	if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
     78 		BIO_free(bio);
     79 		goto err;
     80 	}
     81 	BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
     82 	b64 = BIO_push(b64, bio);
     83 	if (BIO_write(b64, in, (int)inlen) != (int)inlen) {
     84 		BIO_free_all(b64);
     85 		goto err;
     86 	}
     87 	/*LINTED: no effect*/
     88 	(void)BIO_flush(b64);
     89 	n = BIO_read(bio, r, (int)enclen);
     90 	BIO_free_all(b64);
     91 	if (n < 0)
     92 		goto err;
     93 	if (out)
     94 		*out = r;
     95 	if (outlen)
     96 		*outlen = n;
     97 	return 0;
     98  err:
     99 	free(r);
    100 	return -1;
    101 }
    102 
    103 /**
    104  * @brief decode base64 data.
    105  * @param in input data
    106  * @param inlen input data length (in bytes)
    107  * @param out output data
    108  * @param outlen output data length (in bytes)
    109  * @return 0 on success, -1 on failure
    110  */
    111 int
    112 saslc__crypto_decode_base64(const char *in, size_t inlen,
    113     void **out, size_t *outlen)
    114 {
    115 	BIO *bio;
    116 	BIO *b64;
    117 	void *r;
    118 	size_t declen;
    119 	int n;
    120 
    121 	declen = ((inlen + 3) / 4) * 3;
    122 	r = malloc(declen + 1);
    123 	if (r == NULL)
    124 		return -1;
    125 
    126 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
    127 		goto err;
    128 
    129 	if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
    130 		BIO_free(bio);
    131 		goto err;
    132 	}
    133 	BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    134 	b64 = BIO_push(b64, bio);
    135 	if (BIO_write(bio, in, (int)inlen) != (int)inlen) {
    136 		BIO_free_all(b64);
    137 		goto err;
    138 	}
    139 	n = BIO_read(b64, r, (int)declen);
    140 	BIO_free_all(b64);
    141 	if (n < 0)
    142 		goto err;
    143 	((char *)r)[n] = '\0';
    144 	if (out)
    145 		*out = r;
    146 	if (outlen)
    147 		*outlen = n;
    148 	return 0;
    149  err:
    150 	free(r);
    151 	return -1;
    152 }
    153 
    154 /**
    155  * @brief generates safe nonce basing on OpenSSL
    156  * RAND_pseudo_bytes, which should be enough for our purposes.
    157  * @param len nonce length in bytes
    158  * @return nonce, user is responsible for freeing nonce.
    159  */
    160 char *
    161 saslc__crypto_nonce(size_t len)
    162 {
    163 	char *n;
    164 
    165 	if ((n = malloc(len)) == NULL)
    166 		return NULL;
    167 
    168 	if (RAND_bytes((unsigned char *)n, (int)len) != 1) {
    169 		free(n);
    170 		return NULL;
    171 	}
    172 	return n;
    173 }
    174 
    175 /**
    176  * @brief converts MD5 binary digest into text representation.
    177  * @param hash MD5 digest (16 bytes) to convert
    178  * @return the '\0' terminated text representation of the hash.  Note
    179  * that user is responsible for freeing allocated memory.
    180  */
    181 char *
    182 saslc__crypto_hash_to_hex(const uint8_t *hash)
    183 {
    184 	static const char hex[] = "0123456789abcdef";
    185 	char *r;
    186 	size_t i, j;
    187 
    188 	if ((r = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL)
    189 		return NULL;
    190 
    191 	for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
    192 		j = i * 2;
    193 		r[j] = hex[(unsigned)hash[i] >> 4];
    194 		r[j + 1] = hex[hash[i] & 0x0F];
    195 	}
    196 	r[MD5_DIGEST_LENGTH * 2] = '\0';
    197 	return r;
    198 }
    199 
    200 /**
    201  * @brief computes md5(D)
    202  * @param buf input data buffer
    203  * @param buflen number of bytes in input data buffer
    204  * @param digest buffer for hash (must not be NULL)
    205  * @return the md5 digest, note that user is responsible for freeing
    206  * allocated memory if digest is not NULL.
    207  */
    208 void
    209 saslc__crypto_md5_hash(const char *buf, size_t buflen, unsigned char *digest)
    210 {
    211 
    212 	assert(digest != NULL);
    213 	if (digest != NULL)
    214 		(void)MD5((const unsigned char *)buf, buflen, digest);
    215 }
    216 
    217 /**
    218  * @brief computes md5(D)
    219  * @param buf input data buffer
    220  * @param buflen number of bytes in input data buffer
    221  * @return the text representation of the computed digest, note that
    222  * user is responsible for freeing allocated memory.
    223  */
    224 char *
    225 saslc__crypto_md5_hex(const char *buf, size_t buflen)
    226 {
    227 	unsigned char digest[MD5_DIGEST_LENGTH];
    228 
    229 	(void)MD5((const unsigned char *)buf, buflen, digest);
    230 	return saslc__crypto_hash_to_hex(digest);
    231 }
    232 
    233 /**
    234  * @brief computes hmac_md5(K, I)
    235  * @param key hmac_md5 key
    236  * @param keylen hmac_md5 key length
    237  * @param in input data to compute hash for
    238  * @param inlen input data length in bytes
    239  * @param hmac space for output (MD5_DIGEST_LENGTH bytes)
    240  * @return 0 on success, -1 on error
    241  */
    242 int
    243 saslc__crypto_hmac_md5_hash(const unsigned char *key, size_t keylen,
    244     const unsigned char *in, size_t inlen, unsigned char *hmac)
    245 {
    246 	unsigned int hmac_len;
    247 
    248 	assert(hmac != NULL);
    249 	if (hmac == NULL || HMAC(EVP_md5(), key, (int)keylen, in,
    250 	    inlen, hmac, &hmac_len) == NULL)
    251 		return -1;
    252 
    253 	assert(hmac_len == MD5_DIGEST_LENGTH);
    254 	return 0;
    255 }
    256 
    257 /**
    258  * @brief computes hmac_md5(K, I)
    259  * @param key hmac_md5 key
    260  * @param keylen hmac_md5 key length
    261  * @param in input data to compute hash for
    262  * @param inlen input data length in bytes
    263  * @return the text representation of the computed digest, note that user is
    264  * responsible for freeing allocated memory.
    265  */
    266 char *
    267 saslc__crypto_hmac_md5_hex(const unsigned char *key, size_t keylen,
    268     const unsigned char *in, size_t inlen)
    269 {
    270 	unsigned char digest[MD5_DIGEST_LENGTH];
    271 
    272 	if (saslc__crypto_hmac_md5_hash(key, keylen, in, inlen, digest) == -1)
    273 		return NULL;
    274 
    275 	return saslc__crypto_hash_to_hex(digest);
    276 }
    277