Search Unity

RSA cryptography causes large memory allocations

Discussion in 'Scripting' started by TwoTen, Oct 23, 2017.

  1. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Hello!
    We are building a multiplayer game, during the connection process we are exchanging AES keys. This is of course done with RSA. However, the RSA encryption is not too much of an issue as it's on the client during the connection process. But on the server, we are having large issues with Decryption performance. The decrypt method is allocating about 4.7MB of garbage on decrypt, this call alone takes 200+ ms to perform.

    What are the ways to solve this? The server needs to be able to continue operation. We did half the memory allocations by encrypting the AES key and salt in one RSA pass. But it's still not good enough.

    Here is a profiler screenshot.


    Here is the code we use to decrypt
    Code (CSharp):
    1.             using (RSA rsa = RSA.Create())
    2.             {
    3.                 rsa.ImportParameters(singleton.ServerPrivateKey);
    4.                 byte[] decryptedKey = rsa.Decrypt(combinedKey, RSAEncryptionPadding.OaepSHA1);
    5.                 Array.Copy(decryptedKey, keyBuffer, keyBuffer.Length);
    6.                 Array.Copy(decryptedKey, keyBuffer.Length, saltBuffer, 0, saltBuffer.Length);
    7.                 byte[] decryptedData = AESDecryptBytes(data, keyBuffer, saltBuffer);
    8.  
    9.                 using (MemoryStream stream = new MemoryStream(decryptedData))
    10.                 {
    11.                     GiveInfo giveInfo = Serializer.Deserialize<GiveInfo>(stream);
    12.                     giveInfo.valid = true;
    13.                     return giveInfo;
    14.                 }
    15.             }
    The following is with a keysize of 2048. If we use 4096 which we intend, we get to 500+ ms for the decryption and about 15.5 mb of garbage.
     
    Last edited: Oct 23, 2017
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    What's your KeySize when for the RSACryptoServiceProvider? Large values will be slower and require more memory (while giving increased security).

    Garbage is inevitably going to be generated during decryption, it's just the nature of the beast. I wouldn't worry myself about that so much. 4.7MB of garbage memory really isn't that scary on a server, unless you're doing this like 1000 times a second.

    The 200 ms+ delay can be annoying though, if you do this on the main thread. Since it halts the main thread... meaning all other messages processed on that thread can't be processed until you're done. So try starting a task/thread on which the job gets done, and return once complete. A single client requesting the server to handshake will take that time, but the server can also handle new requests during that time.
     
    TwoTen likes this.
  3. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    I am using 2048 when I got those results. We intend to use 4096 as we transfer credentials (Tokens). When using 4096 we get results of 500+ms and about 15MB of garbage.

    As for your suggestion, I had a similar idea. To simply to the decryption on a separate thread. And btw, I'm not that up to date on the Tasks. Got any good MSDN resources or similar?
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Well, tasks aren't directly supported in older versions of Unity. Only the new Unity 2017 versions of unity support it, and that's only if you target the appropriate .net library in the project settings.

    OR, there is a 3rd party library you can include in your older versions of unity that open up support to it. There's a thread somewhere here on the forums talking about it... I'd dig it up, but I'm not actually at my work computer and instead on a crappy laptop that makes such things honestly annoying for me to accomplish. Anyways, I personally haven't used it so can not speak to quarks or anything, but the user who put it together is a fairly good programmer so I'd give it an "untested thumbs up".

    As for how to use tasks... MSDN has many articles on it:
    https://msdn.microsoft.com/library/hh191443(vs.110).aspx

    Regardless of all this though, you don't need to use Tasks/async/await... you can just use the older ThreadPool:
    https://msdn.microsoft.com/en-us/library/system.threading.threadpool(v=vs.110).aspx

    As for returning to the main thread when using threadpool, one method I like is the method used by ThreadNinja:
    https://www.assetstore.unity3d.com/en/#!/content/15717

    This integrates with coroutines to easy jumping in/out of a background thread using ThreadPool. The library hasn't been updated in some time, but it's code backing is fairly simple and still works as far as I know... I don't personally use this library either though since I have my own implementation which essentially follows the same idea.
     
    TwoTen likes this.
  5. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Hm, thank you very much sir.

    For future readers this is the solution I came to:
    Code (CSharp):
    1.         Task<GiveInfo> task = new Task<GiveInfo>(delegate { return ServerSettings.GiveInfoDecrypted(bm.data, bm.combinedKey); });
    2.         task.Start();
    3.         while (!task.IsCompleted)
    4.             yield return null;
    5.         GiveInfo giveInfo = task.Result;
    6.         ServerEventHandler.OnGiveInfo(connectionId,
    7.             giveInfo.steamTicket,
    8.             giveInfo.valid,
    9.             giveInfo.clothingIndexes,
    10.             giveInfo.skinColor,
    11.             giveInfo.steamId,
    12.             giveInfo.matchmakingTicket);
    The decryption happens on a task, while the Coroutine yields until it's completed. This is because I need the continuation to happen on the main thread thus could not use another ContinueWith task.

    And once again, thanks a lot for your help. Performance is rock solid on the server again.
     
    lordofduct likes this.