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