util/sasl/scram.lua
changeset 2196 614c839c30c5
parent 2194 41d42d253a1d
child 2197 1509cabb8321
equal deleted inserted replaced
2195:8788c995fcbd 2196:614c839c30c5
    15 
    15 
    16 local base64 = require "util.encodings".base64;
    16 local base64 = require "util.encodings".base64;
    17 local xor = require "bit".bxor
    17 local xor = require "bit".bxor
    18 local hmac_sha1 = require "util.hmac".sha1;
    18 local hmac_sha1 = require "util.hmac".sha1;
    19 local sha1 = require "util.hashes".sha1;
    19 local sha1 = require "util.hashes".sha1;
       
    20 local generate_uuid = require "util.uuid".generate;
    20 
    21 
    21 module "plain"
    22 module "plain"
    22 
    23 
    23 --=========================
    24 --=========================
    24 --SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10
    25 --SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10
    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);