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
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;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
3 local crypto = require"crypto";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
4 local bit = require"bit";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
5
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
6 local XOR, H, HMAC, Hi;
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
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
12 function XOR(a, b)
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
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
18 function H(str)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
19 return crypto.digest("sha1", str, true);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
20 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
21
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
22 function HMAC(key, str)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
23 return crypto.hmac.digest("sha1", str, key, true);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
24 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
25
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
26 function Hi(str, salt, i)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
27 local U = HMAC(str, salt .. "\0\0\0\1");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
28 local ret = U;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
29 for _ = 2, i do
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
30 U = HMAC(str, U);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
31 ret = XOR(ret, U);
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 return ret;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
34 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
35
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
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));
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
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));
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
38
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
39 local function Normalize(str)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
40 return str; -- TODO
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
41 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
42
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
43 local function value_safe(str)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
44 return (gsub(str, "[,=]", { [","] = "=2C", ["="] = "=3D" }));
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
45 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
46
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
47 local function scram(stream, name)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
48 local username = "n=" .. value_safe(stream.username);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
49 local c_nonce = base64(crypto.rand.bytes(15));
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
50 local nonce = "r=" .. c_nonce;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
51 local client_first_message_bare = username .. "," .. nonce;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
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
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
58 local gs2_header = gs2_cbind_flag .. ",,";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
59 local client_first_message = gs2_header .. client_first_message_bare;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
60 local cont, server_first_message = coroutine.yield(client_first_message);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
61 if cont ~= "challenge" then return false end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
62
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
63 local salt, iteration_count;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
64 nonce, salt, iteration_count = server_first_message:match("(r=[^,]+),s=([^,]*),i=(%d+)");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
65 local i = tonumber(iteration_count);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
66 salt = unbase64(salt);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
67 if not nonce or not salt or not i then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
68 return false, "Could not parse server_first_message";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
69 elseif nonce:find(c_nonce, 3, true) ~= 3 then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
70 return false, "nonce sent by server does not match our nonce";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
71 elseif nonce == c_nonce then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
72 return false, "server did not append s-nonce to nonce";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
73 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
74
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
75 local cbind_input = gs2_header .. cbind_data;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
76 local channel_binding = "c=" .. base64(cbind_input);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
77 local client_final_message_without_proof = channel_binding .. "," .. nonce;
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 SaltedPassword = Hi(Normalize(stream.password), salt, i);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
80 local ClientKey = HMAC(SaltedPassword, "Client Key");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
81 local StoredKey = H(ClientKey);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
82 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
83 local ClientSignature = HMAC(StoredKey, AuthMessage);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
84 local ClientProof = XOR(ClientKey, ClientSignature);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
85 local ServerKey = HMAC(SaltedPassword, "Server Key");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
86 local ServerSignature = HMAC(ServerKey, AuthMessage);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
87
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
88 local proof = "p=" .. base64(ClientProof);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
89 local client_final_message = client_final_message_without_proof .. "," .. proof;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
90
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
91 local ok, server_final_message = coroutine.yield(client_final_message);
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
92 if ok ~= "success" then return false, "success-expected" end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
93
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
94 local verifier = server_final_message:match("v=([^,]+)");
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
95 if unbase64(verifier) ~= ServerSignature then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
96 return false, "server signature did not match";
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
97 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
98 return true;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
99 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
100
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
101 return function (stream, mechanisms, preference, supported)
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
102 if stream.username and (stream.password or (stream.client_key or stream.server_key)) then
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
103 mechanisms["SCRAM-SHA-1"] = scram;
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
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
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
110 end
dfe095fcf89c Add SCRAM-SHA-1 implementation
Kim Alvefur <zash@zash.se>
parents:
diff changeset
111 end