Update gnulib.
[gsasl.git] / lib / digest-md5 / digesthmac.c
blobba5a354dc878b5b99ffdb07c084f383b7584635c
1 /* digesthmac.c --- Compute DIGEST-MD5 response value.
2 * Copyright (C) 2002, 2003, 2004 Simon Josefsson
4 * This file is part of GNU SASL Library.
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.
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.
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.
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 /* Get specification. */
28 #include "digesthmac.h"
30 /* Get malloc, free. */
31 #include <stdlib.h>
33 /* Get memcpy, strlen. */
34 #include <string.h>
36 /* Get sprintf. */
37 #include <stdio.h>
39 /* Get gc_md5. */
40 #include <gc.h>
42 #define HEXCHAR(c) ((c & 0x0F) > 9 ? 'a' + (c & 0x0F) - 10 : '0' + (c & 0x0F))
44 #define QOP_AUTH "auth"
45 #define QOP_AUTH_INT "auth-int"
46 #define QOP_AUTH_CONF "auth-conf"
48 #define A2_PRE "AUTHENTICATE:"
49 #define A2_POST ":00000000000000000000000000000000"
50 #define COLON ":"
51 #define MD5LEN 16
52 #define RESPONSE_LENGTH 32
53 #define RSPAUTH_LENGTH RESPONSE_LENGTH
54 #define DERIVE_CLIENT_INTEGRITY_KEY_STRING \
55 "Digest session key to client-to-server signing key magic constant"
56 #define DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN 65
57 #define DERIVE_SERVER_INTEGRITY_KEY_STRING \
58 "Digest session key to server-to-client signing key magic constant"
59 #define DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN 65
60 #define DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING \
61 "Digest H(A1) to client-to-server sealing key magic constant"
62 #define DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN 59
63 #define DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING \
64 "Digest H(A1) to server-to-client sealing key magic constant"
65 #define DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN 59
67 /* Compute in 33 bytes large array OUTPUT the DIGEST-MD5 response
68 value. SECRET holds the 16 bytes MD5 hash SS, i.e.,
69 H(username:realm:passwd). NONCE is a zero terminated string with
70 the server nonce. NC is the nonce-count, typically 1 for initial
71 authentication. CNONCE is a zero terminated string with the client
72 nonce. QOP is the quality of protection to use. AUTHZID is a zero
73 terminated string with the authorization identity. DIGESTURI is a
74 zero terminated string with the server principal (e.g.,
75 imap/mail.example.org). RSPAUTH is a boolean which indicate
76 whether to compute a value for the RSPAUTH response or the "real"
77 authentication. CIPHER is the cipher to use. KIC, KIS, KCC, KCS
78 are either NULL, or points to 16 byte arrays that will hold the
79 computed keys on output. Returns 0 on success. */
80 int
81 digest_md5_hmac (char *output, char secret[MD5LEN], char *nonce,
82 unsigned long nc, char *cnonce, digest_md5_qop qop,
83 char *authzid, char *digesturi, int rspauth,
84 digest_md5_cipher cipher,
85 char *kic, char *kis, char *kcc, char *kcs)
87 const char *a2string = rspauth ? COLON : A2_PRE;
88 char nchex[9];
89 char a1hexhash[2 * MD5LEN];
90 char a2hexhash[2 * MD5LEN];
91 char hash[MD5LEN];
92 char *tmp, *p;
93 size_t tmplen;
94 int rc;
95 int i;
97 /* A1 */
99 tmplen = MD5LEN + strlen (COLON) + strlen (nonce) +
100 strlen (COLON) + strlen (cnonce);
101 if (authzid && strlen (authzid) > 0)
102 tmplen += strlen (COLON) + strlen (authzid);
104 p = tmp = malloc (tmplen);
105 if (tmp == NULL)
106 return -1;
108 memcpy (p, secret, MD5LEN);
109 p += MD5LEN;
110 memcpy (p, COLON, strlen (COLON));
111 p += strlen (COLON);
112 memcpy (p, nonce, strlen (nonce));
113 p += strlen (nonce);
114 memcpy (p, COLON, strlen (COLON));
115 p += strlen (COLON);
116 memcpy (p, cnonce, strlen (cnonce));
117 p += strlen (cnonce);
118 if (authzid && strlen (authzid) > 0)
120 memcpy (p, COLON, strlen (COLON));
121 p += strlen (COLON);
122 memcpy (p, authzid, strlen (authzid));
123 p += strlen (authzid);
126 rc = gc_md5 (tmp, tmplen, hash);
127 free (tmp);
128 if (rc)
129 return rc;
131 if (kic)
133 char hash2[MD5LEN];
134 char tmp[MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN];
135 size_t tmplen = MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN;
137 memcpy (tmp, hash, MD5LEN);
138 memcpy (tmp + MD5LEN, DERIVE_CLIENT_INTEGRITY_KEY_STRING,
139 DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN);
141 rc = gc_md5 (tmp, tmplen, hash2);
142 if (rc)
143 return rc;
145 memcpy (kic, hash2, MD5LEN);
148 if (kis)
150 char hash2[MD5LEN];
151 char tmp[MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN];
153 memcpy (tmp, hash, MD5LEN);
154 memcpy (tmp + MD5LEN, DERIVE_SERVER_INTEGRITY_KEY_STRING,
155 DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN);
157 rc = gc_md5 (tmp,
158 MD5LEN + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN,
159 hash2);
160 if (rc)
161 return rc;
163 memcpy (kis, hash2, MD5LEN);
166 if (kcc)
168 char hash2[MD5LEN];
169 int n;
170 char tmp[MD5LEN + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN];
172 if (cipher == DIGEST_MD5_CIPHER_RC4_40)
173 n = 5;
174 else if (cipher == DIGEST_MD5_CIPHER_RC4_56)
175 n = 7;
176 else
177 n = MD5LEN;
179 memcpy (tmp, hash, n);
180 memcpy (tmp + n, DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING,
181 DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN);
183 rc = gc_md5 (tmp, n + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN,
184 hash2);
185 if (rc)
186 return rc;
188 memcpy (kcc, hash2, MD5LEN);
191 if (kcs)
193 char hash2[MD5LEN];
194 int n;
195 char tmp[MD5LEN + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN];
197 if (cipher == DIGEST_MD5_CIPHER_RC4_40)
198 n = 5;
199 else if (cipher == DIGEST_MD5_CIPHER_RC4_56)
200 n = 7;
201 else
202 n = MD5LEN;
204 memcpy (tmp, hash, n);
205 memcpy (tmp + n, DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING,
206 DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN);
208 rc = gc_md5 (tmp, n + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN,
209 hash2);
210 if (rc)
211 return rc;
213 memcpy (kcs, hash2, MD5LEN);
216 for (i = 0; i < MD5LEN; i++)
218 a1hexhash[2 * i + 1] = HEXCHAR (hash[i]);
219 a1hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4);
222 /* A2 */
224 tmplen = strlen (a2string) + strlen (digesturi);
225 if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF)
226 tmplen += strlen (A2_POST);
228 p = tmp = malloc (tmplen);
229 if (tmp == NULL)
230 return -1;
232 memcpy (p, a2string, strlen (a2string));
233 p += strlen (a2string);
234 memcpy (p, digesturi, strlen (digesturi));
235 p += strlen (digesturi);
236 if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF)
237 memcpy (p, A2_POST, strlen (A2_POST));
239 rc = gc_md5 (tmp, tmplen, hash);
240 free (tmp);
241 if (rc)
242 return rc;
244 for (i = 0; i < MD5LEN; i++)
246 a2hexhash[2 * i + 1] = HEXCHAR (hash[i]);
247 a2hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4);
250 /* response_value */
252 sprintf (nchex, "%08lx", nc);
254 tmplen = 2 * MD5LEN + strlen (COLON) + strlen (nonce) + strlen (COLON) +
255 strlen (nchex) + strlen (COLON) + strlen (cnonce) + strlen (COLON);
256 if (qop & DIGEST_MD5_QOP_AUTH_CONF)
257 tmplen += strlen (QOP_AUTH_CONF);
258 else if (qop & DIGEST_MD5_QOP_AUTH_INT)
259 tmplen += strlen (QOP_AUTH_INT);
260 else if (qop & DIGEST_MD5_QOP_AUTH)
261 tmplen += strlen (QOP_AUTH);
262 tmplen += strlen (COLON) + 2 * MD5LEN;
264 p = tmp = malloc (tmplen);
265 if (tmp == NULL)
266 return -1;
268 memcpy (p, a1hexhash, 2 * MD5LEN);
269 p += 2 * MD5LEN;
270 memcpy (p, COLON, strlen (COLON));
271 p += strlen (COLON);
272 memcpy (p, nonce, strlen (nonce));
273 p += strlen (nonce);
274 memcpy (p, COLON, strlen (COLON));
275 p += strlen (COLON);
276 memcpy (p, nchex, strlen (nchex));
277 p += strlen (nchex);
278 memcpy (p, COLON, strlen (COLON));
279 p += strlen (COLON);
280 memcpy (p, cnonce, strlen (cnonce));
281 p += strlen (cnonce);
282 memcpy (p, COLON, strlen (COLON));
283 p += strlen (COLON);
284 if (qop & DIGEST_MD5_QOP_AUTH_CONF)
286 memcpy (p, QOP_AUTH_CONF, strlen (QOP_AUTH_CONF));
287 p += strlen (QOP_AUTH_CONF);
289 else if (qop & DIGEST_MD5_QOP_AUTH_INT)
291 memcpy (p, QOP_AUTH_INT, strlen (QOP_AUTH_INT));
292 p += strlen (QOP_AUTH_INT);
294 else if (qop & DIGEST_MD5_QOP_AUTH)
296 memcpy (p, QOP_AUTH, strlen (QOP_AUTH));
297 p += strlen (QOP_AUTH);
299 memcpy (p, COLON, strlen (COLON));
300 p += strlen (COLON);
301 memcpy (p, a2hexhash, 2 * MD5LEN);
303 rc = gc_md5 (tmp, tmplen, hash);
304 free (tmp);
305 if (rc)
306 return rc;
308 for (i = 0; i < MD5LEN; i++)
310 output[2 * i + 1] = HEXCHAR (hash[i]);
311 output[2 * i + 0] = HEXCHAR (hash[i] >> 4);
313 output[32] = '\0';
315 return 0;