shadowsocks / shadowsocks-chromeapp

Chrome client for shadowsocks
696 stars 541 forks source link

Importance of Shadowsocks-ChromeApp #1

Closed patwwh closed 10 years ago

patwwh commented 10 years ago

Hi All Developers, I notice that this Shadowsocks Client (as Chrome Extension) has been stopped developed for over 1 year. Sincerely encourage you to take a look on it again and see if it can be re-developed now or not. Being able to deploy Shadowsocks is a big convenience for users. It also fully utilize the advantage of Shadowsocks, which is the rare one fully support Javascript client (needed for Chrome extension). Pat

clowwindy commented 10 years ago

No, it hasn't been stopped. Everything that can be done has be done.

At the moment, I'm still waiting for Chrome to implement the Web Cryptography API.

patwwh commented 10 years ago

Thanks. Look forward to seeing its birth :)

kzahel commented 10 years ago

Hi. web crypto API is shipped in current stable versions in chrome. Any chance you'll continue development?

clowwindy commented 10 years ago

Wonderful work, I'll check if we share any same cipher.

clowwindy commented 10 years ago

Yes we can use AES-CFB8. But it's 16x slower than AES-CFB128. Why did they choose this algorithm?

zhuzhuor commented 10 years ago

@clowwindy Highly recommend you to use AES-GCM if possible. It is an authenticated encryption scheme. It's also included in TLS 1.2

clowwindy commented 10 years ago

Using GCM requires us to encapsulate each packet. This is suitable for each end of the communication. For a middle proxy, doing this will split each packet of a full MTU 1500 into two packets: a packet of length 1500, and a packet of a small length, resulting in low throughput and being vulnerable to detect.

ref: https://tools.ietf.org/html/rfc5116#ref-GCM https://tools.ietf.org/html/rfc4303

clowwindy commented 10 years ago

Actually WebCrypto API requires us to encrypt the whole data at one time. Thus those ciphers are not stream ciphers.

kzahel commented 10 years ago

Oh right I remember that. I was also disappointed when I found out there was no .update() and .digest() methods for even a SHA1 digest. web crypto API seems pretty weak

Is it really too slow to use crypto-js for the ciphers? I imagine it would be too much work to try to use some c library which supports streaming mode with pnacl.

clowwindy commented 10 years ago

I wonder is there actually one JavaScript crypto library that has implemented stream cipher and CFB mode correctly. I looked at some of them a year ago, like crypto-js, forge, but none of them meet the requirement for Shadowsocks.

If you know any library that will allow us to encrypt and decrypt AES-256-CFB or RC4 progressively, please let me know.

kzahel commented 10 years ago

I haven't used crypto-js stream ciphers, but according to the documents, it supports RC4 at least https://code.google.com/p/crypto-js/#RC4,_RC4Drop

zhuzhuor commented 10 years ago

You can use AES-CTR as a stream cipher. It is extremely easy to implement if you have code for AES.

PS. RC4 is (almost) broken, so please use other cipher suites if you can. See https://www.schneier.com/blog/archives/2013/03/new_rc4_attack.html

clowwindy commented 10 years ago

Shadowsocks is not for serious encryption since it's not even CCA secure. For the firewall we are facing, recording 2^24 first packets from each TCP connection between each two endpoints is not practical at the moment. We RC4 is considered dead, switching protocol just takes minutes.

zhuzhuor commented 10 years ago

For AES-CTR, I may write a small js lib based on webcrypto api to make it output progressively, later today when I have time. It should also be faster and more parallelizable than CFB and RC4.

clowwindy commented 10 years ago

Added AES-CTR mode from OpenSSL EVP interface in server: https://github.com/clowwindy/shadowsocks/commit/0e649453edb26905f0ad5006593a5de9a272ff0a

clowwindy commented 10 years ago

@kzahel Tested crypto-js with RC4. Still broken. The implementation does not behave like a real stream cipher.

