Annotate

util/sasl/scram.lua @ 498:50d0bd035bb7

util.sasl.oauthbearer: Don't send authzid It's not needed and not recommended in XMPP unless we want to act as someone other than who we authenticate as. We find out the JID during resource binding.
author Kim Alvefur <zash@zash.se>
date Fri, 23 Jun 2023 12:09:49 +0200
parent 490:6b2f31da9610
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
1
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
2 local base64, unbase64 = require "mime".b64, require"mime".unb64;
490
6b2f31da9610 Update for new Prosody module namespace
Kim Alvefur <zash@zash.se>
parents: 455
diff changeset
3 local hashes = require"prosody.util.hashes";
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
4 local bit = require"bit";
490
6b2f31da9610 Update for new Prosody module namespace
Kim Alvefur <zash@zash.se>
parents: 455
diff changeset
5 local random = require"prosody.util.random";
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
6
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
7 local tonumber = tonumber;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
8 local char, byte = string.char, string.byte;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
9 local gsub = string.gsub;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
10 local xor = bit.bxor;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
11
358
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
12 local function XOR(a, b)
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
13 return (gsub(a, "()(.)", function(i, c)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
14 return char(xor(byte(c), byte(b, i)))
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
15 end));
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
16 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
17
390
7f535a1d5827 util.sasl.scram: Use the new util.hashes and util.random
Kim Alvefur <zash@zash.se>
parents: 365
diff changeset
18 local H, HMAC = hashes.sha1, hashes.hmac_sha1;
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
19
358
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
20 local function Hi(str, salt, i)
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
21 local U = HMAC(str, salt .. "\0\0\0\1");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
22 local ret = U;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
23 for _ = 2, i do
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
24 U = HMAC(str, U);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
25 ret = XOR(ret, U);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
26 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
27 return ret;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
28 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
29
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
30 local function Normalize(str)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
31 return str; -- TODO
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
32 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
33
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
34 local function value_safe(str)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
35 return (gsub(str, "[,=]", { [","] = "=2C", ["="] = "=3D" }));
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
36 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
37
453
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
38 local function cb(conn)
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
39 if conn:ssl() then
454
9f27a2075e9e util.sasl.scram: Disable 'tls-unique' channel binding on TLS 1.3
Kim Alvefur <zash@zash.se>
parents: 453
diff changeset
40 local sock = conn:socket();
9f27a2075e9e util.sasl.scram: Disable 'tls-unique' channel binding on TLS 1.3
Kim Alvefur <zash@zash.se>
parents: 453
diff changeset
41 if sock.info and sock:info().protocol == "TLSv1.3" then
455
753d6983dc45 util.sasl.scram: Add support for 'tls-exporter' channel binding
Kim Alvefur <zash@zash.se>
parents: 454
diff changeset
42 if sock.exportkeyingmaterial then
753d6983dc45 util.sasl.scram: Add support for 'tls-exporter' channel binding
Kim Alvefur <zash@zash.se>
parents: 454
diff changeset
43 return "p=tls-exporter", sock:exportkeyingmaterial("EXPORTER-Channel-Binding", 32, "");
753d6983dc45 util.sasl.scram: Add support for 'tls-exporter' channel binding
Kim Alvefur <zash@zash.se>
parents: 454
diff changeset
44 end
454
9f27a2075e9e util.sasl.scram: Disable 'tls-unique' channel binding on TLS 1.3
Kim Alvefur <zash@zash.se>
parents: 453
diff changeset
45 elseif sock.getfinished then
453
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
46 return "p=tls-unique", sock:getfinished();
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
47 end
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
48 end
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
49 end
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
50
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
51 local function scram(stream, name)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
52 local username = "n=" .. value_safe(stream.username);
390
7f535a1d5827 util.sasl.scram: Use the new util.hashes and util.random
Kim Alvefur <zash@zash.se>
parents: 365
diff changeset
53 local c_nonce = base64(random.bytes(15));
362
d8c3e94d765d util.sasl.scram: Correctly verify that the server added its own nonce
Kim Alvefur <zash@zash.se>
parents: 359
diff changeset
54 local our_nonce = "r=" .. c_nonce;
d8c3e94d765d util.sasl.scram: Correctly verify that the server added its own nonce
Kim Alvefur <zash@zash.se>
parents: 359
diff changeset
55 local client_first_message_bare = username .. "," .. our_nonce;
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
56 local cbind_data = "";
453
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
57 local gs2_cbind_flag = "n";
356
f95e797895ee SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents: 355
diff changeset
58 if name == "SCRAM-SHA-1-PLUS" then
453
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
59 gs2_cbind_flag, cbind_data = cb(stream.conn);
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
60 elseif cb(stream.conn) then
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
61 gs2_cbind_flag = "y";
356
f95e797895ee SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents: 355
diff changeset
62 end
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
63 local gs2_header = gs2_cbind_flag .. ",,";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
64 local client_first_message = gs2_header .. client_first_message_bare;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
65 local cont, server_first_message = coroutine.yield(client_first_message);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
66 if cont ~= "challenge" then return false end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
67
362
d8c3e94d765d util.sasl.scram: Correctly verify that the server added its own nonce
Kim Alvefur <zash@zash.se>
parents: 359
diff changeset
68 local nonce, salt, iteration_count = server_first_message:match("(r=[^,]+),s=([^,]*),i=(%d+)");
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
69 local i = tonumber(iteration_count);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
70 salt = unbase64(salt);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
71 if not nonce or not salt or not i then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
72 return false, "Could not parse server_first_message";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
73 elseif nonce:find(c_nonce, 3, true) ~= 3 then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
74 return false, "nonce sent by server does not match our nonce";
362
d8c3e94d765d util.sasl.scram: Correctly verify that the server added its own nonce
Kim Alvefur <zash@zash.se>
parents: 359
diff changeset
75 elseif nonce == our_nonce then
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
76 return false, "server did not append s-nonce to nonce";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
77 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
78
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
79 local cbind_input = gs2_header .. cbind_data;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
80 local channel_binding = "c=" .. base64(cbind_input);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
81 local client_final_message_without_proof = channel_binding .. "," .. nonce;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
82
407
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
83 local SaltedPassword;
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
84 local ClientKey;
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
85 local ServerKey;
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
86
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
87 if stream.client_key and stream.server_key then
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
88 ClientKey = stream.client_key;
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
89 ServerKey = stream.server_key;
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
90 else
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
91 if stream.salted_password then
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
92 SaltedPassword = stream.salted_password;
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
93 elseif stream.password then
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
94 SaltedPassword = Hi(Normalize(stream.password), salt, i);
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
95 end
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
96 ServerKey = HMAC(SaltedPassword, "Server Key");
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
97 ClientKey = HMAC(SaltedPassword, "Client Key");
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
98 end
c99db5172309 util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents: 390
diff changeset
99
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
100 local StoredKey = H(ClientKey);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
101 local AuthMessage = client_first_message_bare .. "," .. server_first_message .. "," .. client_final_message_without_proof;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
102 local ClientSignature = HMAC(StoredKey, AuthMessage);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
103 local ClientProof = XOR(ClientKey, ClientSignature);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
104 local ServerSignature = HMAC(ServerKey, AuthMessage);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
105
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
106 local proof = "p=" .. base64(ClientProof);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
107 local client_final_message = client_final_message_without_proof .. "," .. proof;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
108
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
109 local ok, server_final_message = coroutine.yield(client_final_message);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
110 if ok ~= "success" then return false, "success-expected" end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
111
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
112 local verifier = server_final_message:match("v=([^,]+)");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
113 if unbase64(verifier) ~= ServerSignature then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
114 return false, "server signature did not match";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
115 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
116 return true;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
117 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
118
358
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
119 return function (stream, name)
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
120 if stream.username and (stream.password or (stream.client_key or stream.server_key)) then
358
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
121 if name == "SCRAM-SHA-1" then
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
122 return scram, 99;
359
a7aa761a436d util.sasl.scram: Fix typo
Kim Alvefur <zash@zash.se>
parents: 358
diff changeset
123 elseif name == "SCRAM-SHA-1-PLUS" then
453
e60c776b7760 util.sasl.scram: Refactor channel binding
Kim Alvefur <zash@zash.se>
parents: 407
diff changeset
124 if cb(stream.conn) then
358
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
125 return scram, 100;
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
126 end
356
f95e797895ee SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents: 355
diff changeset
127 end
355
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
128 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
129 end
358
a8f6fd6a70ed plugins.sasl: Alter mechanism loading and pass name of loaded mechanism. Fixes attempting SCRAM-PLUS when only SCRAM is offered
Kim Alvefur <zash@zash.se>
parents: 356
diff changeset
130