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 }