Software / code / verse
Annotate
util/sasl/scram.lua @ 356:f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 18 Sep 2014 19:03:15 +0200 |
| parent | 355:dfe095fcf89c |
| child | 358:a8f6fd6a70ed |
| rev | line source |
|---|---|
| 355 | 1 |
| 2 local base64, unbase64 = require "mime".b64, require"mime".unb64; | |
| 3 local crypto = require"crypto"; | |
| 4 local bit = require"bit"; | |
| 5 | |
| 6 local XOR, H, HMAC, Hi; | |
| 7 local tonumber = tonumber; | |
| 8 local char, byte = string.char, string.byte; | |
| 9 local gsub = string.gsub; | |
| 10 local xor = bit.bxor; | |
| 11 | |
| 12 function XOR(a, b) | |
| 13 return (gsub(a, "()(.)", function(i, c) | |
| 14 return char(xor(byte(c), byte(b, i))) | |
| 15 end)); | |
| 16 end | |
| 17 | |
| 18 function H(str) | |
| 19 return crypto.digest("sha1", str, true); | |
| 20 end | |
| 21 | |
| 22 function HMAC(key, str) | |
| 23 return crypto.hmac.digest("sha1", str, key, true); | |
| 24 end | |
| 25 | |
| 26 function Hi(str, salt, i) | |
| 27 local U = HMAC(str, salt .. "\0\0\0\1"); | |
| 28 local ret = U; | |
| 29 for _ = 2, i do | |
| 30 U = HMAC(str, U); | |
| 31 ret = XOR(ret, U); | |
| 32 end | |
| 33 return ret; | |
| 34 end | |
| 35 | |
| 36 -- assert(Hi("password", "salt", 1) == string.char(0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, 0x2f, 0xe0, 0x37, 0xa6)); | |
| 37 -- assert(Hi("password", "salt", 2) == string.char(0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, 0xd8, 0xde, 0x89, 0x57)); | |
| 38 | |
| 39 local function Normalize(str) | |
| 40 return str; -- TODO | |
| 41 end | |
| 42 | |
| 43 local function value_safe(str) | |
| 44 return (gsub(str, "[,=]", { [","] = "=2C", ["="] = "=3D" })); | |
| 45 end | |
| 46 | |
| 47 local function scram(stream, name) | |
| 48 local username = "n=" .. value_safe(stream.username); | |
| 49 local c_nonce = base64(crypto.rand.bytes(15)); | |
| 50 local nonce = "r=" .. c_nonce; | |
| 51 local client_first_message_bare = username .. "," .. nonce; | |
| 52 local cbind_data = ""; | |
|
356
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
53 local gs2_cbind_flag = "y"; |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
54 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
|
55 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
|
56 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
|
57 end |
| 355 | 58 local gs2_header = gs2_cbind_flag .. ",,"; |
| 59 local client_first_message = gs2_header .. client_first_message_bare; | |
| 60 local cont, server_first_message = coroutine.yield(client_first_message); | |
| 61 if cont ~= "challenge" then return false end | |
| 62 | |
| 63 local salt, iteration_count; | |
| 64 nonce, salt, iteration_count = server_first_message:match("(r=[^,]+),s=([^,]*),i=(%d+)"); | |
| 65 local i = tonumber(iteration_count); | |
| 66 salt = unbase64(salt); | |
| 67 if not nonce or not salt or not i then | |
| 68 return false, "Could not parse server_first_message"; | |
| 69 elseif nonce:find(c_nonce, 3, true) ~= 3 then | |
| 70 return false, "nonce sent by server does not match our nonce"; | |
| 71 elseif nonce == c_nonce then | |
| 72 return false, "server did not append s-nonce to nonce"; | |
| 73 end | |
| 74 | |
| 75 local cbind_input = gs2_header .. cbind_data; | |
| 76 local channel_binding = "c=" .. base64(cbind_input); | |
| 77 local client_final_message_without_proof = channel_binding .. "," .. nonce; | |
| 78 | |
| 79 local SaltedPassword = Hi(Normalize(stream.password), salt, i); | |
| 80 local ClientKey = HMAC(SaltedPassword, "Client Key"); | |
| 81 local StoredKey = H(ClientKey); | |
| 82 local AuthMessage = client_first_message_bare .. "," .. server_first_message .. "," .. client_final_message_without_proof; | |
| 83 local ClientSignature = HMAC(StoredKey, AuthMessage); | |
| 84 local ClientProof = XOR(ClientKey, ClientSignature); | |
| 85 local ServerKey = HMAC(SaltedPassword, "Server Key"); | |
| 86 local ServerSignature = HMAC(ServerKey, AuthMessage); | |
| 87 | |
| 88 local proof = "p=" .. base64(ClientProof); | |
| 89 local client_final_message = client_final_message_without_proof .. "," .. proof; | |
| 90 | |
| 91 local ok, server_final_message = coroutine.yield(client_final_message); | |
| 92 if ok ~= "success" then return false, "success-expected" end | |
| 93 | |
| 94 local verifier = server_final_message:match("v=([^,]+)"); | |
| 95 if unbase64(verifier) ~= ServerSignature then | |
| 96 return false, "server signature did not match"; | |
| 97 end | |
| 98 return true; | |
| 99 end | |
| 100 | |
| 101 return function (stream, mechanisms, preference, supported) | |
| 102 if stream.username and (stream.password or (stream.client_key or stream.server_key)) then | |
| 103 mechanisms["SCRAM-SHA-1"] = scram; | |
| 104 preference["SCRAM-SHA-1"] = 99; | |
|
356
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
105 local sock = stream.conn:ssl() and stream.conn:socket(); |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
106 if sock and sock.getfinished then |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
107 mechanisms["SCRAM-SHA-1-PLUS"] = scram; |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
108 preference["SCRAM-SHA-1-PLUS"] = 100 |
|
f95e797895ee
SCRAM: Add channel binding support (SCRAM-SHA-1-PLUS)
Kim Alvefur <zash@zash.se>
parents:
355
diff
changeset
|
109 end |
| 355 | 110 end |
| 111 end |