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. */
78int
79digest_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 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 tmplen = MD5LEN + strlen (COLON) + strlen (nonce) +
98 strlen (COLON) + strlen (cnonce);
99 if (authzid && strlen (authzid) > 0)
100 tmplen += strlen (COLON) + strlen (authzid);
101
102 p = tmp = malloc (tmplen);
103 if (tmp == NULL)
104 return -1;
105
106 memcpy (p, secret, MD5LEN);
107 p += MD5LEN;
108 memcpy (p, COLON, strlen (COLON));
109 p += strlen (COLON);
110 memcpy (p, nonce, strlen (nonce));
111 p += strlen (nonce);
112 memcpy (p, COLON, strlen (COLON));
113 p += strlen (COLON);
114 memcpy (p, cnonce, strlen (cnonce));
115 p += strlen (cnonce);
116 if (authzid && strlen (authzid) > 0)
117 {
118 memcpy (p, COLON, strlen (COLON));
119 p += strlen (COLON);
120 memcpy (p, authzid, strlen (authzid));
121 p += strlen (authzid);
122 }
123
124 rc = gc_md5 (tmp, tmplen, hash);
125 free (tmp);
126 if (rc)
127 return rc;
128
129 if (kic)
130 {
131 char hash2[MD5LEN];
132 char q[MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN];
133 size_t qlen = MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN;
134
135 memcpy (q, hash, MD5LEN);
136 memcpy (q + MD5LEN, DERIVE_CLIENT_INTEGRITY_KEY_STRING,
137 DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN);
138
139 rc = gc_md5 (q, qlen, hash2);
140 if (rc)
141 return rc;
142
143 memcpy (kic, hash2, MD5LEN);
144 }
145
146 if (kis)
147 {
148 char hash2[MD5LEN];
149 char q[MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN];
150 size_t qlen = MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN;
151
152 memcpy (q, hash, MD5LEN);
153 memcpy (q + MD5LEN, DERIVE_SERVER_INTEGRITY_KEY_STRING,
154 DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN);
155
156 rc = gc_md5 (q, qlen, hash2);
157 if (rc)
158 return rc;
159
160 memcpy (kis, hash2, MD5LEN);
161 }
162
163 if (kcc)
164 {
165 char hash2[MD5LEN];
166 int n;
167 char q[MD5LEN + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN];
168
169 if (cipher == DIGEST_MD5_CIPHER_RC4_40)
170 n = 5;
171 else if (cipher == DIGEST_MD5_CIPHER_RC4_56)
172 n = 7;
173 else
174 n = MD5LEN;
175
176 memcpy (q, hash, n);
177 memcpy (q + n, DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING,
178 DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN);
179
180 rc = gc_md5 (q, n + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN,
181 hash2);
182 if (rc)
183 return rc;
184
185 memcpy (kcc, hash2, MD5LEN);
186 }
187
188 if (kcs)
189 {
190 char hash2[MD5LEN];
191 int n;
192 char q[MD5LEN + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN];
193
194 if (cipher == DIGEST_MD5_CIPHER_RC4_40)
195 n = 5;
196 else if (cipher == DIGEST_MD5_CIPHER_RC4_56)
197 n = 7;
198 else
199 n = MD5LEN;
200
201 memcpy (q, hash, n);
202 memcpy (q + n, DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING,
203 DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN);
204
205 rc = gc_md5 (q, n + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN,
206 hash2);
207 if (rc)
208 return rc;
209
210 memcpy (kcs, hash2, MD5LEN);
211 }
212
213 for (i = 0; i < MD5LEN; i++)
214 {
215 a1hexhash[2 * i + 1] = HEXCHAR (hash[i]);
216 a1hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4);
217 }
218
219 /* A2 */
220
221 tmplen = strlen (a2string) + strlen (digesturi);
222 if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF)
223 tmplen += strlen (A2_POST);
224
225 p = tmp = malloc (tmplen);
226 if (tmp == NULL)
227 return -1;
228
229 memcpy (p, a2string, strlen (a2string));
230 p += strlen (a2string);
231 memcpy (p, digesturi, strlen (digesturi));
232 p += strlen (digesturi);
233 if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF)
234 memcpy (p, A2_POST, strlen (A2_POST));
235
236 rc = gc_md5 (tmp, tmplen, hash);
237 free (tmp);
238 if (rc)
239 return rc;
240
241 for (i = 0; i < MD5LEN; i++)
242 {
243 a2hexhash[2 * i + 1] = HEXCHAR (hash[i]);
244 a2hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4);
245 }
246
247 /* response_value */
248
249 sprintf (nchex, "%08lx", nc);
250
251 tmplen = 2 * MD5LEN + strlen (COLON) + strlen (nonce) + strlen (COLON) +
252 strlen (nchex) + strlen (COLON) + strlen (cnonce) + strlen (COLON);
253 if (qop & DIGEST_MD5_QOP_AUTH_CONF)
254 tmplen += strlen (QOP_AUTH_CONF);
255 else if (qop & DIGEST_MD5_QOP_AUTH_INT)
256 tmplen += strlen (QOP_AUTH_INT);
257 else if (qop & DIGEST_MD5_QOP_AUTH)
258 tmplen += strlen (QOP_AUTH);
259 tmplen += strlen (COLON) + 2 * MD5LEN;
260
261 p = tmp = malloc (tmplen);
262 if (tmp == NULL)
263 return -1;
264
265 memcpy (p, a1hexhash, 2 * MD5LEN);
266 p += 2 * MD5LEN;
267 memcpy (p, COLON, strlen (COLON));
268 p += strlen (COLON);
269 memcpy (p, nonce, strlen (nonce));
270 p += strlen (nonce);
271 memcpy (p, COLON, strlen (COLON));
272 p += strlen (COLON);
273 memcpy (p, nchex, strlen (nchex));
274 p += strlen (nchex);
275 memcpy (p, COLON, strlen (COLON));
276 p += strlen (COLON);
277 memcpy (p, cnonce, strlen (cnonce));
278 p += strlen (cnonce);
279 memcpy (p, COLON, strlen (COLON));
280 p += strlen (COLON);
281 if (qop & DIGEST_MD5_QOP_AUTH_CONF)
282 {
283 memcpy (p, QOP_AUTH_CONF, strlen (QOP_AUTH_CONF));
284 p += strlen (QOP_AUTH_CONF);
285 }
286 else if (qop & DIGEST_MD5_QOP_AUTH_INT)
287 {
288 memcpy (p, QOP_AUTH_INT, strlen (QOP_AUTH_INT));
289 p += strlen (QOP_AUTH_INT);
290 }
291 else if (qop & DIGEST_MD5_QOP_AUTH)
292 {
293 memcpy (p, QOP_AUTH, strlen (QOP_AUTH));
294 p += strlen (QOP_AUTH);
295 }
296 memcpy (p, COLON, strlen (COLON));
297 p += strlen (COLON);
298 memcpy (p, a2hexhash, 2 * MD5LEN);
299
300 rc = gc_md5 (tmp, tmplen, hash);
301 free (tmp);
302 if (rc)
303 return rc;
304
305 for (i = 0; i < MD5LEN; i++)
306 {
307 output[2 * i + 1] = HEXCHAR (hash[i]);
308 output[2 * i + 0] = HEXCHAR (hash[i] >> 4);
309 }
310 output[32] = '\0';
311
312 return 0;
313}
314