diff options
author | Tobias Markmann <tm@ayena.de> | 2009-11-17 11:03:54 +0100 |
---|---|---|
committer | Tobias Markmann <tm@ayena.de> | 2009-11-17 11:03:54 +0100 |
commit | 1fa16fc88c2492df997fa5471eb282e977602c25 (patch) | |
tree | 4916d8b97ea233678da6cecfdde0d30ec984a384 | |
parent | edd37a1e348c6f989351cc9b682bbbae4f6a2a1c (diff) | |
download | prosody-1fa16fc88c2492df997fa5471eb282e977602c25.tar.gz prosody-1fa16fc88c2492df997fa5471eb282e977602c25.zip |
Completed SCRAM-SHA-1 implementation to a ready-to-test state.
-rw-r--r-- | util/sasl/scram.lua | 44 |
1 files changed, 41 insertions, 3 deletions
diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index b7507f3e..9362cca7 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -17,6 +17,7 @@ local base64 = require "util.encodings".base64; local xor = require "bit".bxor local hmac_sha1 = require "util.hmac".sha1; local sha1 = require "util.hashes".sha1; +local generate_uuid = require "util.uuid".generate; module "plain" @@ -71,15 +72,52 @@ local function scram_sha_1(self, message) if not self.state.name then -- we are processing client_first_message - self.state["name"] = string.match(client_first_message, "n=(.+),r=") - self.state["clientnonce"] = string.match(client_first_message, "r=([^,]+)") + local client_first_message = message; + self.state["name"] = client_first_message:match("n=(.+),r=") + self.state["clientnonce"] = client_first_message:match("r=([^,]+)") self.state.name = validate_username(self.state.name); - if not self.state.name then + if not self.state.name or not self.state.clientnonce then return "failure", "malformed-request"; end + self.state["servernonce"] = generate_uuid(); + self.state["salt"] = generate_uuid(); + + local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i; + return "challenge", server_first_message else -- we are processing client_final_message + local client_final_message = message; + + self.state["proof"] = client_final_message:match("p=(.+)"); + self.state["nonce"] = client_final_message:match("r=(.+),p="); + self.state["channelbinding"] = client_final_message:match("c=(.+),r="); + if not self.state.proof or not self.state.nonce or not self.state.channelbinding then + return "failure", "malformed-request"; + end + + local password; + if self.profile.plain then + password, state = self.profile.plain(self.state.name, self.realm) + if state == nil then return "failure", "not-authorized" + elseif state == false then return "failure", "account-disabled" end + end + + local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i) + local ClientKey = hmac_sha1(SaltedPassword, "Client Key") + local ServerKey = hmac_sha1(SaltedPassword, "Server Key") + local StoredKey = sha1(ClientKey) + local AuthMessage = "n=" .. s_match(client_first_message,"n=(.+)") .. "," .. server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") + local ClientSignature = hmac_sha1(StoredKey, AuthMessage) + local ClientProof = binaryXOR(ClientKey, ClientSignature) + local ServerSignature = hmac_sha1(ServerKey, AuthMessage) + + if base64.encode(ClientProof) == self.state.proof then + local server_final_message = "v="..base64.encode(ServerSignature); + return "success", server_final_message; + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; + end end end |