Author Topic: websockets and woe - lugnut's mission to create html5 websockets - still broked  (Read 3361 times)

some of you may know i've been running truce's torque webserver.
i've modified it a bit to allow support for various things, and my latest goal is to add support for html5 websockets.

following this guide and numerous other resources
http://www.altdevblogaday.com/2012/01/23/writing-your-own-websocket-server/
and
https://developer.mozilla.org/en-US/docs/WebSockets
i've made this addition to the Webclient::finish method in the webserver at line 253

Code: [Select]
if($_SERVERHTTP_Connection $= "Upgrade")
{
%server.debug("> Upgrading connection to websocket.");

if($WebSocketNum $= "")
$WebSocketNum = 0;

%spec = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
%clkey = $_SERVERHTTP_Sec_WebSocket_Key;

%catkey = %clkey @ %spec;
%server.debug("> CatKey: " @ %catkey);

%rkey = base64Encode(sha1(%catkey));
%server.debug("> key: " @ %rkey);

%this.send("HTTP/1.1 101 Switching Protocols\r\n");
%this.send("Upgrade: websocket\r\n");
%this.send("Connection: Upgrade\r\n");
%this.send("Sec-WebSocket-Accept: " @ %rkey @ "\r\n");
%this.send("\r\n");
// %this.send("Num = " @ $WebSocketNum @ "\r\n");

$WebSocketArray[$WebSocketNum] = %this;
$WebSocketNum++;
return;
}

the problem occurs when the system is calculating either the SHA1 sum or the base64 encoding at the end. here's a sample request utilizing javascript console:
Code: (client) [Select]
var s = new WebSocket("69.64.43.11:33580");
Code: (server) [Select]
% [Webserver] > Client timeout in 500 milliseconds.
% [Webserver] Packet terminated from client 1845687.
% [Webserver] Parsing client 1845687's header: Upgrade: websocket
Connection: Upgrade
Host: 69.64.43.11:33580
Origin: http://69.64.43.11:33580
Sec-WebSocket-Key: aGf7b7cHpm/HtRf16dG3Xg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
Cookie: a=streamline; n=Lugnut

% [Webserver] > Assigning websocket to HTTP_Upgrade.
% [Webserver] > Assigning Upgrade to HTTP_Connection.
% [Webserver] > Assigning 69.64.43.11:33580 to HTTP_Host.
% [Webserver] > Assigning http://69.64.43.11:33580 to HTTP_Origin.
% [Webserver] > Assigning aGf7b7cHpm/HtRf16dG3Xg== to HTTP_Sec_WebSocket_Key.
% [Webserver] > Assigning 13 to HTTP_Sec_WebSocket_Version.
% [Webserver] > Assigning x-webkit-deflate-frame to HTTP_Sec_WebSocket_Extensions.
% [Webserver] > Assigning a=streamline; n=Lugnut to HTTP_Cookie.
% [Webserver] Parsing client 1845687's POST args:
% [Webserver] > No POST args found to parse!
% [Webserver] > Upgrading connection to websocket.
% [Webserver] > clKey = aGf7b7cHpm/HtRf16dG3Xg==
% [Webserver] > recdKey = aGf7b7cHpm/HtRf16dG3Xg==
% [Webserver] > CatKey: aGf7b7cHpm/HtRf16dG3Xg==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
% [Webserver] > key: ZDdkN2FlMGM5MzFmN2RhY2Y0NDRlOWJmNzVmZTJjNzBkNTQyNzIwOQ==
Code: (client) [Select]
Error during WebSocket handshake: Sec-WebSocket-Accept mismatchas you can see, the specification magic key is appended to the sent key successfully, and the end result is pooped out.

using default programs in my linux distro, i ran the following commands in my terminal
Code: [Select]
lugnut@LugBook:~$ sha1sum
aGf7b7cHpm/HtRf16dG3Xg==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
bf416c18230328f3113518ed03f8fbcee351d7b4  -
lugnut@LugBook:~$ base64
bf416c18230328f3113518ed03f8fbcee351d7b4
YmY0MTZjMTgyMzAzMjhmMzExMzUxOGVkMDNmOGZiY2VlMzUxZDdiNAo=
firstly i input the whole key (sent + spec) into sha1sum
then i took that and put it into the base64 converter

