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