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