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
saslc__crypto_encode_base64(const void * in,size_t inlen,char ** out,size_t * outlen)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
saslc__crypto_decode_base64(const char * in,size_t inlen,void ** out,size_t * outlen)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 *
saslc__crypto_nonce(size_t len)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 *
saslc__crypto_hash_to_hex(const uint8_t * hash)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
saslc__crypto_md5_hash(const char * buf,size_t buflen,unsigned char * digest)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 *
saslc__crypto_md5_hex(const char * buf,size_t buflen)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
saslc__crypto_hmac_md5_hash(const unsigned char * key,size_t keylen,const unsigned char * in,size_t inlen,unsigned char * hmac)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 *
saslc__crypto_hmac_md5_hex(const unsigned char * key,size_t keylen,const unsigned char * in,size_t inlen)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