Hi,
I noticed that because the obfsproxy api can sometimes buffer and resend smaller chunks of data. My simple use of Nacl stream_crypto to wrap each incoming data buffers will not work... that is because the client and server must keep synchronized nonce counters for the decrypt/encrypt to work... and in this case the client may send one large buffer and the server may receive many smaller buffers... trying to decrypt them with different nonces will of course fail.
https://github.com/david415/obfsproxy/tree/david-nacl-stream-withoutkeyexcha...
It seems like the solution is to write a super simple "framing protocol"... which is to say that I can first send a frame length; and on the receiving end simply read until frame length worth of data is consumed... and then apply the crypto_stream cipher on that frame with the correct corresponding nonce.
David
On Sun, Nov 17, 2013 at 07:33:12PM -0800, David Stainton wrote:
It seems like the solution is to write a super simple "framing protocol"... which is to say that I can first send a frame length; and on the receiving end simply read until frame length worth of data is consumed... and then apply the crypto_stream cipher on that frame with the correct corresponding nonce.
That sounds like a reasonable solution. ScrambleSuit also has its own protocol header, if that helps: https://gitweb.torproject.org/user/phw/scramblesuit.git/blob/HEAD:/message.p...
Also, I'm probably stating the obvious here, but you seem to be using a static key for encryption and decryption. That results in a many time pad, i.e., the same key is used for many plain texts. That's a problem because Tor's TLS handshake is predictable and a censor could observe that multiple independent SaltyStream connections share several bytes in their handshake. You might want to use a key derivation function, as also suggested by the NaCl doc:
NaCl does not make any promises regarding the resistance of crypto_stream to "related-key attacks." It is the caller's responsibility to use proper key-derivation functions.
Cheers, Philipp
It seems like the solution is to write a super simple "framing protocol"... which is to say that I can first send a frame length; and on the receiving end simply read until frame length worth of data is consumed... and then apply the crypto_stream cipher on that frame with the correct corresponding nonce.
That sounds like a reasonable solution. ScrambleSuit also has its own protocol header, if that helps: https://gitweb.torproject.org/user/phw/scramblesuit.git/blob/HEAD:/message.p...
Cool. Thanks! I'll take a look.
Also, I'm probably stating the obvious here, but you seem to be using a static key for encryption and decryption. That results in a many time pad, i.e., the same key is used for many plain texts. That's a problem because Tor's TLS handshake is predictable and a censor could observe that multiple independent SaltyStream connections share several bytes in their handshake. You might want to use a key derivation function, as also suggested by the NaCl doc:
NaCl does not make any promises regarding the resistance of crypto_stream to "related-key attacks." It is the caller's responsibility to use proper key-derivation functions.
I wrote this as a sort of rough draft... It is meant to accept a key from the commandline... you know, for testing. So I specified default keys... but really this is just for testing. To be useful at all there'd have to be proper key generation like you are saying and key exchange...
It's OK to use crypto_stream to encode multiple messages with the same key as long as the nonce is different each time :
"""This means that an attacker cannot distinguish this function from a uniform random function. Consequently, if a series of messages is encrypted by crypto_stream_xor with a different nonce for each message, the ciphertexts are indistinguishable from uniform random strings of the same length."""
Cheers
David
On Sun, Nov 17, 2013 at 07:33:12PM -0800, David Stainton wrote:
Hi,
I noticed that because the obfsproxy api can sometimes buffer and resend smaller chunks of data. My simple use of Nacl stream_crypto to wrap each incoming data buffers will not work... that is because the client and server must keep synchronized nonce counters for the decrypt/encrypt to work... and in this case the client may send one large buffer and the server may receive many smaller buffers... trying to decrypt them with different nonces will of course fail.
https://github.com/david415/obfsproxy/tree/david-nacl-stream-withoutkeyexcha...
It seems like the solution is to write a super simple "framing protocol"... which is to say that I can first send a frame length; and on the receiving end simply read until frame length worth of data is consumed... and then apply the crypto_stream cipher on that frame with the correct corresponding nonce.
Super-simple framing protocols often fall victim to attacks in which the adversary messes with the length in the frame header. See, for example, "Plaintext Recovery Attacks Against SSH": http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf
So be careful here.
- Ian
Super-simple framing protocols often fall victim to attacks in which the adversary messes with the length in the frame header. See, for example, "Plaintext Recovery Attacks Against SSH": http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf
So be careful here.
- Ian
Over Tor it won't be a problem because Tor is authenticated. Thanks for the paper... I do really love to read papers like this.
On Mon, Nov 18, 2013 at 10:47 AM, David Stainton dstainton415@gmail.com wrote:
Super-simple framing protocols often fall victim to attacks in which the adversary messes with the length in the frame header. See, for example, "Plaintext Recovery Attacks Against SSH": http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf
Over Tor it won't be a problem because Tor is authenticated.
I think that's an oversimplistic way of looking at the problem, but you're mostly right.
Suppose that your message framing is just
len (4 bytes plaintext) | LEN bytes of cipher text including higher level MAC | ...
If an active MITM flips bits in the length field, you will feed the wrong number of bytes to the decryptor and the MAC check will fail. Moreover, this is an *unrecoverable* failure condition: there's no way for you to get back in sync with the other end. You have to drop the connection, and the other side has to respond to a connection drop in a sensible fashion. (I wouldn't recommend trying to reconnect and retransmit the queue - rather, propagate the failure up to Tor.) But, because the length field is plaintext, there is no decryption oracle; the adversary can't trick you into decrypting something that's not a length field and treating the result as a length field. I think this is probably acceptable for obfsK, which is just supposed to be different from anything recognizable.
If you want all-bytes-on-the-wire-indistinguishable-from-randomness, that is considerably harder -- a great deal of the complexity of the "chopper" of https://hacks.owlfolio.org/pubs/2012-stegotorus.pdf is in service of being able to encrypt the length field without exposing a decryption oracle. Note that I no longer think that design is sound, but it will still give you an idea of what it takes.
zw
yeah... you are right! Thanks for the clarification. I've been meaning to read the Stegotorus paper soon.
Cheers!
David
On Mon, Nov 18, 2013 at 9:24 AM, Zack Weinberg zackw@panix.com wrote:
On Mon, Nov 18, 2013 at 10:47 AM, David Stainton dstainton415@gmail.com wrote:
Super-simple framing protocols often fall victim to attacks in which the adversary messes with the length in the frame header. See, for example, "Plaintext Recovery Attacks Against SSH": http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf
Over Tor it won't be a problem because Tor is authenticated.
I think that's an oversimplistic way of looking at the problem, but you're mostly right.
Suppose that your message framing is just
len (4 bytes plaintext) | LEN bytes of cipher text including higher level MAC | ...
If an active MITM flips bits in the length field, you will feed the wrong number of bytes to the decryptor and the MAC check will fail. Moreover, this is an *unrecoverable* failure condition: there's no way for you to get back in sync with the other end. You have to drop the connection, and the other side has to respond to a connection drop in a sensible fashion. (I wouldn't recommend trying to reconnect and retransmit the queue - rather, propagate the failure up to Tor.) But, because the length field is plaintext, there is no decryption oracle; the adversary can't trick you into decrypting something that's not a length field and treating the result as a length field. I think this is probably acceptable for obfsK, which is just supposed to be different from anything recognizable.
If you want all-bytes-on-the-wire-indistinguishable-from-randomness, that is considerably harder -- a great deal of the complexity of the "chopper" of https://hacks.owlfolio.org/pubs/2012-stegotorus.pdf is in service of being able to encrypt the length field without exposing a decryption oracle. Note that I no longer think that design is sound, but it will still give you an idea of what it takes.
zw _______________________________________________ tor-dev mailing list tor-dev@lists.torproject.org https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-dev