obviously the webservers result is drastically different than the one i manually created.

however, i don't know who to blame :(

it appears that torque is miscalculating either base64 or sha1...

What can I do?
EDIT: after posting this i realized there was two spaces and a - after my sha1 encoding i had ignored
Code: [Select]
lugnut@LugBook:~$ base64
bf416c18230328f3113518ed03f8fbcee351d7b4  -
YmY0MTZjMTgyMzAzMjhmMzExMzUxOGVkMDNmOGZiY2VlMzUxZDdiNCAgLQo=
this is a match to my earlier result without the whitespace.
« Last Edit: August 31, 2012, 12:20:44 AM by Lugnut »

Here's the relevant part of that blog post
Quote from: article, code is in C
Connecting
Creating a WebSocket connection is initiated by the client sending the following upgrade request:

Code: [Select]
GET /servicename HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
The server responds with:

Code: [Select]
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Most of these HTTP fields are self explanatory but not Sec-WebSocket-Key and Sec-WebSocket-Accept. Sec-WebSocket-Key is a string sent by the client as a challenge to the server. This leads to the question- how does the server calculate the value of Sec-WebSocket-Accept and complete the challenge? It is quite simple. The server first takes Sec-WebSocket-Key and concatenates it with a GUID string from the WebSocket specification. Then the SHA-1 hash of the resulting string is computed and, finally, Sec-WebSocket-Accept is the base64 encoding of the hash value. Let’s work through an example:

Code: [Select]
SpecifcationGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
FullWebSocketKey = concatenate(Sec-WebSocket-Key, SpecifcationGUID);
  // dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
KeyHash = SHA-1(FullWebSocketKey);
  // 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
Sec-Websocket-Accept = base64(KeyHash);
  // s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Additionally, I ran the things in the blog through both my webserver and the default programs:
