1 /* $Id: crypto.c,v 1.1.1.1 2010/11/27 21:23:59 agc 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 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <openssl/md5.h> 43 #include <openssl/rand.h> 44 #include <openssl/bio.h> 45 #include <openssl/evp.h> 46 #include <openssl/buffer.h> 47 #include "crypto.h" 48 49 #define HMAC_MD5_KEYSIZE 64 50 #define HMAC_MD5_IPAD 0x36 51 #define HMAC_MD5_OPAD 0x5C 52 53 /* local header */ 54 55 static const char saslc__hex[] = "0123456789abcdef"; 56 57 static char * saslc__digest_to_ascii(const unsigned char *); 58 59 /** 60 * @brief converts MD5 binary digest into text representation. 61 * @param d MD5 digest 62 * @return the text representation, note that user is responsible for freeing 63 * allocated memory. 64 */ 65 66 static char * 67 saslc__digest_to_ascii(const unsigned char *d) 68 { 69 char *r; 70 size_t i; 71 72 if ((r = calloc((MD5_DIGEST_LENGTH << 1) + 1, sizeof(*r))) == NULL) 73 return NULL; 74 75 for (i = 0; i < MD5_DIGEST_LENGTH; i++) { 76 size_t j = i << 1; 77 r[j] = saslc__hex[(unsigned int)d[i] >> 4]; 78 r[j + 1] = saslc__hex[d[i] & 0x0F]; 79 } 80 81 return r; 82 } 83 84 /** 85 * @brief encode data to base64. 86 * @param in input data 87 * @param len input data length 88 * @return in encoded using base64. User is responsible for freeing string. 89 */ 90 91 char * 92 saslc__crypto_base64(const unsigned char *in, size_t len) 93 { 94 BIO *m; 95 BIO *base64; 96 BUF_MEM *c; 97 char *r; 98 99 m = BIO_new(BIO_s_mem()); 100 base64 = BIO_new(BIO_f_base64()); 101 102 /* base64 -> mem */ 103 base64 = BIO_push(base64, m); 104 /*LINTED len should be size_t in api*/ 105 BIO_write(base64, in, len); 106 /*LINTED wrong argument, null effect*/ 107 (void)BIO_flush(base64); 108 /*LINTED wrong argument, non-portable cast*/ 109 BIO_get_mem_ptr(base64, &c); 110 #if 0 111 /* c->length is unsigned and cannot be < 0 */ 112 if (c->length < 0) 113 return NULL; 114 #endif 115 /*LINTED length should be size_t in api*/ 116 r = calloc(c->length, sizeof(*r)); 117 if (r == NULL) 118 goto end; 119 if (c->length != 0) { 120 /*LINTED length should be size_t in api*/ 121 memcpy(r, c->data, c->length - 1); 122 } 123 end: 124 BIO_free_all(base64); 125 126 return r; 127 } 128 129 /** 130 * @brief generates safe nonce basing on OpenSSL 131 * RAND_pseudo_bytes, which should be enough for our purposes. 132 * @param b nonce length 133 * @return nonce, user is responsible for freeing nonce. 134 */ 135 136 unsigned char * 137 saslc__crypto_nonce(size_t b) 138 { 139 unsigned char *n; 140 141 if ((n = calloc(b, sizeof(*n))) == NULL) 142 return NULL; 143 144 /*LINTED b should be size_t in api*/ 145 if (RAND_pseudo_bytes(n, b) != 1) { 146 free(n); 147 return NULL; 148 } 149 150 return n; 151 } 152 153 /** 154 * @brief computes md5(D) 155 * @param d data (string) 156 * @return the text representation of the computed digest, note that user is 157 * responsible for freeing allocated memory. 158 */ 159 160 char * 161 saslc__crypto_md5(const char *d) 162 { 163 unsigned char digest[MD5_DIGEST_LENGTH]; 164 MD5_CTX md5c; 165 166 MD5_Init(&md5c); 167 MD5_Update(&md5c, d, strlen(d)); 168 MD5_Final(digest, &md5c); 169 170 return saslc__digest_to_ascii(digest); 171 } 172 173 /** 174 * @brief computes hmac_md5(K, I) 175 * @param key hmac_md5 key 176 * @param keylen hmac_md5 key length 177 * @param in hmac_md5 input 178 * @param inlen hmac_md5 input length 179 * @return the text representation of the computed digest, note that user is 180 * responsible for freeing allocated memory. 181 */ 182 183 char * 184 saslc__crypto_hmac_md5(const unsigned char *key, size_t keylen, 185 const unsigned char *in, size_t inlen) 186 { 187 unsigned char ipad_key[HMAC_MD5_KEYSIZE], 188 opad_key[HMAC_MD5_KEYSIZE], digest[MD5_DIGEST_LENGTH]; 189 const unsigned char *hmac_key; 190 size_t i; 191 MD5_CTX md5c; 192 193 /* HMAC_MD5(K, T) = MD5(K XOR OPAD, MD5(K XOR IPAD, T)) */ 194 195 memset(ipad_key, 0, sizeof(ipad_key)); 196 memset(opad_key, 0, sizeof(opad_key)); 197 198 if (keylen > 64) { 199 hmac_key = MD5(key, keylen, NULL); 200 keylen = MD5_DIGEST_LENGTH; 201 } else 202 hmac_key = (const unsigned char *)key; 203 204 for (i = 0; i < HMAC_MD5_KEYSIZE ; i++) { 205 unsigned char k = i < keylen ? hmac_key[i] : 0; 206 ipad_key[i] = k ^ HMAC_MD5_IPAD; 207 opad_key[i] = k ^ HMAC_MD5_OPAD; 208 } 209 210 /* d = MD5(K XOR IPAD, T) */ 211 MD5_Init(&md5c); 212 MD5_Update(&md5c, ipad_key, HMAC_MD5_KEYSIZE); 213 MD5_Update(&md5c, in, inlen); 214 MD5_Final(digest, &md5c); 215 /* MD5(K XOR OPAD, d) */ 216 MD5_Init(&md5c); 217 MD5_Update(&md5c, opad_key, HMAC_MD5_KEYSIZE); 218 MD5_Update(&md5c, digest, MD5_DIGEST_LENGTH); 219 MD5_Final(digest, &md5c); 220 221 return saslc__digest_to_ascii(digest); 222 } 223