gsasl  1.8.0
digesthmac.c
Go to the documentation of this file.
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 }