|
gsasl
1.8.0
|
00001 /* digesthmac.c --- Compute DIGEST-MD5 response value. 00002 * Copyright (C) 2002-2012 Simon Josefsson 00003 * 00004 * This file is part of GNU SASL Library. 00005 * 00006 * GNU SASL Library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public License 00008 * as published by the Free Software Foundation; either version 2.1 of 00009 * the License, or (at your option) any later version. 00010 * 00011 * GNU SASL Library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with GNU SASL Library; if not, write to the Free 00018 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 * Boston, MA 02110-1301, USA. 00020 * 00021 */ 00022 00023 #ifdef HAVE_CONFIG_H 00024 #include "config.h" 00025 #endif 00026 00027 /* Get specification. */ 00028 #include "digesthmac.h" 00029 00030 /* Get malloc, free. */ 00031 #include <stdlib.h> 00032 00033 /* Get memcpy, strlen. */ 00034 #include <string.h> 00035 00036 /* Get sprintf. */ 00037 #include <stdio.h> 00038 00039 /* Get gc_md5. */ 00040 #include <gc.h> 00041 00042 #define HEXCHAR(c) ((c & 0x0F) > 9 ? 'a' + (c & 0x0F) - 10 : '0' + (c & 0x0F)) 00043 00044 #define QOP_AUTH "auth" 00045 #define QOP_AUTH_INT "auth-int" 00046 #define QOP_AUTH_CONF "auth-conf" 00047 00048 #define A2_PRE "AUTHENTICATE:" 00049 #define A2_POST ":00000000000000000000000000000000" 00050 #define COLON ":" 00051 #define MD5LEN 16 00052 #define DERIVE_CLIENT_INTEGRITY_KEY_STRING \ 00053 "Digest session key to client-to-server signing key magic constant" 00054 #define DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN 65 00055 #define DERIVE_SERVER_INTEGRITY_KEY_STRING \ 00056 "Digest session key to server-to-client signing key magic constant" 00057 #define DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN 65 00058 #define DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING \ 00059 "Digest H(A1) to client-to-server sealing key magic constant" 00060 #define DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN 59 00061 #define DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING \ 00062 "Digest H(A1) to server-to-client sealing key magic constant" 00063 #define DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN 59 00064 00065 /* Compute in 33 bytes large array OUTPUT the DIGEST-MD5 response 00066 value. SECRET holds the 16 bytes MD5 hash SS, i.e., 00067 H(username:realm:passwd). NONCE is a zero terminated string with 00068 the server nonce. NC is the nonce-count, typically 1 for initial 00069 authentication. CNONCE is a zero terminated string with the client 00070 nonce. QOP is the quality of protection to use. AUTHZID is a zero 00071 terminated string with the authorization identity. DIGESTURI is a 00072 zero terminated string with the server principal (e.g., 00073 imap/mail.example.org). RSPAUTH is a boolean which indicate 00074 whether to compute a value for the RSPAUTH response or the "real" 00075 authentication. CIPHER is the cipher to use. KIC, KIS, KCC, KCS 00076 are either NULL, or points to 16 byte arrays that will hold the 00077 computed keys on output. Returns 0 on success. */ 00078 int 00079 digest_md5_hmac (char *output, char secret[MD5LEN], const char *nonce, 00080 unsigned long nc, const char *cnonce, digest_md5_qop qop, 00081 const char *authzid, const char *digesturi, int rspauth, 00082 digest_md5_cipher cipher, 00083 char *kic, char *kis, char *kcc, char *kcs) 00084 { 00085 const char *a2string = rspauth ? COLON : A2_PRE; 00086 char nchex[9]; 00087 char a1hexhash[2 * MD5LEN]; 00088 char a2hexhash[2 * MD5LEN]; 00089 char hash[MD5LEN]; 00090 char *tmp, *p; 00091 size_t tmplen; 00092 int rc; 00093 int i; 00094 00095 /* A1 */ 00096 00097 tmplen = MD5LEN + strlen (COLON) + strlen (nonce) + 00098 strlen (COLON) + strlen (cnonce); 00099 if (authzid && strlen (authzid) > 0) 00100 tmplen += strlen (COLON) + strlen (authzid); 00101 00102 p = tmp = malloc (tmplen); 00103 if (tmp == NULL) 00104 return -1; 00105 00106 memcpy (p, secret, MD5LEN); 00107 p += MD5LEN; 00108 memcpy (p, COLON, strlen (COLON)); 00109 p += strlen (COLON); 00110 memcpy (p, nonce, strlen (nonce)); 00111 p += strlen (nonce); 00112 memcpy (p, COLON, strlen (COLON)); 00113 p += strlen (COLON); 00114 memcpy (p, cnonce, strlen (cnonce)); 00115 p += strlen (cnonce); 00116 if (authzid && strlen (authzid) > 0) 00117 { 00118 memcpy (p, COLON, strlen (COLON)); 00119 p += strlen (COLON); 00120 memcpy (p, authzid, strlen (authzid)); 00121 p += strlen (authzid); 00122 } 00123 00124 rc = gc_md5 (tmp, tmplen, hash); 00125 free (tmp); 00126 if (rc) 00127 return rc; 00128 00129 if (kic) 00130 { 00131 char hash2[MD5LEN]; 00132 char q[MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN]; 00133 size_t qlen = MD5LEN + DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN; 00134 00135 memcpy (q, hash, MD5LEN); 00136 memcpy (q + MD5LEN, DERIVE_CLIENT_INTEGRITY_KEY_STRING, 00137 DERIVE_CLIENT_INTEGRITY_KEY_STRING_LEN); 00138 00139 rc = gc_md5 (q, qlen, hash2); 00140 if (rc) 00141 return rc; 00142 00143 memcpy (kic, hash2, MD5LEN); 00144 } 00145 00146 if (kis) 00147 { 00148 char hash2[MD5LEN]; 00149 char q[MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN]; 00150 size_t qlen = MD5LEN + DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN; 00151 00152 memcpy (q, hash, MD5LEN); 00153 memcpy (q + MD5LEN, DERIVE_SERVER_INTEGRITY_KEY_STRING, 00154 DERIVE_SERVER_INTEGRITY_KEY_STRING_LEN); 00155 00156 rc = gc_md5 (q, qlen, hash2); 00157 if (rc) 00158 return rc; 00159 00160 memcpy (kis, hash2, MD5LEN); 00161 } 00162 00163 if (kcc) 00164 { 00165 char hash2[MD5LEN]; 00166 int n; 00167 char q[MD5LEN + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN]; 00168 00169 if (cipher == DIGEST_MD5_CIPHER_RC4_40) 00170 n = 5; 00171 else if (cipher == DIGEST_MD5_CIPHER_RC4_56) 00172 n = 7; 00173 else 00174 n = MD5LEN; 00175 00176 memcpy (q, hash, n); 00177 memcpy (q + n, DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING, 00178 DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN); 00179 00180 rc = gc_md5 (q, n + DERIVE_CLIENT_CONFIDENTIALITY_KEY_STRING_LEN, 00181 hash2); 00182 if (rc) 00183 return rc; 00184 00185 memcpy (kcc, hash2, MD5LEN); 00186 } 00187 00188 if (kcs) 00189 { 00190 char hash2[MD5LEN]; 00191 int n; 00192 char q[MD5LEN + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN]; 00193 00194 if (cipher == DIGEST_MD5_CIPHER_RC4_40) 00195 n = 5; 00196 else if (cipher == DIGEST_MD5_CIPHER_RC4_56) 00197 n = 7; 00198 else 00199 n = MD5LEN; 00200 00201 memcpy (q, hash, n); 00202 memcpy (q + n, DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING, 00203 DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN); 00204 00205 rc = gc_md5 (q, n + DERIVE_SERVER_CONFIDENTIALITY_KEY_STRING_LEN, 00206 hash2); 00207 if (rc) 00208 return rc; 00209 00210 memcpy (kcs, hash2, MD5LEN); 00211 } 00212 00213 for (i = 0; i < MD5LEN; i++) 00214 { 00215 a1hexhash[2 * i + 1] = HEXCHAR (hash[i]); 00216 a1hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4); 00217 } 00218 00219 /* A2 */ 00220 00221 tmplen = strlen (a2string) + strlen (digesturi); 00222 if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF) 00223 tmplen += strlen (A2_POST); 00224 00225 p = tmp = malloc (tmplen); 00226 if (tmp == NULL) 00227 return -1; 00228 00229 memcpy (p, a2string, strlen (a2string)); 00230 p += strlen (a2string); 00231 memcpy (p, digesturi, strlen (digesturi)); 00232 p += strlen (digesturi); 00233 if (qop & DIGEST_MD5_QOP_AUTH_INT || qop & DIGEST_MD5_QOP_AUTH_CONF) 00234 memcpy (p, A2_POST, strlen (A2_POST)); 00235 00236 rc = gc_md5 (tmp, tmplen, hash); 00237 free (tmp); 00238 if (rc) 00239 return rc; 00240 00241 for (i = 0; i < MD5LEN; i++) 00242 { 00243 a2hexhash[2 * i + 1] = HEXCHAR (hash[i]); 00244 a2hexhash[2 * i + 0] = HEXCHAR (hash[i] >> 4); 00245 } 00246 00247 /* response_value */ 00248 00249 sprintf (nchex, "%08lx", nc); 00250 00251 tmplen = 2 * MD5LEN + strlen (COLON) + strlen (nonce) + strlen (COLON) + 00252 strlen (nchex) + strlen (COLON) + strlen (cnonce) + strlen (COLON); 00253 if (qop & DIGEST_MD5_QOP_AUTH_CONF) 00254 tmplen += strlen (QOP_AUTH_CONF); 00255 else if (qop & DIGEST_MD5_QOP_AUTH_INT) 00256 tmplen += strlen (QOP_AUTH_INT); 00257 else if (qop & DIGEST_MD5_QOP_AUTH) 00258 tmplen += strlen (QOP_AUTH); 00259 tmplen += strlen (COLON) + 2 * MD5LEN; 00260 00261 p = tmp = malloc (tmplen); 00262 if (tmp == NULL) 00263 return -1; 00264 00265 memcpy (p, a1hexhash, 2 * MD5LEN); 00266 p += 2 * MD5LEN; 00267 memcpy (p, COLON, strlen (COLON)); 00268 p += strlen (COLON); 00269 memcpy (p, nonce, strlen (nonce)); 00270 p += strlen (nonce); 00271 memcpy (p, COLON, strlen (COLON)); 00272 p += strlen (COLON); 00273 memcpy (p, nchex, strlen (nchex)); 00274 p += strlen (nchex); 00275 memcpy (p, COLON, strlen (COLON)); 00276 p += strlen (COLON); 00277 memcpy (p, cnonce, strlen (cnonce)); 00278 p += strlen (cnonce); 00279 memcpy (p, COLON, strlen (COLON)); 00280 p += strlen (COLON); 00281 if (qop & DIGEST_MD5_QOP_AUTH_CONF) 00282 { 00283 memcpy (p, QOP_AUTH_CONF, strlen (QOP_AUTH_CONF)); 00284 p += strlen (QOP_AUTH_CONF); 00285 } 00286 else if (qop & DIGEST_MD5_QOP_AUTH_INT) 00287 { 00288 memcpy (p, QOP_AUTH_INT, strlen (QOP_AUTH_INT)); 00289 p += strlen (QOP_AUTH_INT); 00290 } 00291 else if (qop & DIGEST_MD5_QOP_AUTH) 00292 { 00293 memcpy (p, QOP_AUTH, strlen (QOP_AUTH)); 00294 p += strlen (QOP_AUTH); 00295 } 00296 memcpy (p, COLON, strlen (COLON)); 00297 p += strlen (COLON); 00298 memcpy (p, a2hexhash, 2 * MD5LEN); 00299 00300 rc = gc_md5 (tmp, tmplen, hash); 00301 free (tmp); 00302 if (rc) 00303 return rc; 00304 00305 for (i = 0; i < MD5LEN; i++) 00306 { 00307 output[2 * i + 1] = HEXCHAR (hash[i]); 00308 output[2 * i + 0] = HEXCHAR (hash[i] >> 4); 00309 } 00310 output[32] = '\0'; 00311 00312 return 0; 00313 }
1.7.6.1