Code: [Select]
lugnut@LugBook:~$ sha1sum
dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
05082898ab78f6da9c1ad2587b8012cd0cf52172  -
lugnut@LugBook:~$ base64
05082898ab78f6da9c1ad2587b8012cd0cf52172
MDUwODI4OThhYjc4ZjZkYTljMWFkMjU4N2I4MDEyY2QwY2Y1MjE3Mgo=
Code: [Select]
==> echo(base64encode(sha1("dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
YjM3YTRmMmNjMDYyNGYxNjkwZjY0NjA2Y2YzODU5NDViMmJlYzRlYQ==

what the forget.

here's truce's base64 pack and dependancy FWIW
Code: [Select]
////////////////////////////////////////
//  Base64 Pack             by Truce  //
////////////////////////////////////////

function convertBase(%val,%atype,%btype)
{
%vlen = strLen(%val);
%alen = strLen(%atype);
%blen = strLen(%btype);

for(%i = 0; %i < %vlen; %i++)
%sum += striPos(%atype,getSubStr(%val,%i,1)) * mPow(%alen,%vlen - %i - 1);

while(1)
{
%rem = %sum % %blen;
%new = getSubStr(%btype,%rem,1) @ %new;
%sum = mFloor(%sum / %blen);

if(!%sum)
break;
}

return %new;
}

function base64Encode(%str)
{
%base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
%asciimap  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN" @
             "OPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";

%len = strLen(%str);

for(%i = 0; %i < %len; %i++)
{
%chr   = getSubStr(%str,%i,1);
%ascii = strPos(%asciimap,%chr) + 32;
%bin   = convertBase(%ascii,"0123456789","01");

while(strLen(%bin) < 8)
%bin = "0" @ %bin;

%all = %all @ %bin;
}

%len = strLen(%all);

for(%i = 0; %i < %len; %i += 6)
{
%pack = getSubStr(%all,%i,6);

while(strLen(%pack) < 6)
%pack = %pack @ "0";

%dec = convertBase(%pack,"01","0123456789");
%new = %new @ getSubStr(%base64map,%dec,1);
}

while(strLen(%new) % 4 > 0)
%new = %new @ "=";

return %new;
}
« Last Edit: August 28, 2012, 02:17:50 AM by Lugnut »

First, replace the base64Encode function with this version of it:
(Not sure what I was thinking by not including all characters...)

Quote
function base64Encode(%str)
{
   %base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabc defghijklmnopqrstuvwxyz012345 6789+/";
   %asciimap  =     "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" @
                "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" @
                "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F" @
                "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F" @
                "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F" @
                "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F" @
                "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F" @
                "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F" @
                "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" @
                "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" @
                "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" @
                "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" @
                "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" @
                "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" @
                "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" @
                "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
   
   %len = strLen(%str);
   
   for(%i = 0; %i < %len; %i++)
   {
      %chr   = getSubStr(%str,%i,1);
      %ascii = strPos(%asciimap,%chr) + 1;
      %bin   = convertBase(%ascii,"0123456789","01");
      
      while(strLen(%bin) < 8)
         %bin = "0" @ %bin;
      
      %all = %all @ %bin;
   }
   
   %len = strLen(%all);
   
   for(%i = 0; %i < %len; %i += 6)
   {
      %pack = getSubStr(%all,%i,6);
      
      while(strLen(%pack) < 6)
         %pack = %pack @ "0";
      
      %dec = convertBase(%pack,"01","0123456789");
      %new = %new @ getSubStr(%base64map,%dec,1);
   }
   
   while(strLen(%new) % 4 > 0)
      %new = %new @ "=";
   
   return %new;
}

Then in Torque, I skipped to line 5 of this code:

Code: [Select]
SpecifcationGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
FullWebSocketKey = concatenate(Sec-WebSocket-Key, SpecifcationGUID);
  // dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
KeyHash = SHA-1(FullWebSocketKey);
  // 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
Sec-Websocket-Accept = base64(KeyHash);
  // s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

and tested it as such:

Code: [Select]
$testsha = "\xB3\x7A\x4F\x2C\xC0\x62\x4F\x16\x90\xF6\x46\x06\xCF\x38\x59\x45\xB2\xBE\xC4\xEA";
echo(base64Encode($testsha));
// s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

So now it encodes the sha1 result properly. But what is Blockland's sha1 result?

Code: [Select]
$testkey = "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
echo(sha1($testkey));
// b37a4f2cc0624f1690f64606cf385945b2bec4ea

It looks different than line 5 of the first code block in this post, but it's not.

Code: [Select]
$testkey  = "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
$testsha  = sha1($testkey);
$testsha2 = "";

for(%i = 0; %i < strLen($testsha); %i += 2)
eval("$testsha2 = $testsha2 @ \"\\x" @ getSubStr($testsha,%i,2) @ "\";");

$result = base64Encode($testsha2);
echo($result);
// s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Basically, it's using every two characters in the sha1 result as one ascii character.
So with the fixed version of base64Encode + this method, it seems to work fine.

thanks mate!
i hear eval uses local variables from the context it's called in. this is valid, right?
Code: [Select]
function trueSha1(%input)
{
%sourceSha = sha1(%input);
%output = "";

for(%i = 0; %i < strLen(%sourceSha); %i += 2)
eval("%output = %output @ \"\\x" @ getSubStr(%sourceSha,%input,2) @ "\";");

%return = base64Encode(%output);
return %return;
}

i got worried when no one was responding, as if i had asked a question that was too complex :(

That is valid.

But, you don't need to do that.

You can just use collapseEscape("\\x" @ %a) where %a is the hex number.

Code: [Select]
function trueSha1(%input)
{
%sourceSha = sha1(%input);
%output = "";

for(%i = 0; %i < strLen(%sourceSha); %i += 2)
eval("%output = %output @ " @ collapseEscape("\\x", getSubStr(%sourceSha,%input,2)) @ "\";");

%return = base64Encode(%output);
return %return;
}
???

Code: [Select]
function trueSha1(%input)
{
%sourceSha = sha1(%input);
%output = "";

for(%i = 0; %i < strLen(%sourceSha); %i += 2)
%output = %output @ "\\x" @ getSubStr(%sourceSha,%input,2);

%return = base64Encode(%output);
return %return;
}

Why are you using eval unnecessarily 

???

I didn't know about collapseEscape, but if it works like I think it does, use it in place of eval.

Why are you using eval unnecessarily 

You need eval (or collapseEscape apparently) for this to work - actually try what you just posted.

I didn't know about collapseEscape, but if it works like I think it does, use it in place of eval.

You need eval (or collapseEscape apparently) for this to work - actually try what you just posted.
oh. ok

Code: [Select]
function trueSha1(%input)
{
%sourceSha = sha1(%input);
%output = "";

for(%i = 0; %i < strLen(%sourceSha); %i += 2)
%output = %output @ collapseEscape("\\x" @ getSubStr(%sourceSha,%input,2));

return base64Encode(%output);
}

lovey
thanks for the help guys, i'ma test it now

mmm that didn't work out so swell

> Upgrading connection to websocket.
> CatKey: h/D1cwIGMUTXf8khfOOFLw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
> ShaKey: z7PTz7PTz7PTz7PTz7PTz7PTz7M= //this is the key calculated using the trueSha1 function that truce made and ip modified
> key: di QVHn2UFR5N0BUdi QVHn2UFR5N0BUdi NPQ==

... spaces?

using ip's code

Replace %input in the for loop with %i
I don't know why you did that

Replace %input in the for loop with %i
I don't know why you did that
misinterpreted the code, was too excited to see it finally working T.T

Code: [Select]
> CatKey: 27ebx99X70eajZ2+toSlog==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
> ShaKey: pBmGM6MRWiwhVBJD85M0vIZ8nQj=
> key: cEJsRzz1TVJXaX nVjJKRDf0TTB1SVn3bkFpPQ==
Code: [Select]
function trueSha1(%input)
{
%sourceSha = sha1(%input);

for(%i = 0; %i < strLen(%sourceSha); %i += 2)
%output = %output @ collapseEscape("\\x" @ getSubStr(%sourceSha,%i,2));

return base64Encode(%output);
}

wat do
it appears to be throwing random spaces in
should i try just removing them?
Code: [Select]
lugnut@LugBook:~$ base64
pBmGM6MRWiwhVBJD85M0vIZ8nQj=
cEJtR002TVJXaXdoVkJKRDg1TTB2SVo4blFqPQo=
Code: [Select]
cEJtR002TVJXaX [space in torque version is here] doVkJKRDg1TTB2SVo4blFqPQo= // note difference


it appears the code is misinterpreting one of the characters as a space OR outputting some of it as a space

which i guess doesn't work

Quote from: me
wat do

ran it through my stuff
Code: [Select]
lugnut@LugBook:~$ base64 -d
cEJsRzz1TVJXaX nVjJKRDf0TTB1SVn3bkFpPQ==
pBlG<�MRWibase64: invalid input
lugnut@LugBook:~$ base64
pBmGM6MRWiwhVBJD85M0vIZ8nQj=
cEJtR002TVJXaXdoVkJKRDg1TTB2SVo4blFqPQo=
lugnut@LugBook:~$ base64 -d
cEJtR002TVJXaXdoVkJKRDg1TTB2SVo4blFqPQo=
pBmGM6MRWiwhVBJD85M0vIZ8nQj=
« Last Edit: August 28, 2012, 02:36:50 PM by Lugnut »

Check your syntax, or post the whole code? Using both my code and the function you posted, I got:

Quote from: input
27ebx99X70eajZ2+toSlog==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
Quote from: sha1
a819c633c3115a3c62541243fba335c0867ea109
Quote from: sha1 ascii
¨\x19Æ3Ã\x11Z<bT\x12Cû£5À†~¡\t
Quote from: base64
qBnGM8MRWjxiVBJD+6M1wIZ+oQk=

(note the escaped characters in sha1 ascii, don't just paste it into a converter)