Branch data Line data Source code
1 : : /* digesthmac.c --- Compute DIGEST-MD5 response value.
2 : : * Copyright (C) 2002-2012 Simon Josefsson
3 : : *
4 : : * This file is part of GNU SASL Library.
5 : : *
6 : : * GNU SASL Library is free software; you can redistribute it and/or
7 : : * modify it under the terms of the GNU Lesser General Public License
8 : : * as published by the Free Software Foundation; either version 2.1 of
9 : : * the License, or (at your option) any later version.
10 : : *
11 : : * GNU SASL Library is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with GNU SASL Library; if not, write to the Free
18 : : * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 : : * Boston, MA 02110-1301, USA.
20 : : *
21 : : */
22 : :
23 : : #ifdef HAVE_CONFIG_H
24 : : #include "config.h"
25 : : #endif
26 : :
27 : : /* Get specification. */
28 : : #include "digesthmac.h"
29 : :
30 : : /* Get malloc, free. */
31 : : #include <stdlib.h>
32 : :
33 : : /* Get memcpy, strlen. */
34 : : #include <string.h>
35 : :
36 : : /* Get sprintf. */
37 : : #include <stdio.h>
38 : :
39 : : /* Get gc_md5. */
40 : : #include <gc.h>
41 : :
42 : : #define HEXCHAR(c) ((c & 0x0F) > 9 ? 'a' + (c & 0x0F) - 10 : '0' + (c & 0x0F))
43 : :
44 : : #define QOP_AUTH "auth"
45 : : #define QOP_AUTH_INT "auth-int"
46 : : #define QOP_AUTH_CONF "auth-conf"
47 : :
48 : : #define A2_PRE "AUTHENTICATE:"
49 : : #define A2_POST ":00000000000000000000000000000000"
50 : : #define COLON ":"
51 : : #define MD5LEN 16
52 : : #define DERIVE_CLIENT_INTEGRITY_KEY_STRING \
53 : : "Digest session key to client-to-server signing key magic constant"
54 : : #define DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN 65
55 : : #define DERIVE_SERVER_INTEGRITY_KEY_STRING \
56 : : "Digest session key to server-to-client signing key magic constant"
57 : : #define DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN 65
58 : : #define DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING \
59 : : "Digest H(A1) to client-to-server sealing key magic constant"
60 : : #define DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN 59
61 : : #define DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING \
62 : : "Digest H(A1) to server-to-client sealing key magic constant"
63 : : #define DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN 59
64 : :
65 : : /* Compute in 33 bytes large array OUTPUT the DIGEST-MD5 response
66 : : value. SECRET holds the 16 bytes MD5 hash SS, i.e.,
67 : : H(username:realm:passwd). NONCE is a zero terminated string with
68 : : the server nonce. NC is the nonce-count, typically 1 for initial
69 : : authentication. CNONCE is a zero terminated string with the client
70 : : nonce. QOP is the quality of protection to use. AUTHZID is a zero
71 : : terminated string with the authorization identity. DIGESTURI is a
72 : : zero terminated string with the server principal (e.g.,
73 : : imap/mail.example.org). RSPAUTH is a boolean which indicate
74 : : whether to compute a value for the RSPAUTH response or the "real"
75 : : authentication. CIPHER is the cipher to use. KIC, KIS, KCC, KCS
76 : : are either NULL, or points to 16 byte arrays that will hold the
77 : : computed keys on output. Returns 0 on success. */
78 : : int
79 : 62 : digest_md5_hmac (char *output, char secret[MD5LEN], const char *nonce,
80 : : unsigned long nc, const char *cnonce, digest_md5_qop qop,
81 : : const char *authzid, const char *digesturi, int rspauth,
82 : : digest_md5_cipher cipher,
83 : : char *kic, char *kis, char *kcc, char *kcs)
84 : : {
85 [ + + ]: 62 : const char *a2string = rspauth ? COLON : A2_PRE;
86 : : char nchex[9];
87 : : char a1hexhash[2 * MD5LEN];
88 : : char a2hexhash[2 * MD5LEN];
89 : : char hash[MD5LEN];
90 : : char *tmp, *p;
91 : : size_t tmplen;
92 : : int rc;
93 : : int i;
94 : :
95 : : /* A1 */
96 : :
97 : 124 : tmplen = MD5LEN + strlen (COLON) + strlen (nonce) +
98 : 62 : strlen (COLON) + strlen (cnonce);
99 [ + + ][ + - ]: 62 : if (authzid && strlen (authzid) > 0)
100 : 22 : tmplen += strlen (COLON) + strlen (authzid);
101 : :
102 : 62 : p = tmp = malloc (tmplen);
103 [ - + ]: 62 : if (tmp == NULL)
104 : 0 : return -1;
105 : :
106 : 62 : memcpy (p, secret, MD5LEN);
107 : 62 : p += MD5LEN;
108 : 62 : memcpy (p, COLON, strlen (COLON));
109 : 62 : p += strlen (COLON);
110 : 62 : memcpy (p, nonce, strlen (nonce));
111 : 62 : p += strlen (nonce);
112 : 62 : memcpy (p, COLON, strlen (COLON));
113 : 62 : p += strlen (COLON);
114 : 62 : memcpy (p, cnonce, strlen (cnonce));
115 : 62 : p += strlen (cnonce);
116 [ + + ][ + - ]: 62 : if (authzid && strlen (authzid) > 0)
117 : : {
118 : 22 : memcpy (p, COLON, strlen (COLON));
119 : 22 : p += strlen (COLON);
120 : 22 : memcpy (p, authzid, strlen (authzid));
121 : 22 : p += strlen (authzid);
122 : : }
123 : :
124 : 62 : rc = gc_md5 (tmp, tmplen, hash);
125 : 62 : free (tmp);
126 [ - + ]: 62 : if (rc)
127 : 0 : return rc;
128 : :
129 [ + + ]: 62 : if (kic)
130 : : {
131 : : char hash2[MD5LEN];
132 : : char q[MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN];
133 : 30 : size_t qlen = MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN;
134 : :
135 : 30 : memcpy (q, hash, MD5LEN);
136 : 30 : memcpy (q + MD5LEN, DERIVE_CLIENT_INTEGRITY_KEY_STRING,
137 : : DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN);
138 : :
139 : 30 : rc = gc_md5 (q, qlen, hash2);
140 [ - + ]: 30 : if (rc)
141 : 0 : return rc;
142 : :
143 : 30 : memcpy (kic, hash2, MD5LEN);
144 : : }
145 : :
146 [ + + ]: 62 : if (kis)
147 : : {
148 : : char hash2[MD5LEN];
149 : : char q[MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN];
150 : 30 : size_t qlen = MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN;
151 : :
152 : 30 : memcpy (q, hash, MD5LEN);
153 : 30 : memcpy (q + MD5LEN, DERIVE_SERVER_INTEGRITY_KEY_STRING,
154 : : DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN);
155 : :
156 : 30 : rc = gc_md5 (q, qlen, hash2);
157 [ - + ]: 30 : if (rc)
158 : 0 : return rc;
159 : :
160 : 30 : memcpy (kis, hash2, MD5LEN);
161 : : }
162 : :
163 [ + + ]: 62 : if (kcc)
164 : : {
165 : : char hash2[MD5LEN];
166 : : int n;
167 : : char q[MD5LEN + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN];
168 : :
169 [ - + ]: 30 : if (cipher == DIGEST_MD5_CIPHER_RC4_40)
170 : 0 : n = 5;
171 [ - + ]: 30 : else if (cipher == DIGEST_MD5_CIPHER_RC4_56)
172 : 0 : n = 7;
173 : : else
174 : 30 : n = MD5LEN;
175 : :
176 : 30 : memcpy (q, hash, n);
177 : 30 : memcpy (q + n, DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING,
178 : : DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN);
179 : :
180 : 30 : rc = gc_md5 (q, n + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN,
181 : : hash2);
182 [ - + ]: 30 : if (rc)
183 : 0 : return rc;
184 : :
185 : 30 : memcpy (kcc, hash2, MD5LEN);
186 : : }
187 : :
188 [ + + ]: 62 : if (kcs)
189 : : {
190 : : char hash2[MD5LEN];
191 : : int n;
192 : : char q[MD5LEN + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN];
193 : :
194 [ - + ]: 30 : if (cipher == DIGEST_MD5_CIPHER_RC4_40)
195 : 0 : n = 5;
196 [ - + ]: 30 : else if (cipher == DIGEST_MD5_CIPHER_RC4_56)
197 : 0 : n = 7;
198 : : else
199 : 30 : n = MD5LEN;
200 : :
201 : 30 : memcpy (q, hash, n);
202 : 30 : memcpy (q + n, DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING,
203 : : DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN);
204 : :
205 : 30 : rc = gc_md5 (q, n + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN,
206 : : hash2);
207 [ - + ]: 30 : if (rc)
208 : 0 : return rc;
209 : :
210 : 30 : memcpy (kcs, hash2, MD5LEN);
211 : : }
212 : :
213 [ + + ]: 1054 : for (i = 0; i < MD5LEN; i++)
214 : : {
215 [ + + ]: 992 : a1hexhash[2 * i + 1] = HEXCHAR (hash[i]);
216 [ + + ]: 992 : a1hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4);
217 : : }
218 : :
219 : : /* A2 */
220 : :
221 : 62 : tmplen = strlen (a2string) + strlen (digesturi);
222 [ + + ][ - + ]: 62 : if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF)
223 : 8 : tmplen += strlen (A2_POST);
224 : :
225 : 62 : p = tmp = malloc (tmplen);
226 [ - + ]: 62 : if (tmp == NULL)
227 : 0 : return -1;
228 : :
229 : 62 : memcpy (p, a2string, strlen (a2string));
230 : 62 : p += strlen (a2string);
231 : 62 : memcpy (p, digesturi, strlen (digesturi));
232 : 62 : p += strlen (digesturi);
233 [ + + ][ - + ]: 62 : if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF)
234 : 8 : memcpy (p, A2_POST, strlen (A2_POST));
235 : :
236 : 62 : rc = gc_md5 (tmp, tmplen, hash);
237 : 62 : free (tmp);
238 [ - + ]: 62 : if (rc)
239 : 0 : return rc;
240 : :
241 [ + + ]: 1054 : for (i = 0; i < MD5LEN; i++)
242 : : {
243 [ + + ]: 992 : a2hexhash[2 * i + 1] = HEXCHAR (hash[i]);
244 [ + + ]: 992 : a2hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4);
245 : : }
246 : :
247 : : /* response_value */
248 : :
249 : 62 : sprintf (nchex, "%08lx", nc);
250 : :
251 : 186 : tmplen = 2 * MD5LEN + strlen (COLON) + strlen (nonce) + strlen (COLON) +
252 : 124 : strlen (nchex) + strlen (COLON) + strlen (cnonce) + strlen (COLON);
253 [ - + ]: 62 : if (qop & DIGEST_MD5_QOP_AUTH_CONF)
254 : 0 : tmplen += strlen (QOP_AUTH_CONF);
255 [ + + ]: 62 : else if (qop & DIGEST_MD5_QOP_AUTH_INT)
256 : 8 : tmplen += strlen (QOP_AUTH_INT);
257 [ + - ]: 54 : else if (qop & DIGEST_MD5_QOP_AUTH)
258 : 54 : tmplen += strlen (QOP_AUTH);
259 : 62 : tmplen += strlen (COLON) + 2 * MD5LEN;
260 : :
261 : 62 : p = tmp = malloc (tmplen);
262 [ - + ]: 62 : if (tmp == NULL)
263 : 0 : return -1;
264 : :
265 : 62 : memcpy (p, a1hexhash, 2 * MD5LEN);
266 : 62 : p += 2 * MD5LEN;
267 : 62 : memcpy (p, COLON, strlen (COLON));
268 : 62 : p += strlen (COLON);
269 : 62 : memcpy (p, nonce, strlen (nonce));
270 : 62 : p += strlen (nonce);
271 : 62 : memcpy (p, COLON, strlen (COLON));
272 : 62 : p += strlen (COLON);
273 : 62 : memcpy (p, nchex, strlen (nchex));
274 : 62 : p += strlen (nchex);
275 : 62 : memcpy (p, COLON, strlen (COLON));
276 : 62 : p += strlen (COLON);
277 : 62 : memcpy (p, cnonce, strlen (cnonce));
278 : 62 : p += strlen (cnonce);
279 : 62 : memcpy (p, COLON, strlen (COLON));
280 : 62 : p += strlen (COLON);
281 [ - + ]: 62 : if (qop & DIGEST_MD5_QOP_AUTH_CONF)
282 : : {
283 : 0 : memcpy (p, QOP_AUTH_CONF, strlen (QOP_AUTH_CONF));
284 : 0 : p += strlen (QOP_AUTH_CONF);
285 : : }
286 [ + + ]: 62 : else if (qop & DIGEST_MD5_QOP_AUTH_INT)
287 : : {
288 : 8 : memcpy (p, QOP_AUTH_INT, strlen (QOP_AUTH_INT));
289 : 8 : p += strlen (QOP_AUTH_INT);
290 : : }
291 [ + - ]: 54 : else if (qop & DIGEST_MD5_QOP_AUTH)
292 : : {
293 : 54 : memcpy (p, QOP_AUTH, strlen (QOP_AUTH));
294 : 54 : p += strlen (QOP_AUTH);
295 : : }
296 : 62 : memcpy (p, COLON, strlen (COLON));
297 : 62 : p += strlen (COLON);
298 : 62 : memcpy (p, a2hexhash, 2 * MD5LEN);
299 : :
300 : 62 : rc = gc_md5 (tmp, tmplen, hash);
301 : 62 : free (tmp);
302 [ - + ]: 62 : if (rc)
303 : 0 : return rc;
304 : :
305 [ + + ]: 1054 : for (i = 0; i < MD5LEN; i++)
306 : : {
307 [ + + ]: 992 : output[2 * i + 1] = HEXCHAR (hash[i]);
308 [ + + ]: 992 : output[2 * i + 0] = HEXCHAR (hash[i] >> 4);
309 : : }
310 : 62 : output[32] = '\0';
311 : :
312 : 62 : return 0;
313 : : }
|