Software /
code /
prosody
Comparison
util/sasl/scram.lua @ 10216:a51d017e6173
util.sasl.scram: Factor out SHA-1 specific getAuthenticationDatabaseSHA1
This makes the code more generic, allowing SHA-1 to be replaced
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 13 Jan 2019 14:01:31 +0100 |
parent | 8728:41c959c5c84b |
child | 10217:60b445183d84 |
comparison
equal
deleted
inserted
replaced
10215:82abf88db13f | 10216:a51d017e6173 |
---|---|
12 -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 12 -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
13 | 13 |
14 local s_match = string.match; | 14 local s_match = string.match; |
15 local type = type | 15 local type = type |
16 local base64 = require "util.encodings".base64; | 16 local base64 = require "util.encodings".base64; |
17 local hmac_sha1 = require "util.hashes".hmac_sha1; | 17 local hashes = require "util.hashes"; |
18 local sha1 = require "util.hashes".sha1; | |
19 local Hi = require "util.hashes".scram_Hi_sha1; | |
20 local generate_uuid = require "util.uuid".generate; | 18 local generate_uuid = require "util.uuid".generate; |
21 local saslprep = require "util.encodings".stringprep.saslprep; | 19 local saslprep = require "util.encodings".stringprep.saslprep; |
22 local nodeprep = require "util.encodings".stringprep.nodeprep; | 20 local nodeprep = require "util.encodings".stringprep.nodeprep; |
23 local log = require "util.logger".init("sasl"); | 21 local log = require "util.logger".init("sasl"); |
24 local t_concat = table.concat; | 22 local t_concat = table.concat; |
97 | 95 |
98 local function hashprep(hashname) | 96 local function hashprep(hashname) |
99 return hashname:lower():gsub("-", "_"); | 97 return hashname:lower():gsub("-", "_"); |
100 end | 98 end |
101 | 99 |
102 local function getAuthenticationDatabaseSHA1(password, salt, iteration_count) | 100 local function get_scram_hasher(H, HMAC, Hi) |
103 if type(password) ~= "string" or type(salt) ~= "string" or type(iteration_count) ~= "number" then | 101 return function (password, salt, iteration_count) |
104 return false, "inappropriate argument types" | 102 if type(password) ~= "string" or type(salt) ~= "string" or type(iteration_count) ~= "number" then |
105 end | 103 return false, "inappropriate argument types" |
106 if iteration_count < 4096 then | 104 end |
107 log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") | 105 if iteration_count < 4096 then |
108 end | 106 log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") |
109 local salted_password = Hi(password, salt, iteration_count); | 107 end |
110 local stored_key = sha1(hmac_sha1(salted_password, "Client Key")) | 108 local salted_password = Hi(password, salt, iteration_count); |
111 local server_key = hmac_sha1(salted_password, "Server Key"); | 109 local stored_key = H(HMAC(salted_password, "Client Key")) |
112 return true, stored_key, server_key | 110 local server_key = HMAC(salted_password, "Server Key"); |
113 end | 111 return true, stored_key, server_key |
114 | 112 end |
115 local function scram_gen(hash_name, H_f, HMAC_f) | 113 end |
114 | |
115 local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db) | |
116 local profile_name = "scram_" .. hashprep(hash_name); | 116 local profile_name = "scram_" .. hashprep(hash_name); |
117 local function scram_hash(self, message) | 117 local function scram_hash(self, message) |
118 local support_channel_binding = false; | 118 local support_channel_binding = false; |
119 if self.profile.cb then support_channel_binding = true; end | 119 if self.profile.cb then support_channel_binding = true; end |
120 | 120 |
175 | 175 |
176 salt = generate_uuid(); | 176 salt = generate_uuid(); |
177 iteration_count = default_i; | 177 iteration_count = default_i; |
178 | 178 |
179 local succ; | 179 local succ; |
180 succ, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, iteration_count); | 180 succ, stored_key, server_key = get_auth_db(password, salt, iteration_count); |
181 if not succ then | 181 if not succ then |
182 log("error", "Generating authentication database failed. Reason: %s", stored_key); | 182 log("error", "Generating authentication database failed. Reason: %s", stored_key); |
183 return "failure", "temporary-auth-failure"; | 183 return "failure", "temporary-auth-failure"; |
184 end | 184 end |
185 elseif self.profile[profile_name] then | 185 elseif self.profile[profile_name] then |
245 end | 245 end |
246 end | 246 end |
247 return scram_hash; | 247 return scram_hash; |
248 end | 248 end |
249 | 249 |
250 local auth_db_getters = {} | |
250 local function init(registerMechanism) | 251 local function init(registerMechanism) |
251 local function registerSCRAMMechanism(hash_name, hash, hmac_hash) | 252 local function registerSCRAMMechanism(hash_name, hash, hmac_hash, pbkdf2) |
253 local get_auth_db = get_scram_hasher(hash, hmac_hash, pbkdf2); | |
254 auth_db_getters[hash_name] = get_auth_db; | |
252 registerMechanism("SCRAM-"..hash_name, | 255 registerMechanism("SCRAM-"..hash_name, |
253 {"plain", "scram_"..(hashprep(hash_name))}, | 256 {"plain", "scram_"..(hashprep(hash_name))}, |
254 scram_gen(hash_name:lower(), hash, hmac_hash)); | 257 scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db)); |
255 | 258 |
256 -- register channel binding equivalent | 259 -- register channel binding equivalent |
257 registerMechanism("SCRAM-"..hash_name.."-PLUS", | 260 registerMechanism("SCRAM-"..hash_name.."-PLUS", |
258 {"plain", "scram_"..(hashprep(hash_name))}, | 261 {"plain", "scram_"..(hashprep(hash_name))}, |
259 scram_gen(hash_name:lower(), hash, hmac_hash), {"tls-unique"}); | 262 scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db), {"tls-unique"}); |
260 end | 263 end |
261 | 264 |
262 registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); | 265 registerSCRAMMechanism("SHA-1", hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1); |
263 end | 266 end |
264 | 267 |
265 return { | 268 return { |
266 getAuthenticationDatabaseSHA1 = getAuthenticationDatabaseSHA1; | 269 get_hash = get_scram_hasher; |
270 hashers = auth_db_getters; | |
271 getAuthenticationDatabaseSHA1 = get_scram_hasher(hashes.sha1, hashes.sha256, hashes.pbkdf2_hmac_sha1); | |
267 init = init; | 272 init = init; |
268 } | 273 } |