Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / cred / _digest.py
1 # -*- test-case-name: twisted.test.test_digestauth -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Calculations for HTTP Digest authentication.
7
8 @see: U{http://www.faqs.org/rfcs/rfc2617.html}
9 """
10
11 from twisted.python.hashlib import md5, sha1
12
13
14
15 # The digest math
16
17 algorithms = {
18     'md5': md5,
19
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
27     'md5-sess': md5,
28
29     'sha': sha1,
30 }
31
32 # DigestCalcHA1
33 def calcHA1(pszAlg, pszUserName, pszRealm, pszPassword, pszNonce, pszCNonce,
34             preHA1=None):
35     """
36     Compute H(A1) from RFC 2617.
37
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
45
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.
49     """
50
51     if (preHA1 and (pszUserName or pszRealm or pszPassword)):
52         raise TypeError(("preHA1 is incompatible with the pszUserName, "
53                          "pszRealm, and pszPassword arguments"))
54
55     if preHA1 is None:
56         # We need to calculate the HA1 from the username:realm:password
57         m = algorithms[pszAlg]()
58         m.update(pszUserName)
59         m.update(":")
60         m.update(pszRealm)
61         m.update(":")
62         m.update(pszPassword)
63         HA1 = m.digest()
64     else:
65         # We were given a username:realm:password
66         HA1 = preHA1.decode('hex')
67
68     if pszAlg == "md5-sess":
69         m = algorithms[pszAlg]()
70         m.update(HA1)
71         m.update(":")
72         m.update(pszNonce)
73         m.update(":")
74         m.update(pszCNonce)
75         HA1 = m.digest()
76
77     return HA1.encode('hex')
78
79
80 def calcHA2(algo, pszMethod, pszDigestUri, pszQop, pszHEntity):
81     """
82     Compute H(A2) from RFC 2617.
83
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
90         not C{'auth-int'}.
91     @return: The hash of the A2 value for the calculation of the response
92         digest.
93     """
94     m = algorithms[algo]()
95     m.update(pszMethod)
96     m.update(":")
97     m.update(pszDigestUri)
98     if pszQop == "auth-int":
99         m.update(":")
100         m.update(pszHEntity)
101     return m.digest().encode('hex')
102
103
104 def calcResponse(HA1, HA2, algo, pszNonce, pszNonceCount, pszCNonce, pszQop):
105     """
106     Compute the digest for the given parameters.
107
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.
114     """
115     m = algorithms[algo]()
116     m.update(HA1)
117     m.update(":")
118     m.update(pszNonce)
119     m.update(":")
120     if pszNonceCount and pszCNonce:
121         m.update(pszNonceCount)
122         m.update(":")
123         m.update(pszCNonce)
124         m.update(":")
125         m.update(pszQop)
126         m.update(":")
127     m.update(HA2)
128     respHash = m.digest().encode('hex')
129     return respHash