> To detect a bunny specific packet, each outbound packet is resized to match pre determined pack_len % mod = remainder values. Then when looking for packet bunny just tests each packets length against the modulus and remainder values.
Ok so the code is a bit weird in that section because of issues with different wifi chipsets (thus the -4). Sorry about the messy code, I would barely call Bunny alpha.
The mod/remainder values are agreed upon by the network operators, that is why they are in config.
If you know enough about floating point to prove that the rounding will always give you the correct answer, then probably you would have just used the integer representation to begin with.
The crypto is super simple currently. I have spent a long time trying to figure out a better solution but currently there is a single round of AES-256 in CBC mode, each message gets a random IV.
To answer the first questions from this, 1) yes I know there is no MAC I am working on that and could use some ideas 2) forward security is not built in, but if you can find a quicker way to renegotiable mpOTR we might be in business.
Any feedback would be great, I would really like to improve Bunny to be more solid in its use of crypto.
you really need hmac or similar. someone else already said, but in more words: in a practical system (that has error handling etc) your current approach can allow someone to provide fake data. if they can trick you into repeating the same message (random iv doesn't help) then they can use padding to work out what bits of iv to flip to give any first block. and you including length in message only makes that easier (no second block error).
better: why not implement a lower level without crypto then layer a known good crypto on top? so just go for datagrams (udp), then add reliability (tcp), then add, say tls. I don't know much about this (sorry), but I bet once you have udp there are libraries to do almost all the rest.
Others have rightly mentioned HMAC as being a good option, but being approximately equal to the work of the CBC encryption.
There are peer-reviewed standard cipher modes that provide a cryptographic checksum with a low overhead per block plus one extra block cipher operation per message. These are generally known as single-pass authenticated encryption modes. GCM mode is probably your best option. If you're willing to rekey every 64 GB, are releasing your code under an OSI-approved Open Source license, and you're okay using a mode that's patented, OCB mode is another option.
Really, you don't want to use a static key. Ideally you'd either use the shared master key just to provide confidentiality (in case ECDH is broken) and authentication for ECDH session key agreement. Failing that, you'd use the shared master key and the current time to generate the session encryption keys in order to limit the amount of traffic under a given session key that an attacker can use.
session_counter = (AES256_enc(master_key, 0) + time()) | 0x0FFF; session_key = AES256_enc(master_key, session_counter); will generate for you a 128-bit key that changes every 1.13 hours (2^12 seconds). If an attacker can notice some traffic pattern that gives away when the rekey happens, this will give away the lower 12 bits of AES256_enc(master_key, 0), but this is very little plain text to work with. Note that due to the | 0x0FFF, it's impossible to accidentally use the leaked bits in a session key by using AES256_enc(master, 0) as a session key. If you want a 256-bit session key, it's best to use a second 256-bit shared key to encrypt session_counter to generate the second 128-bit half of your session key. Of course, in practice you'd pre-calculate when the session_counter rolls over rather than recomputing the session key for every packet. You either live with data being lost a few seconds every hour, synchronize clocks very well using GPS or NTP, or else calculate two sets of session keys and use the MACs to figure out which key the sender is using. When using two sets of session keys, you'd throw away the "expired" key and calculate the "upcoming" key 2^11 seconds after session_counter rolls over. (That is, you actually roll your pair of session keys half out of phase of when you'd roll a single session key.)
Edit: Changed to use C's xor operator for exponentiation since Python's double-asterisk notation doesn't display properly on HN.
Suggestion: Forget the modulus and remainder trick and just use a cryptographically strong MAC to decide what to ignore and what to process. Even a truncated HMAC.
Hmmm I like this trick, but that means I would have to process and decode each 802.11 as is comes in. It would create quite a bit of overhead for each packet.
Maybe I am mis-understanding you. Where would you put the MAC data in a packet? would in be broken up or just stuffed in to a single location for each packet?
> It would create quite a bit of overhead for each packet.
Do some benchmarking to prove that it's a serious problem first before rejecting "the right way" as too expensive.
> Where would you put the MAC data in a packet? would in be broken up or just stuffed in to a single location for each packet?
If you use a long enough MAC, say 160 bits, you can split it up and stuff the individual bits anywhere you like. You're the expert in hiding stuff in Wifi packets, not me!
But as it stands (MODULUS = N.NN, REMAINDER = 0.NN) only has 5 decimal digits = 16 bits of entropy. Bunny traffic could be identified after observing just a handful of packets (not even knowing the actual values for M and R).
Very good points. I did some benchmarks a while ago and found the the decode routines (un-optimized) were some of the slower pieces of code in round trip life of a packet. But with some work it could be done.
> But as it stands (MODULUS = N.NN, REMAINDER = 0.NN) only has 5 decimal digits = 16 bits of entropy. Bunny traffic could be identified after observing just a handful of packets (not even knowing the actual values for M and R).
Very true, I will add this to my TODO list, thanks.
You're sending the IV as part of the data, and the IV is not encrypted. The message being sent is time.time(), which is hugely predictable. The padding is easy to attack, and the inclusion of the IV means you'll get the first data block too.
What I meant is that using PKCS#5 padding ("N bytes of a value of N") will make it even more susceptible to a CBC oracle attack.
I'm not nearly qualified to talk about that stuff, but instead of requiring PyCrypto maybe you should require Keyczar (http://code.google.com/p/keyczar/) and not have to worry about padding, IVs etc
https://github.com/mothran/bunny/blob/master/proposal.txt :
> To detect a bunny specific packet, each outbound packet is resized to match pre determined pack_len % mod = remainder values. Then when looking for packet bunny just tests each packets length against the modulus and remainder values.
https://github.com/mothran/bunny/blob/master/libbunny/SendRe... :
https://github.com/mothran/bunny/blob/master/libbunny/config... : What?!