69 local function scram_sha_1(self, message) |
70 local function scram_sha_1(self, message) |
70 if not self.state then self["state"] = {} end |
71 if not self.state then self["state"] = {} end |
71 |
72 |
72 if not self.state.name then |
73 if not self.state.name then |
73 -- we are processing client_first_message |
74 -- we are processing client_first_message |
74 self.state["name"] = string.match(client_first_message, "n=(.+),r=") |
75 local client_first_message = message; |
75 self.state["clientnonce"] = string.match(client_first_message, "r=([^,]+)") |
76 self.state["name"] = client_first_message:match("n=(.+),r=") |
|
77 self.state["clientnonce"] = client_first_message:match("r=([^,]+)") |
76 |
78 |
77 self.state.name = validate_username(self.state.name); |
79 self.state.name = validate_username(self.state.name); |
78 if not self.state.name then |
80 if not self.state.name or not self.state.clientnonce then |
79 return "failure", "malformed-request"; |
81 return "failure", "malformed-request"; |
80 end |
82 end |
|
83 self.state["servernonce"] = generate_uuid(); |
|
84 self.state["salt"] = generate_uuid(); |
|
85 |
|
86 local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i; |
|
87 return "challenge", server_first_message |
81 else |
88 else |
82 -- we are processing client_final_message |
89 -- we are processing client_final_message |
|
90 local client_final_message = message; |
|
91 |
|
92 self.state["proof"] = client_final_message:match("p=(.+)"); |
|
93 self.state["nonce"] = client_final_message:match("r=(.+),p="); |
|
94 self.state["channelbinding"] = client_final_message:match("c=(.+),r="); |
|
95 if not self.state.proof or not self.state.nonce or not self.state.channelbinding then |
|
96 return "failure", "malformed-request"; |
|
97 end |
|
98 |
|
99 local password; |
|
100 if self.profile.plain then |
|
101 password, state = self.profile.plain(self.state.name, self.realm) |
|
102 if state == nil then return "failure", "not-authorized" |
|
103 elseif state == false then return "failure", "account-disabled" end |
|
104 end |
|
105 |
|
106 local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i) |
|
107 local ClientKey = hmac_sha1(SaltedPassword, "Client Key") |
|
108 local ServerKey = hmac_sha1(SaltedPassword, "Server Key") |
|
109 local StoredKey = sha1(ClientKey) |
|
110 local AuthMessage = "n=" .. s_match(client_first_message,"n=(.+)") .. "," .. server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") |
|
111 local ClientSignature = hmac_sha1(StoredKey, AuthMessage) |
|
112 local ClientProof = binaryXOR(ClientKey, ClientSignature) |
|
113 local ServerSignature = hmac_sha1(ServerKey, AuthMessage) |
|
114 |
|
115 if base64.encode(ClientProof) == self.state.proof then |
|
116 local server_final_message = "v="..base64.encode(ServerSignature); |
|
117 return "success", server_final_message; |
|
118 else |
|
119 return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; |
|
120 end |
83 end |
121 end |
84 end |
122 end |
85 |
123 |
86 function init(registerMechanism) |
124 function init(registerMechanism) |
87 registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1); |
125 registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1); |