1/*
2 *  OpenVPN -- An application to securely tunnel IP networks
3 *             over a single TCP/UDP port, with support for SSL/TLS-based
4 *             session authentication and key exchange,
5 *             packet encryption, packet authentication, and
6 *             packet compression.
7 *
8 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 2
12 *  as published by the Free Software Foundation.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program (see the file COPYING included with this
21 *  distribution); if not, write to the Free Software Foundation, Inc.,
22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#elif defined(_MSC_VER)
28#include "config-msvc.h"
29#endif
30
31#include "syshead.h"
32
33#if PROXY_DIGEST_AUTH
34
35#include "crypto.h"
36#include "httpdigest.h"
37
38static void
39CvtHex(
40       IN HASH Bin,
41       OUT HASHHEX Hex
42       )
43{
44  unsigned short i;
45  unsigned char j;
46
47  for (i = 0; i < HASHLEN; i++) {
48    j = (Bin[i] >> 4) & 0xf;
49    if (j <= 9)
50      Hex[i*2] = (j + '0');
51    else
52      Hex[i*2] = (j + 'a' - 10);
53    j = Bin[i] & 0xf;
54    if (j <= 9)
55      Hex[i*2+1] = (j + '0');
56    else
57      Hex[i*2+1] = (j + 'a' - 10);
58  };
59  Hex[HASHHEXLEN] = '\0';
60};
61
62/* calculate H(A1) as per spec */
63void
64DigestCalcHA1(
65	      IN char * pszAlg,
66	      IN char * pszUserName,
67	      IN char * pszRealm,
68	      IN char * pszPassword,
69	      IN char * pszNonce,
70	      IN char * pszCNonce,
71	      OUT HASHHEX SessionKey
72	      )
73{
74  HASH HA1;
75  md_ctx_t md5_ctx;
76  const md_kt_t *md5_kt = md_kt_get("MD5");
77
78  md_ctx_init(&md5_ctx, md5_kt);
79  md_ctx_update(&md5_ctx, pszUserName, strlen(pszUserName));
80  md_ctx_update(&md5_ctx, ":", 1);
81  md_ctx_update(&md5_ctx, pszRealm, strlen(pszRealm));
82  md_ctx_update(&md5_ctx, ":", 1);
83  md_ctx_update(&md5_ctx, pszPassword, strlen(pszPassword));
84  md_ctx_final(&md5_ctx, HA1);
85  if (pszAlg && strcasecmp(pszAlg, "md5-sess") == 0)
86    {
87      md_ctx_init(&md5_ctx, md5_kt);
88      md_ctx_update(&md5_ctx, HA1, HASHLEN);
89      md_ctx_update(&md5_ctx, ":", 1);
90      md_ctx_update(&md5_ctx, pszNonce, strlen(pszNonce));
91      md_ctx_update(&md5_ctx, ":", 1);
92      md_ctx_update(&md5_ctx, pszCNonce, strlen(pszCNonce));
93      md_ctx_final(&md5_ctx, HA1);
94    };
95  md_ctx_cleanup(&md5_ctx);
96  CvtHex(HA1, SessionKey);
97}
98
99/* calculate request-digest/response-digest as per HTTP Digest spec */
100void
101DigestCalcResponse(
102		   IN HASHHEX HA1,           /* H(A1) */
103		   IN char * pszNonce,       /* nonce from server */
104		   IN char * pszNonceCount,  /* 8 hex digits */
105		   IN char * pszCNonce,      /* client nonce */
106		   IN char * pszQop,         /* qop-value: "", "auth", "auth-int" */
107		   IN char * pszMethod,      /* method from the request */
108		   IN char * pszDigestUri,   /* requested URL */
109		   IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" */
110		   OUT HASHHEX Response      /* request-digest or response-digest */
111		   )
112{
113  HASH HA2;
114  HASH RespHash;
115  HASHHEX HA2Hex;
116
117  md_ctx_t md5_ctx;
118  const md_kt_t *md5_kt = md_kt_get("MD5");
119
120  /* calculate H(A2) */
121  md_ctx_init(&md5_ctx, md5_kt);
122  md_ctx_update(&md5_ctx, pszMethod, strlen(pszMethod));
123  md_ctx_update(&md5_ctx, ":", 1);
124  md_ctx_update(&md5_ctx, pszDigestUri, strlen(pszDigestUri));
125  if (strcasecmp(pszQop, "auth-int") == 0)
126    {
127      md_ctx_update(&md5_ctx, ":", 1);
128      md_ctx_update(&md5_ctx, HEntity, HASHHEXLEN);
129    };
130  md_ctx_final(&md5_ctx, HA2);
131  CvtHex(HA2, HA2Hex);
132
133  /* calculate response */
134  md_ctx_init(&md5_ctx, md5_kt);
135  md_ctx_update(&md5_ctx, HA1, HASHHEXLEN);
136  md_ctx_update(&md5_ctx, ":", 1);
137  md_ctx_update(&md5_ctx, pszNonce, strlen(pszNonce));
138  md_ctx_update(&md5_ctx, ":", 1);
139  if (*pszQop)
140    {
141      md_ctx_update(&md5_ctx, pszNonceCount, strlen(pszNonceCount));
142      md_ctx_update(&md5_ctx, ":", 1);
143      md_ctx_update(&md5_ctx, pszCNonce, strlen(pszCNonce));
144      md_ctx_update(&md5_ctx, ":", 1);
145      md_ctx_update(&md5_ctx, pszQop, strlen(pszQop));
146      md_ctx_update(&md5_ctx, ":", 1);
147    };
148  md_ctx_update(&md5_ctx, HA2Hex, HASHHEXLEN);
149  md_ctx_final(&md5_ctx, RespHash);
150  md_ctx_cleanup(&md5_ctx);
151  CvtHex(RespHash, Response);
152}
153
154#endif
155