Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Creating Secure Cryptographic Handshakes With Diffie Hellman

Discussion in 'Multiplayer' started by TwoTen, Jun 13, 2019.

  1. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    tobiass and Joe-Censored like this.
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Outside of some especially popular games, do you have any information on the importance of network security for game data?

    My anecdotal information is cheaters don't generally write their own cheats targeting network packets, but will buy cheats that do so from cheat vendors. Cheat vendors generally target only the most popular of games, and when you get that popular you would generally have the funds to overhaul your networking system with greater security. Am I off base?

    Thanks for this write up by the way.

    Also, what overhead are you seeing with encryption enabled on your MLAPI?
     
  3. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Network security does not offer ANY protection against cheaters. A cheater can still send whatever packet they want.

    If you are however sending say, a steam ticket, or other authentication material. It is of the HIGHEST importance to encrypt said packet. Not doing this can even be illegal in some countries. In addition, once the connection is authenticated, it's also good to encrypt and authenticate packets such as "purchase from shop" packets, but it's not of any significance to encrypt say movement packets.

    This is why encryption is important. It's not about protecting yourself, but it's about protecting your USERS. They will be your most valuable asset.


    Encryption ensures noone can read the message, potentially stealing credentials for later (re)use. Authentication (I am talking HMAC type of authentication, I.E cryptographic authentication) proves who sent the message (provided that the key exchange was signed, thus not broken)

    As for overhead, the MLAPI uses MLAPI.Cryptography for handshakes. Rijndael 256 (AES) for encryption and HMAC SHA256 for authentication. The actual CPU encryption overhead is basically zero, it's just so fast. But there is a bandwidth overhead as we do send IV's and the HMAC along with the packet. You can check the implementation here.

    The only computationally expensive task really is the handshakes which takes ~15-20 ms on my i7 7700k @4.2GHz. So you really want to move that compute off the main thread.
     
    Last edited: Jun 14, 2019
    Joe-Censored likes this.
  4. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Not sure I agree with this. There are times in your game that it is necessary (or possibly just convenient) to send data to the clients about things happening in the game which they should discover on their own in game, instead of immediately being alerted to based on the contents of the communication. Unless you were referring to the fact that the game client itself already has everything needed to decrypt the messages, so there's really no way to protect them from someone sufficiently skilled and equally motivated.

    In my game I of course am encrypting credentials, but I've been using it to make reading and manipulating network packets more difficult. As a second layer of defense against an attempt to attack the game servers (first layer is obviously doing as much input verification on the server as I can) More than half of the data sent in my game is encrypted. I encrypt all RPC's, chat messages, synced variables, and all transform updates for objects within a specific range of the player.

    Would your opinion be that this is a fools errand?

    I'm using a much weaker encryption algorithm than you're using (XOR obfuscation with a randomized key per connection). When I tested max loopback throughput I got about a 5-10% drop in messages I could process per second with this enabled, which I found acceptable. If you're getting that good of performance from AES though I'll run some of my own tests on it as well. I had avoided AES or other modern encryption algorithms, because I assumed they were performance prohibitive, and went with what I thought would have the best performance even if not particularly strong.
     
  5. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    I don't quite understand what you are saying.

    What I was trying to say was, encrypting packets from the game client to then game server does not prevent people sending whatever packets they like.
    If you are encrypting credentials, how are you sharing the crypto key? That's what diffie hellman does for you.
    As for using encryption to prevent people from easily just peeking at wireshark to read your messages. You are dealing with security through obscurity, and very poor such.

    Security through obscurity HAS it's place. Look at snapchat for example, they use it heavily to make it VERY hard to use their API's from non trusted environments. However, this should only be added to things that CANNOT be secured in a "proper" way. Thus, sharing credentials SHOULD be done properly, and if you need additional security through obscurity on top of that, thats fine, even if my personal oppinion is that it's moslty a waste of your time, and not so much waste of the attackers time.

    Another good implementation of security through obscurity is recaptcha by Google. It uses a runtime language that is produced at runtime and then evalulated. (Yes... A language is being created, code generated for that langauge and then evalulated. And it constantly changes).

    I have reverse engineered many systems, VPN networks, game clients and much more. These developer tricks are usually what makes it enjoyable, gives you a good laugh. You really aren't protecting your products. Even large corporations have very laughable implementations, a few years back I managed to reverse Photoshops key generation, to be able to produce as many licence keys as I want (these programs are known as keygens). If I can reverse Adobes effort, I'm afraid your efforts are pretty much just wasting your development time and giving any future attacker a good laugh once they cracked it.

    What is important is that when you send credentials to authenticate against the server, the client knows 100% that noone else read that message, and if someone gets all the keys involved in the future (with the exception of the encryption key, so that would be the signing keys, private and public), that they CANNOT get hold of those credentials. This can only be ensured with a signed key exchange.

    First, XOR is NOT obfuscation, it's encryption. XOR is very good encryption, but not on its own. AES is built on XOR, and so are pretty much every algorithm. The problem with XOR is that you need a steady, fully random key stream, reusing keys or anything of the sorts will result in patterns in the encrypted payload. Allowing you to analyze it and hopefully grab useful data, or even the key. If you want to try it for yourself, try encrypting ASCII by hand. You can clearly see that it's ASCII by the prefixes and the pattern ASCII uses.

    AES is blazing fast, most modern chips has built in instructions for it to make it accelerated (https://en.wikipedia.org/wiki/AES_instruction_set). As for raw performance, from wikipedia:

    Remember, thats all single threaded. Thread it up and you will have no issues. Pretty much all network traffic, including HTTPS is done via AES. You can also opt for more state of the art algorithms such as ChaCha20, but AES is more than enough.

    In addition, in all of this. It's important to not forget to authenticated the messages with a HMAC to ensure they have not been tampered with.
     
    Last edited: Jun 14, 2019
    Joe-Censored likes this.
  6. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Also, for more understanding. I just had a look in the security part of this website in Firefox. This is what it shows:
    upload_2019-6-14_20-46-14.png

    The important bit here is under Technical Details, inside the parenthesis. Let's break it down.

    First part: ECDHE, Elliptic Curve Diffie Hellman Ephemeral. Just means diffie hellman key exchange, but instead of doing modulo math it was done using elliptic curves (much faster). Ephemeral just meaning a random key. This is what MLAPI.Cryptography would do for you.

    Second Part: ECDSA, Elliptic Curve DSA. DSA being a assymetric algorithm (similar to the more well known RSA). DSA can benefit from using Elliptic Curve math instead of mudolo math, that's why it's prefixed EC.

    What do we know this far? The website and my web browser performed a elliptic curve key exchange, the key exchange was signed with DSA to prevent MITM attacks.

    Ever seen this?


    That just means the signature was invalid for the key exchange. Thus a MITM attack is being done OR the server has a messed up configuration. (In the above example you can see it says CERT_AUTHORITY_INVALID, that just mean the certificate authority was not trusted by the browser. So it could either mean that an attacker is doing a MITM and is just sending some nonsense certificate, hoping that the user will ignore the error. Or the website is imroperly configured. Or the certificate authority list on the browser might be outdated. But you get the idea).


    Now, the details continue with "WITH_AES_256_GCM_SHA384_256 bit key". What does that mean?

    Simple, once the key was established, the data that had to be sent between the two parties was encrypted with AES 256. Aes being set to GCM mode. Read about them here: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

    Next, "SHA384_256 bit key". Well, what this actually means is "HMAC SHA384". That is, the packet was also authenticated to prove that it was not tampered with and is legitimate. The key of the HMAC was 256 bit.
     
    Last edited: Jun 14, 2019
    Joe-Censored likes this.
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    While you can send whatever packets you like, adding your own CRC checks can weed out most random garbage (HMAC in your case even better), and encryption ups the difficulty of crafting packets which result in some perceived benefit to whoever is crafting the packets. It also increases the difficulty of analyzing network packets for useful information in real time by the player outside of the game. That's what I was getting at.

    In the latter as an example, PUBG has a big problem right now with "radar hacks" where network traffic is analyzed on a separate machine and points out where players are hiding and useful loot is located.

    I'm not using a particularly good system at the moment. I'm definitely looking at diffie hellman. Currently I have a key hard coded into the build which I manually change on every version, and in the initial connection negotiation I send a randomized key to the client, in a message encrypted with that hard coded key.

    Yes of course it is security through obscurity. As far as my in game network traffic is concerned, I've done my best to ensure there are as close to zero ways to manipulate the game even without any encryption, seeing that I originally started on Unet so encrypting most game traffic was't practical at the time. The encryption in this context is just an additional stumbling block.

    Good info.
     
    TwoTen likes this.
  8. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Unless you are facing those problems, you are probably wasting your time.

    Yea, you need a proper key exchange if you are sending credentials.


    My general rule of thumb is: 1. Make your code server authoritive, this is just too much of a pain to change if you start out client authoritive. 2. Do proper encryption for credentials. 3. Dont worry about obscure problems like you have proposed until they become a problem. You can have them in mind for sure, but focus your development on the first two points, and making your game great BEFORE anything else.
     
  9. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    823
    Hey @TwoTen,
    Thanks for the library (https://github.com/MidLevel/MLAPI.Cryptography). Would you still consider your repo as a viable solution? There hasn't been a lot of activity as far as I can tell, but that's also not necessarily necessary for a crypto library. Also, can I make your library in any way compatible with .NET's ECDiffieHellmanCng?
    From your comment I figured this might be a viable setup:
    Code (CSharp):
    1. diffieHellman = new ECDiffieHellmanCng();
    2. diffieHellman.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hmac;
    3. diffieHellman.HashAlgorithm = CngAlgorithm.Sha1;
    However, what do I need to pass for bytes, iterations and salt for your method GetSharedSecretStretchedToBytes?
     
  10. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Sure you can still use it. Nothing has changed because I feel the library has everything I needed.

    As for using .NET's ECDiffieHellmanCng. That does not work in Unity (at least when I last tried), hence why I created the library in the first place.
     
  11. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    823
    Thanks for the response,

    yeah I know that I can't use ECDiffieHellmanCng in Unity, but I would like to use it on the server side (which is a .NET application) :)
     
  12. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Oh. I see. I think it should work tbh. But it's honestly easier to just just the same lib on both ends. It might be that the API's are exposing different (and possibly processed) versions of the diffie hellman internals.
     
  13. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    823
    yeah, I guess .NET behaves somewhat different (the first 4 bytes do not store any size as far as I can tell, since the generated array gets really large when trying to read it with this lib). I guess I will stick to your library for both ends. Hopefully the allocations won't be too big of a problem, I'll see.
     
    Last edited: May 14, 2021