Software /
code /
prosody
Comparison
util/sasl/scram.lua @ 2990:21933063dd9f
util.sasl: Abstracting out the hash function used since SCRAM is independent of it. Adding scram-{mech} authentication backend support.
author | Tobias Markmann <tm@ayena.de> |
---|---|
date | Sun, 28 Feb 2010 22:23:03 +0100 |
parent | 2648:538dd5f1810a |
child | 2993:06d06fdd190b |
comparison
equal
deleted
inserted
replaced
2717:c5dd53b38c2f | 2990:21933063dd9f |
---|---|
80 -- apply SASLprep | 80 -- apply SASLprep |
81 username = saslprep(username); | 81 username = saslprep(username); |
82 return username; | 82 return username; |
83 end | 83 end |
84 | 84 |
85 local function scram_sha_1(self, message) | 85 local function scram_gen(hash_name, H_f, HMAC_f) |
86 if not self.state then self["state"] = {} end | 86 local function scram_hash(self, message) |
87 if not self.state then self["state"] = {} end | |
87 | 88 |
88 if not self.state.name then | 89 if not self.state.name then |
89 -- we are processing client_first_message | 90 -- we are processing client_first_message |
90 local client_first_message = message; | 91 local client_first_message = message; |
91 self.state["client_first_message"] = client_first_message; | 92 self.state["client_first_message"] = client_first_message; |
92 self.state["name"] = client_first_message:match("n=(.+),r=") | 93 self.state["name"] = client_first_message:match("n=(.+),r=") |
93 self.state["clientnonce"] = client_first_message:match("r=([^,]+)") | 94 self.state["clientnonce"] = client_first_message:match("r=([^,]+)") |
94 | 95 |
95 if not self.state.name or not self.state.clientnonce then | 96 if not self.state.name or not self.state.clientnonce then |
96 return "failure", "malformed-request"; | 97 return "failure", "malformed-request"; |
97 end | 98 end |
98 | 99 |
99 self.state.name = validate_username(self.state.name); | 100 self.state.name = validate_username(self.state.name); |
100 if not self.state.name then | 101 if not self.state.name then |
101 log("debug", "Username violates either SASLprep or contains forbidden character sequences.") | 102 log("debug", "Username violates either SASLprep or contains forbidden character sequences.") |
102 return "failure", "malformed-request", "Invalid username."; | 103 return "failure", "malformed-request", "Invalid username."; |
103 end | 104 end |
104 | 105 |
105 self.state["servernonce"] = generate_uuid(); | 106 self.state["servernonce"] = generate_uuid(); |
106 self.state["salt"] = generate_uuid(); | 107 |
108 -- retreive credentials | |
109 if self.profile.plain then | |
110 password, state = self.profile.plain(self.state.name, self.realm) | |
111 if state == nil then return "failure", "not-authorized" | |
112 elseif state == false then return "failure", "account-disabled" end | |
113 | |
114 password = saslprep(password); | |
115 if not password then | |
116 log("debug", "Password violates SASLprep."); | |
117 return "failure", "not-authorized", "Invalid password." | |
118 end | |
119 self.state.salt = generate_uuid(); | |
120 self.state.iteration_count = default_i; | |
121 self.state.salted_password = Hi(HMAC_f, password, self.state.salt, default_i); | |
122 elseif self.profile["scram-"..hash_name] then | |
123 salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm); | |
124 if state == nil then return "failure", "not-authorized" | |
125 elseif state == false then return "failure", "account-disabled" end | |
126 | |
127 self.state.salted_password = salted_password; | |
128 self.state.iteration_count = iteration_count; | |
129 self.state.salt = salt | |
130 end | |
107 | 131 |
108 local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i; | 132 local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..self.state.iteration_count; |
109 self.state["server_first_message"] = server_first_message; | 133 self.state["server_first_message"] = server_first_message; |
110 return "challenge", server_first_message | 134 return "challenge", server_first_message |
111 else | 135 else |
112 if type(message) ~= "string" then return "failure", "malformed-request" end | 136 if type(message) ~= "string" then return "failure", "malformed-request" end |
113 -- we are processing client_final_message | 137 -- we are processing client_final_message |
114 local client_final_message = message; | 138 local client_final_message = message; |
115 | 139 |
116 self.state["proof"] = client_final_message:match("p=(.+)"); | 140 self.state["proof"] = client_final_message:match("p=(.+)"); |
117 self.state["nonce"] = client_final_message:match("r=(.+),p="); | 141 self.state["nonce"] = client_final_message:match("r=(.+),p="); |
118 self.state["channelbinding"] = client_final_message:match("c=(.+),r="); | 142 self.state["channelbinding"] = client_final_message:match("c=(.+),r="); |
119 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then | 143 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then |
120 return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; | 144 return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; |
121 end | 145 end |
122 | 146 |
123 local password, state; | 147 local SaltedPassword = self.state.salted_password; |
124 if self.profile.plain then | 148 local ClientKey = HMAC_f(SaltedPassword, "Client Key") |
125 password, state = self.profile.plain(self.state.name, self.realm) | 149 local ServerKey = HMAC_f(SaltedPassword, "Server Key") |
126 if state == nil then return "failure", "not-authorized" | 150 local StoredKey = H_f(ClientKey) |
127 elseif state == false then return "failure", "account-disabled" end | 151 local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") |
128 password = saslprep(password); | 152 local ClientSignature = HMAC_f(StoredKey, AuthMessage) |
129 if not password then | 153 local ClientProof = binaryXOR(ClientKey, ClientSignature) |
130 log("debug", "Password violates SASLprep."); | 154 local ServerSignature = HMAC_f(ServerKey, AuthMessage) |
131 return "failure", "not-authorized", "Invalid password." | 155 |
156 if base64.encode(ClientProof) == self.state.proof then | |
157 local server_final_message = "v="..base64.encode(ServerSignature); | |
158 self["username"] = self.state.name; | |
159 return "success", server_final_message; | |
160 else | |
161 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; | |
132 end | 162 end |
133 end | 163 end |
134 | |
135 local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i) | |
136 local ClientKey = hmac_sha1(SaltedPassword, "Client Key") | |
137 local ServerKey = hmac_sha1(SaltedPassword, "Server Key") | |
138 local StoredKey = sha1(ClientKey) | |
139 local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") | |
140 local ClientSignature = hmac_sha1(StoredKey, AuthMessage) | |
141 local ClientProof = binaryXOR(ClientKey, ClientSignature) | |
142 local ServerSignature = hmac_sha1(ServerKey, AuthMessage) | |
143 | |
144 if base64.encode(ClientProof) == self.state.proof then | |
145 local server_final_message = "v="..base64.encode(ServerSignature); | |
146 self["username"] = self.state.name; | |
147 return "success", server_final_message; | |
148 else | |
149 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; | |
150 end | |
151 end | 164 end |
165 return scram_hash; | |
152 end | 166 end |
153 | 167 |
154 function init(registerMechanism) | 168 function init(registerMechanism) |
155 registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1); | 169 local function registerSCRAMMechanism(hash_name, hash, hmac_hash) |
170 registerMechanism("SCRAM-"..hash_name, {"plain", "scram-"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash)); | |
171 end | |
172 | |
173 registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); | |
156 end | 174 end |
157 | 175 |
158 return _M; | 176 return _M; |