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 | 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 | 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 | |