@@ -19,6 +19,8 @@ class DigestMD5(mech.Mechanism):
19
19
def __init__ (self , auth ):
20
20
self .auth = auth
21
21
22
+ state = mech .AuthState
23
+
22
24
## Server
23
25
## 1. Issue challenge.
24
26
## 2. Verify challenge response is correct; reply with rspauth.
@@ -27,56 +29,56 @@ def __init__(self, auth):
27
29
def challenge (self ):
28
30
## Issue a challenge; continue by verifying the client's
29
31
## response.
30
- return (self .verify_challenge , self .write (self .CHALLENGE , {
32
+ return self . state (self .verify , None , self .write (self .CHALLENGE , {
31
33
'realm' : self .auth .realm (),
32
34
'nonce' : self .make_nonce (),
33
35
'charset' : 'utf-8' ,
34
36
'algorithm' : 'md5-sess'
35
37
}))
36
38
37
- def verify_challenge (self , data ):
39
+ def verify (self , entity , data ):
38
40
try :
39
41
response = dict (rfc .data (self .RESPOND , data ))
40
42
except rfc .ReadError as exc :
41
- return (False , None )
43
+ return self . state (False , entity , None )
42
44
43
45
## If the nonce count is not one, the client is trying to use
44
46
## "subsequent authentication", but this is usupported.
45
47
if response .get ('nc' ) != 1 :
46
- return (False , '' )
48
+ return self . state (False , entity , '' )
47
49
48
50
## Confirm the digest-uri.
49
51
if response .get ('digest-uri' ) != self .make_digest_uri ():
50
- return (False , '' )
52
+ return self . state (False , entity , '' )
51
53
52
54
## Make sure the username exists, get the stored password.
53
- username = response .get ('username' )
54
- passwd = username and self .auth .get_password (username )
55
+ entity = response .get ('username' )
56
+ passwd = entity and self .auth .get_password (entity )
55
57
if not passwd :
56
- return (False , '' )
58
+ return self . state (False , entity , '' )
57
59
58
60
## The password must be stored in a format compatible with
59
61
## DigestMD5Password. Convert it to a binary representation
60
62
## and generate the response hashes.
61
63
try :
62
- uh = DigestMD5Password .digest (self .auth , username , passwd )
64
+ uh = DigestMD5Password .digest (self .auth , entity , passwd )
63
65
(expect , rspauth ) = self .make_response (response , uh )
64
66
except auth .PasswordError as exc :
65
- return (False , '' )
67
+ return self . state (False , entity , '' )
66
68
67
69
## Verify that the response hashes match.
68
70
if expect != response .get ('response' ):
69
- return (False , '' )
71
+ return self . state (False , entity , '' )
70
72
71
73
## Return the rspauth hash; wait for acknowledgement.
72
- return (self .receive_ack , self .write (self .VERIFY , {
74
+ return self . state (self .finish , entity , self .write (self .VERIFY , {
73
75
'rspauth' : rspauth
74
76
}))
75
77
76
- def receive_ack (self , data ):
78
+ def finish (self , entity , data ):
77
79
## The client has acknowledges the rspauth sent;
78
80
## authentication was successful.
79
- return (True , '' )
81
+ return self . state (True , entity , '' )
80
82
81
83
## Client
82
84
## 1. Respond to server challenge.
@@ -87,15 +89,16 @@ def respond(self, data):
87
89
try :
88
90
challenge = dict (rfc .data (self .CHALLENGE , data ))
89
91
except rfc .ReadError :
90
- return (False , None )
92
+ return self . state (False , None , None )
91
93
92
94
enc = self .encoding (challenge )
93
95
zid = self .auth .authorization_id ()
96
+ cid = unicode (self .auth .username ()).encode (enc )
94
97
95
98
## Derive response parameters from the challenge and from the
96
99
## client environment.
97
100
params = {
98
- 'username' : unicode ( self . auth . username ()). encode ( enc ) ,
101
+ 'username' : cid ,
99
102
'realm' : challenge .get ('realm' , u'' ),
100
103
'nonce' : challenge ['nonce' ],
101
104
'cnonce' : self .make_nonce (),
@@ -112,23 +115,25 @@ def respond(self, data):
112
115
113
116
## Generate the response; continue by acknowledging the
114
117
## server's response.
115
- ack = lambda d : self .acknowledge (expect , d )
116
- return (ack , self .write (self .RESPOND , params ))
118
+ ack = lambda * a : self .acknowledge (expect , * a )
119
+ return self . state (ack , zid or cid , self .write (self .RESPOND , params ))
117
120
118
- def acknowledge (self , expect , data ):
121
+ def acknowledge (self , expect , entity , data ):
119
122
try :
120
123
verify = dict (rfc .data (self .VERIFY , data ))
121
124
except rfc .ReadError :
122
- return (False , None )
125
+ return self . state (False , entity , None )
123
126
124
127
## If the rspauth matches the expected value, authentication
125
128
## was successful. The server expects an empty reply that
126
129
## confirms acknowledgement.
127
- return (verify .get ('rspauth' ) == expect and self .ack_accepted , '' )
130
+ if verify .get ('rspauth' ) == expect :
131
+ return self .state (self .accepted , entity , '' )
132
+ return self .state (False , entity , None )
128
133
129
- def ack_accepted (self , data ):
134
+ def accepted (self , entity , data ):
130
135
assert data == ''
131
- return (True , None )
136
+ return self . state (True , entity , None )
132
137
133
138
## Grammars
134
139
@@ -181,7 +186,7 @@ def make_digest_uri(self):
181
186
service = auth .service_name ()
182
187
return '%s/%s%s' % (
183
188
auth .service_type (),
184
- auth .host (),
189
+ auth .realm (),
185
190
('/%s' % service if service else u'' )
186
191
)
187
192
@@ -247,7 +252,7 @@ def user_hash(user, realm, passwd, encoding='utf-8'):
247
252
realm = iso_8859_1 (realm )
248
253
passwd = iso_8859_1 (passwd )
249
254
250
- return colons (md5 , user , passwd , realm )
255
+ return colons (md5 , user , realm , passwd )
251
256
252
257
def a1_hash (uh , nonce , cnonce , authzid ):
253
258
"""A1 hash (see RFC-2831 page 10). The uh parameter is the result
0 commit comments