1 # -*- test-case-name: twisted.test.test_digestauth -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 Calculations for HTTP Digest authentication.
8 @see: U{http://www.faqs.org/rfcs/rfc2617.html}
11 from twisted.python.hashlib import md5, sha1
20 # md5-sess is more complicated than just another algorithm. It requires
21 # H(A1) state to be remembered from the first WWW-Authenticate challenge
22 # issued and re-used to process any Authorization header in response to
23 # that WWW-Authenticate challenge. It is *not* correct to simply
24 # recalculate H(A1) each time an Authorization header is received. Read
25 # RFC 2617, section 3.2.2.2 and do not try to make DigestCredentialFactory
26 # support this unless you completely understand it. -exarkun
33 def calcHA1(pszAlg, pszUserName, pszRealm, pszPassword, pszNonce, pszCNonce,
36 Compute H(A1) from RFC 2617.
38 @param pszAlg: The name of the algorithm to use to calculate the digest.
39 Currently supported are md5, md5-sess, and sha.
40 @param pszUserName: The username
41 @param pszRealm: The realm
42 @param pszPassword: The password
43 @param pszNonce: The nonce
44 @param pszCNonce: The cnonce
46 @param preHA1: If available this is a str containing a previously
47 calculated H(A1) as a hex string. If this is given then the values for
48 pszUserName, pszRealm, and pszPassword must be C{None} and are ignored.
51 if (preHA1 and (pszUserName or pszRealm or pszPassword)):
52 raise TypeError(("preHA1 is incompatible with the pszUserName, "
53 "pszRealm, and pszPassword arguments"))
56 # We need to calculate the HA1 from the username:realm:password
57 m = algorithms[pszAlg]()
65 # We were given a username:realm:password
66 HA1 = preHA1.decode('hex')
68 if pszAlg == "md5-sess":
69 m = algorithms[pszAlg]()
77 return HA1.encode('hex')
80 def calcHA2(algo, pszMethod, pszDigestUri, pszQop, pszHEntity):
82 Compute H(A2) from RFC 2617.
84 @param pszAlg: The name of the algorithm to use to calculate the digest.
85 Currently supported are md5, md5-sess, and sha.
86 @param pszMethod: The request method.
87 @param pszDigestUri: The request URI.
88 @param pszQop: The Quality-of-Protection value.
89 @param pszHEntity: The hash of the entity body or C{None} if C{pszQop} is
91 @return: The hash of the A2 value for the calculation of the response
94 m = algorithms[algo]()
97 m.update(pszDigestUri)
98 if pszQop == "auth-int":
101 return m.digest().encode('hex')
104 def calcResponse(HA1, HA2, algo, pszNonce, pszNonceCount, pszCNonce, pszQop):
106 Compute the digest for the given parameters.
108 @param HA1: The H(A1) value, as computed by L{calcHA1}.
109 @param HA2: The H(A2) value, as computed by L{calcHA2}.
110 @param pszNonce: The challenge nonce.
111 @param pszNonceCount: The (client) nonce count value for this response.
112 @param pszCNonce: The client nonce.
113 @param pszQop: The Quality-of-Protection value.
115 m = algorithms[algo]()
120 if pszNonceCount and pszCNonce:
121 m.update(pszNonceCount)
128 respHash = m.digest().encode('hex')