Software / code / verse
Annotate
util/sasl/scram.lua @ 438:98dc1750584d 0.10.0
pubsub: Support for 'notify' in retract and purge operations
This is a slight API change for :retract(), but should be backwards-compatible.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Mon, 06 Dec 2021 09:09:50 +0000 |
| parent | 407:c99db5172309 |
| child | 453:e60c776b7760 |
| rev | line source |
|---|---|
| 355 | 1 |
| 2 local base64, unbase64 = require "mime".b64, require"mime".unb64; | |
|
390
7f535a1d5827
util.sasl.scram: Use the new util.hashes and util.random
Kim Alvefur <zash@zash.se>
parents:
365
diff
changeset
|
3 local hashes = require"util.hashes"; |
| 355 | 4 local bit = require"bit"; |
|
390
7f535a1d5827
util.sasl.scram: Use the new util.hashes and util.random
Kim Alvefur <zash@zash.se>
parents:
365
diff
changeset
|
5 local random = require"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 | |
| 38 local function scram(stream, name) | |
| 39 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
|
40 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
|
41 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
|
42 local client_first_message_bare = username .. "," .. our_nonce; |
| 355 | 43 local cbind_data = ""; |
|
365
48bf6993b4c4
util.sasl.scram: Only indicate channel binding support when TLS is used
Kim Alvefur <zash@zash.se>
parents:
363
diff
changeset
|
44 local gs2_cbind_flag = stream.conn:ssl() and "y" or "n"; |
|
356
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
45 if name == "SCRAM-SHA-1-PLUS" then |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
46 cbind_data = stream.conn:socket():getfinished(); |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
47 gs2_cbind_flag = "p=tls-unique"; |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
48 end |
| 355 | 49 local gs2_header = gs2_cbind_flag .. ",,"; |
| 50 local client_first_message = gs2_header .. client_first_message_bare; | |
| 51 local cont, server_first_message = coroutine.yield(client_first_message); | |
| 52 if cont ~= "challenge" then return false end | |
| 53 | |
|
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 nonce, salt, iteration_count = server_first_message:match("(r=[^,]+),s=([^,]*),i=(%d+)"); |
| 355 | 55 local i = tonumber(iteration_count); |
| 56 salt = unbase64(salt); | |
| 57 if not nonce or not salt or not i then | |
| 58 return false, "Could not parse server_first_message"; | |
| 59 elseif nonce:find(c_nonce, 3, true) ~= 3 then | |
| 60 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
|
61 elseif nonce == our_nonce then |
| 355 | 62 return false, "server did not append s-nonce to nonce"; |
| 63 end | |
| 64 | |
| 65 local cbind_input = gs2_header .. cbind_data; | |
| 66 local channel_binding = "c=" .. base64(cbind_input); | |
| 67 local client_final_message_without_proof = channel_binding .. "," .. nonce; | |
| 68 | |
|
407
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
69 local SaltedPassword; |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
70 local ClientKey; |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
71 local ServerKey; |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
72 |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
73 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
|
74 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
|
75 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
|
76 else |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
77 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
|
78 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
|
79 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
|
80 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
|
81 end |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
82 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
|
83 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
|
84 end |
|
c99db5172309
util.sasl.scram: Add support for authenticating with pre-hashed password
Kim Alvefur <zash@zash.se>
parents:
390
diff
changeset
|
85 |
| 355 | 86 local StoredKey = H(ClientKey); |
| 87 local AuthMessage = client_first_message_bare .. "," .. server_first_message .. "," .. client_final_message_without_proof; | |
| 88 local ClientSignature = HMAC(StoredKey, AuthMessage); | |
| 89 local ClientProof = XOR(ClientKey, ClientSignature); | |
| 90 local ServerSignature = HMAC(ServerKey, AuthMessage); | |
| 91 | |
| 92 local proof = "p=" .. base64(ClientProof); | |
| 93 local client_final_message = client_final_message_without_proof .. "," .. proof; | |
| 94 | |
| 95 local ok, server_final_message = coroutine.yield(client_final_message); | |
| 96 if ok ~= "success" then return false, "success-expected" end | |
| 97 | |
| 98 local verifier = server_final_message:match("v=([^,]+)"); | |
| 99 if unbase64(verifier) ~= ServerSignature then | |
| 100 return false, "server signature did not match"; | |
| 101 end | |
| 102 return true; | |
| 103 end | |
| 104 | |
|
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
|
105 return function (stream, name) |
| 355 | 106 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
|
107 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
|
108 return scram, 99; |
| 359 | 109 elseif name == "SCRAM-SHA-1-PLUS" 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
|
110 local sock = stream.conn:ssl() and stream.conn:socket(); |
|
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
|
111 if sock and sock.getfinished 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
|
112 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
|
113 end |
|
356
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
114 end |
| 355 | 115 end |
| 116 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
|
117 |