Test code:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/rc4.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
    var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');                           

    var aesEncryptor = CryptoJS.algo.RC4.createEncryptor(key);

    var ciphertextPart1 = aesEncryptor.process("Message Part 1");
    var ciphertextPart2 = aesEncryptor.process("Message Part 2");
    var ciphertextPart3 = aesEncryptor.process("Message Part 3");
    var ciphertextPart4 = aesEncryptor.finalize();

    var aesDecryptor = CryptoJS.algo.RC4.createDecryptor(key);

    var plaintextPart1 = aesDecryptor.process(ciphertextPart1);
    var plaintextPart2 = aesDecryptor.process(ciphertextPart2);
    var plaintextPart3 = aesDecryptor.process(ciphertextPart3);
    var plaintextPart4 = aesDecryptor.process(ciphertextPart4);
    var plaintextPart5 = aesDecryptor.finalize();

    console.log('1:' + plaintextPart1.toString(CryptoJS.enc.Utf8));
    console.log('2:' + plaintextPart2.toString(CryptoJS.enc.Utf8));
    console.log('3:' + plaintextPart3.toString(CryptoJS.enc.Utf8));
    console.log('4:' + plaintextPart4.toString(CryptoJS.enc.Utf8));
    console.log('5:' + plaintextPart5.toString(CryptoJS.enc.Utf8));
</script>

Expected:

1:Message Part 1
2:Message Part 2
3:Message Part 3
4:
5:

Actual:

1:Message Part
2: 1Message Part 2
3:Message Part
4:
5: 3
clowwindy commented 10 years ago

Will use an RC4 library written by myself just now.

https://github.com/clowwindy/jsrc4/blob/master/jsrc4.js

On Chrome 37: 185.98884066955983MB/s

kzahel commented 10 years ago

Wow, @clowwindy that is really, really great. I am eagerly following your progress. I very much would like to have a lightweight RC4 for bittorrent protocol encryption.

clowwindy commented 10 years ago

@kzahel Do you have any experience with chrome.sockets.tcp API? I found its performance really poor.

I created a TCP server that does nothing but piping data from one socket to another. It takes 150% CPU with 2.5MB/s throughput. One Google Chrome Helper takes 100% and one Google Chrome takes about 50%. At the same time, curl process that is dumping that data into /dev/null takes 3% CPU.

RC4 encryption is quite fast compared to that.

The code I use can be simplified as:

  readHandler = function(data, error) {
    if (error) {
      // close sockets
      // ...
      return;
    }
    tcp.send(anotherSocketId, data, function(sendInfo) {
      if (sendInfo.resultCode < 0) {
        // close sockets
        // ...
        return;
      }
    });
  };
zhuzhuor commented 10 years ago

@clowwindy It seems Chrome hasn't fully implemented APIs for AES-CTR yet, although it's in the w3 specification. I tried to build my own (progressive) AES-CTR by using AES-CBC as an underlying block. It is working but very slow. I guess we may have to wait for some time if we want to use built-in AES-CTR in Chrome as a stream cipher.

clowwindy commented 10 years ago

@zhuzhuor I implemented RC4 in JavaScript and it's around 180MB/s on Chrome. But after I then found that the new socket API provided by Chrome is terrible. It's not only slow, but requires us to keep creating new ArrayBuffers for each send() and recv() operation, rather than reusing them.

kzahel commented 10 years ago

@clowwindy you're using "chrome.sockets", not "chrome.socket" API, right? I found that the newer one performs better. You don't call .recv() anymore, you set pause/unpaused and have an event listener for when there is data to be read from the socket. I found it increased the throughput.

I do notice my app which uses chrome.sockets heavily is a very CPU bound app. But I haven't done any good benchmarking to see exactly where it's CPU bound. I am very interested in seeing example code gist of your sockets piping example that uses a lot of CPU. Maybe making a "crbug" would bring it to google's attention and somebody would work on it

I didn't ever try simply reading from one socket and writing the same arraybuffer to another socket, it's strange that that would give an error (it throws some kind of exception, or what?)