Software /
code /
verse
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 |
rev | line source |
---|---|
355 | 1 |
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 | 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 | 6 |
7 local tonumber = tonumber; | |
8 local char, byte = string.char, string.byte; | |
9 local gsub = string.gsub; | |
10 local xor = bit.bxor; | |
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 | 13 return (gsub(a, "()(.)", function(i, c) |
14 return char(xor(byte(c), byte(b, i))) | |
15 end)); | |
16 end | |
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 | 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 | 21 local U = HMAC(str, salt .. "\0\0\0\1"); |
22 local ret = U; | |
23 for _ = 2, i do | |
24 U = HMAC(str, U); | |
25 ret = XOR(ret, U); | |
26 end | |
27 return ret; | |
28 end | |
29 | |
30 local function Normalize(str) | |
31 return str; -- TODO | |
32 end | |
33 | |
34 local function value_safe(str) | |
35 return (gsub(str, "[,=]", { [","] = "=2C", ["="] = "=3D" })); | |
36 end | |
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 | 51 local function scram(stream, name) |
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 | 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 | 63 local gs2_header = gs2_cbind_flag .. ",,"; |
64 local client_first_message = gs2_header .. client_first_message_bare; | |
65 local cont, server_first_message = coroutine.yield(client_first_message); | |
66 if cont ~= "challenge" then return false end | |
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 | 69 local i = tonumber(iteration_count); |
70 salt = unbase64(salt); | |
71 if not nonce or not salt or not i then | |
72 return false, "Could not parse server_first_message"; | |
73 elseif nonce:find(c_nonce, 3, true) ~= 3 then | |
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 | 76 return false, "server did not append s-nonce to nonce"; |
77 end | |
78 | |
79 local cbind_input = gs2_header .. cbind_data; | |
80 local channel_binding = "c=" .. base64(cbind_input); | |
81 local client_final_message_without_proof = channel_binding .. "," .. nonce; | |
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 | 100 local StoredKey = H(ClientKey); |
101 local AuthMessage = client_first_message_bare .. "," .. server_first_message .. "," .. client_final_message_without_proof; | |
102 local ClientSignature = HMAC(StoredKey, AuthMessage); | |
103 local ClientProof = XOR(ClientKey, ClientSignature); | |
104 local ServerSignature = HMAC(ServerKey, AuthMessage); | |
105 | |
106 local proof = "p=" .. base64(ClientProof); | |
107 local client_final_message = client_final_message_without_proof .. "," .. proof; | |
108 | |
109 local ok, server_final_message = coroutine.yield(client_final_message); | |
110 if ok ~= "success" then return false, "success-expected" end | |
111 | |
112 local verifier = server_final_message:match("v=([^,]+)"); | |
113 if unbase64(verifier) ~= ServerSignature then | |
114 return false, "server signature did not match"; | |
115 end | |
116 return true; | |
117 end | |
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 | 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 | 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 | 128 end |
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 |