1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 from caldavclientlibrary.protocol.http.authentication.authenticator import Authenticator
18 from caldavclientlibrary.protocol.http.util import parsetoken
19 from caldavclientlibrary.protocol.http.definitions import headers
20 from StringIO import StringIO
21 import hashlib
22
23 -class Digest(Authenticator):
24
25 - def __init__(self, user, pswd, www_authenticate):
32
33 - def setDetails(self, user, pswd, www_authenticate):
37
39
40 self.generateResponse(request)
41
42
43 os = StringIO()
44 os.write("Digest username=\"%s\"," % (self.fields['username'],))
45 os.write(" realm=\"%s\"," % (self.fields['realm'],))
46 os.write(" nonce=\"%s\"," % (self.fields['nonce'],))
47 os.write(" uri=\"%s\"," % (request.getURL(),))
48 if "qop" in self.fields:
49 os.write(" qop=auth,")
50 os.write(" nc=\"%s\"" % (self.fields['nc'],))
51 os.write(" cnonce=\"%s\"" % (self.fields['cnonce'],))
52 os.write(" response=\"%s\"" % (self.response,))
53
54 if "algorithm" in self.fields:
55 os.write(", algorithm=\"%s\"" % (self.fields['algorithm'],))
56 if "opaque" in self.fields:
57 os.write(", opaque=\"%s\"" % (self.fields['opaque'],))
58
59 hdrs.append((headers.Authorization, os.getvalue()))
60
62 for hdr in hdrs:
63
64
65 hdr = hdr.strip()
66
67
68 if hdr[:7].lower() != "digest ":
69 continue
70 else:
71 hdr = hdr[7:]
72
73
74 while True:
75 name, hdr = parsetoken(hdr, " \t=")
76
77 if not name or not hdr:
78 return
79 name = name.lower()
80
81 value, hdr = parsetoken(hdr, ", ")
82 if not value:
83 return
84
85 if name in ("realm", "domain", "nonce", "opaque", "algorithm", "qop"):
86 self.fields[name] = value
87
88 elif name == "stale":
89 self.stale = (value.lower() != "false")
90
91 else:
92
93 pass
94
95
96 hdr = hdr.strip()
97
98 break
99
100 algorithms = {
101 'md5': hashlib.md5,
102 'md5-sess': hashlib.md5,
103 'sha': hashlib.sha1,
104 }
105
106
107 @staticmethod
108 - def calcHA1(
109 pszAlg,
110 pszUserName,
111 pszRealm,
112 pszPassword,
113 pszNonce,
114 pszCNonce,
115 preHA1=None
116 ):
117 """
118 @param pszAlg: The name of the algorithm to use to calculate the Digest.
119 Currently supported are md5 md5-sess and sha.
120
121 @param pszUserName: The username
122 @param pszRealm: The realm
123 @param pszPassword: The password
124 @param pszNonce: The nonce
125 @param pszCNonce: The cnonce
126
127 @param preHA1: If available this is a str containing a previously
128 calculated HA1 as a hex string. If this is given then the values for
129 pszUserName, pszRealm, and pszPassword are ignored.
130 """
131
132 if (preHA1 and (pszUserName or pszRealm or pszPassword)):
133 raise TypeError(("preHA1 is incompatible with the pszUserName, "
134 "pszRealm, and pszPassword arguments"))
135
136 if preHA1 is None:
137
138 m = Digest.algorithms[pszAlg]()
139 m.update(pszUserName)
140 m.update(":")
141 m.update(pszRealm)
142 m.update(":")
143 m.update(pszPassword)
144 HA1 = m.digest()
145 else:
146
147 HA1 = preHA1.decode('hex')
148
149 if pszAlg == "md5-sess":
150 m = Digest.algorithms[pszAlg]()
151 m.update(HA1)
152 m.update(":")
153 m.update(pszNonce)
154 m.update(":")
155 m.update(pszCNonce)
156 HA1 = m.digest()
157
158 return HA1.encode('hex')
159
160
161 @staticmethod
162 - def calcResponse(
163 HA1,
164 algo,
165 pszNonce,
166 pszNonceCount,
167 pszCNonce,
168 pszQop,
169 pszMethod,
170 pszDigestUri,
171 pszHEntity,
172 ):
173 m = Digest.algorithms[algo]()
174 m.update(pszMethod)
175 m.update(":")
176 m.update(pszDigestUri)
177 if pszQop == "auth-int":
178 m.update(":")
179 m.update(pszHEntity)
180 HA2 = m.digest().encode('hex')
181
182 m = Digest.algorithms[algo]()
183 m.update(HA1)
184 m.update(":")
185 m.update(pszNonce)
186 m.update(":")
187 if pszNonceCount and pszCNonce:
188 m.update(pszNonceCount)
189 m.update(":")
190 m.update(pszCNonce)
191 m.update(":")
192 m.update(pszQop)
193 m.update(":")
194 m.update(HA2)
195 respHash = m.digest().encode('hex')
196 return respHash
197
199 self.response = Digest.calcResponse(
200 Digest.calcHA1(
201 self.fields.get("algorithm", "md5"),
202 self.fields.get("username", ""),
203 self.fields.get("realm", ""),
204 self.fields.get("password", ""),
205 self.fields.get("nonce", ""),
206 self.fields.get("cnonce", ""),
207 ),
208 self.fields.get("algorithm", "md5"),
209 self.fields.get("nonce", ""),
210 self.fields.get("nc", ""),
211 self.fields.get("cnonce", ""),
212 self.fields.get("qop", ""),
213 request.method,
214 request.url,
215 None,
216 )
217