Sat, 20 May 2023 20:48:03 +0200
Update for new Prosody module namespace
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 |