• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
MatsK

Cryptography - am I doing it right?

27 posts in this topic

I'm trying to establish a secure connection in C#, and I'm not sure I'm doing it right. :(

 

Here is my encryption class:

/*The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.

The Original Code is the GonzoNet.

The Initial Developer of the Original Code is
Mats 'Afr0' Vederhus. All Rights Reserved.

Contributor(s): ______________________________________.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace GonzoNet.Encryption
{
    public class ARC4Encryptor : Encryptor
    {
        private DESCryptoServiceProvider m_CryptoService = new DESCryptoServiceProvider();
        private ICryptoTransform m_DecryptTransformer, m_EncryptTransformer;
        public byte[] EncryptionKey;

        public ARC4Encryptor(string Password)
            : base(Password)
        {
            PasswordDeriveBytes Pwd = new PasswordDeriveBytes(Encoding.ASCII.GetBytes(Password), 
                Encoding.ASCII.GetBytes("SALT"), "SHA1", 10);
            EncryptionKey = Pwd.GetBytes(8);
            m_DecryptTransformer = m_CryptoService.CreateDecryptor(EncryptionKey, Encoding.ASCII.GetBytes("@1B2c3D4e5F6g7H8"));
            m_EncryptTransformer = m_CryptoService.CreateEncryptor(EncryptionKey, Encoding.ASCII.GetBytes("@1B2c3D4e5F6g7H8"));
        }

        public ARC4Encryptor(string Password, byte[] EncKey)
            : base(Password)
        {
            PasswordDeriveBytes Pwd = new PasswordDeriveBytes(Encoding.ASCII.GetBytes(Password),
                Encoding.ASCII.GetBytes("SALT"), "SHA1", 10);
            EncryptionKey = EncKey;
            m_DecryptTransformer = m_CryptoService.CreateDecryptor(EncryptionKey, Encoding.ASCII.GetBytes("@1B2c3D4e5F6g7H8"));
            m_EncryptTransformer = m_CryptoService.CreateEncryptor(EncryptionKey, Encoding.ASCII.GetBytes("@1B2c3D4e5F6g7H8"));
        }

        public override DecryptionArgsContainer GetDecryptionArgsContainer()
        {
            DecryptionArgsContainer DArgsContainer = new DecryptionArgsContainer();
            DArgsContainer.ARC4DecryptArgs = new ARC4DecryptionArgs();
            DArgsContainer.ARC4DecryptArgs.Transformer = m_DecryptTransformer;

            return DArgsContainer;
        }

        public override byte[] FinalizePacket(byte PacketID, byte[] PacketData)
        {
            MemoryStream FinalizedPacket = new MemoryStream();
            BinaryWriter PacketWriter = new BinaryWriter(FinalizedPacket);

            MemoryStream TempStream = new MemoryStream();
            CryptoStream EncryptedStream = new CryptoStream(TempStream,
                m_EncryptTransformer, CryptoStreamMode.Write);
            EncryptedStream.Write(PacketData, 0, PacketData.Length);
            EncryptedStream.FlushFinalBlock();

            PacketWriter.Write(PacketID);
            //The length of the encrypted data can be longer or smaller than the original length,
            //so write the length of the encrypted data.
            PacketWriter.Write((ushort)((long)PacketHeaders.ENCRYPTED + TempStream.Length));
            //Also write the length of the unencrypted data.
            PacketWriter.Write((ushort)PacketData.Length);
            PacketWriter.Flush();

            PacketWriter.Write(TempStream.ToArray());
            PacketWriter.Flush();

            byte[] ReturnPacket = FinalizedPacket.ToArray();

            PacketWriter.Close();

            return ReturnPacket;
        }

        public override MemoryStream DecryptPacket(PacketStream EncryptedPacket, DecryptionArgsContainer DecryptionArgs)
        {
            CryptoStream CStream = new CryptoStream(EncryptedPacket, m_DecryptTransformer, CryptoStreamMode.Read);

            byte[] DecryptedBuffer = new byte[DecryptionArgs.UnencryptedLength];
            CStream.Read(DecryptedBuffer, 0, DecryptedBuffer.Length);

            return new MemoryStream(DecryptedBuffer);
        }
    }
}

Then I initialize it as such on the client and server side:

            //Doing the encryption this way eliminates the need to send key across the wire! :D
            SaltedHash Hash = new SaltedHash(new SHA512Managed(), Args.Username.Length);
            byte[] HashBuf = Hash.ComputePasswordHash(Args.Username, Args.Password);
            Args.Enc = new GonzoNet.Encryption.ARC4Encryptor(Convert.ToBase64String(HashBuf));

Then when establishing the secure connection, I'm sending the hash across. What I'm wondering is: Could the hash be used to generate the key by a man-in-the-middle attacker?

If so, would it help to Blowfish the hash using itself as they key, so that if the hash stored in the DB is correct, it will be able to decrypt itself?

Should I just rewrite the protocol using Diffie-Helllman?

0

Share this post


Link to post
Share on other sites

I'm deeply concerned about how you're managing passwords in this code, authentication should be well-separated from encryption. Just use SSL. Please. It isn't worth it.

0

Share this post


Link to post
Share on other sites
Hplus pretty much already said everything important, but to give you an idea what's also wrong: DES is way outdated ( one might argue that it was designed to be insecure). Also, never use an encryption key twice. You are at least reusing it for every session, maybe even for every packet.
0

Share this post


Link to post
Share on other sites

Thanks guys! :)

I've had a think, and I've decided that using TLS isn't neccessary. This is an open source project, so people can figure out the protocol either way. And there's no way they can retrieve the password from the hash.

However I won't remove the encryption currently in place, because its been a learning experience. I'll switch to using RSA key exchange just because I can. :)

 

0

Share this post


Link to post
Share on other sites

Thanks guys! smile.png

I've had a think, and I've decided that using TLS isn't neccessary. This is an open source project, so people can figure out the protocol either way. And there's no way they can retrieve the password from the hash.

However I won't remove the encryption currently in place, because its been a learning experience. I'll switch to using RSA key exchange just because I can. smile.png

 

Ignoring absolutely all of the advice in this thread is your call, but "people can figure out the protocol either way", "there's no way they can retrieve the password from the hash", "it's been a learning experience", and "just because I can" really aren't good ways to write security-related code, and don't inspire confidence. There's a reason it's recommended not to roll your own. Learning experiences in cryptography should be confined to personal experimentation, as soon as you're handling user information you are morally and ethically obligated* to secure it to the best of your ability, and if that means using existing, industry-standard, proven technology or contracting an expert to implement or audit your code, so be it. Just be aware that by making this choice, you are almost certainly not acting in your or your users' best interest.

 

* depending on what your program does, you may in fact be legally required to submit such code for an audit and be held legally responsible for protecting your users' privacy, whoever that may be. This probably doesn't apply in your case, though, usually this applies to banking authentication and transactions, storing employee payroll information, and so on, but it is worth keeping in mind that if a significant data breach occurs, and a company is found to have not acted responsibly by using a home-made security backend, things can occasionally become very, very unpleasant for them.

1

Share this post


Link to post
Share on other sites

Huh?

The first thing hplus asked me was "is this really an attack you need to worry about?"

I thought about it, and I decided "no".

Also, are you suggesting that hashing a password with SHA512 isn't safe (enough?) to send in plaintext, and it needs to be encrypted in addition?

Lastly, if I'm going to use encryption at all, what's wrong with using RSA key exchange?

 

The main reason I really don't want to use TLS is because it just seems overly complex. I did some research, and I haven't been able to figure out how to generate a "self signed root certificate". Can I do this programmatically?

0

Share this post


Link to post
Share on other sites

are you suggesting that hashing a password with SHA512 isn't safe (enough?) to send in plaintext


That's not the problem. The problem is that, if someone sniffs the hash of the password, they can re-play that login to your servers, and pretend that they know the password, because they know the hash of the password.

Designing cryptosystems is really hard. I suggest you read a good book, if you actually want to know about these things. There are a variety of books, some worse than others, but you can get started with an introduction for free:
http://en.wikibooks.org/wiki/Cryptography
And then perhaps something reasonable-well rated like this:
http://www.amazon.com/gp/product/0470474246/ref=as_li_ss_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=0470474246&linkCode=as2&tag=enchage-20

To use TLS, download OpenSSL, and follow the documentation :-)

If all you want to do is scramble bits on a wire, then use AES, and hard-code the key in both the client and the server. (Suggested key; "swordfish")
2

Share this post


Link to post
Share on other sites

Then would it not be safer to encrypt everything in the first packet BUT the accountname with AES, so that if the accountname's corresponding hash in the DB can descramble the packet, that hash can be used to encrypt communication onwards? Why/why not?

0

Share this post


Link to post
Share on other sites

Thanks, that was a very thoughtful response! :)

I will spend the weekend trying to fork out some code that can implement this protocol. I think I already found relevant example code for C# [url=http://lapuinka.blogspot.no/2012/10/elliptic-curve-diffiehellman.html]here[/url].

 

Couple of questions though:

 

You say the server's message is encrypted using the server's private key. Does this mean the client can decrypt it using the server's public key? o_O

So far I've been using automatic account generation for signups (because I didn't want to use my server as a web server as well), but that probably isn't a good idea.

Anyway, can I hash the hashes currently stored in the DB and generate public keys from them?

0

Share this post


Link to post
Share on other sites

Then would it not be safer to encrypt everything in the first packet BUT the accountname with AES, so that if the accountname's corresponding hash in the DB can descramble the packet, that hash can be used to encrypt communication onwards? Why/why not?


So, are you saying that you're storing hash(password) in the database, and compute hash(password) on the client, and then encrypt the first packet, except for the account name, using hash(password) as key?

That's better, but it's not particularly good, because now, someone can just take a dump of your database (which contains hash(password) mapped to customer-name) and impostor any customer name, without knowing the password -- they don't need to know the password; they just need to know hash(password)!

As suggested above, if you use public/private keys, you're much better off. TLS/SSL does this for you.
0

Share this post


Link to post
Share on other sites


I will spend the weekend trying to fork out some code that can implement this protocol.

 

I would still be concerned that it doesn't actually solve your security problems.

 

You are looking for an encrypted communications channel. That is analogous to hiring an armored vehicle delivery service. Very nice, you have protected one thing. On your server side you have a nice environment that you control. The armored car will be delivering the package to BadGuysRUs, and will be returning a package using the same armored vehicle service.

 
Let's assume you do implement a rock-solid TLS implementation.  Communication between the two endpoints is moderately secure; TLS interception proxies are very common in corporate, academic, and other computing environments. Your connection with the corporate firewall is secure, they re-encode it with the company-mandated certificate, and it works just fine.  Using your own encryption system is much more effort and is extremely difficult to get right, but it can get through a TLS proxy securely.  That is just one item on a long list of security.
 
I have also heard security described as a fence. Communications encryption is a single fencepost. It is high and very strong, but still just a single post that can be easily walked around.
 
That kind of encryption does have many valid uses. If you are talking with a bank to check your account balance and do transfers, it makes it difficult to eavesdrop on the session to learn your bank balance or to add some extra money to a money transfer. The bank assumes that you are in charge of your end of the connection; they don't have protection against keyloggers or compromised web browsers or someone with a gun to your head. It only protects in-transit communication, but for banking that is the critical part.
 
 
Before trying to secure your application, you need to figure out exactly what attacks you are trying to prevent. Encrypting the communication reduces the risk of a "robbing the stagecoach" type of attack. While it is more common for financial institutions, that type of attack is extremely rare in games. The most common attack is aimed against the client itself; why attack the stagecoach when you have complete ownership of one endpoint? The bad guys can just attach a debugger or run inside a sandbox environment and have perfect access to everything.
1

Share this post


Link to post
Share on other sites
You say the server's message is encrypted using the server's private key. Does this mean the client can decrypt it using the server's public key?

Yes, that is how public key cryptography works. It takes the public key to read what the private key has encrypted, and vice versa. There are different sytems, some use exponentiation modulo a prime (with huge numbers) and others use multiplication on an elliptic curve or Edwards curve or ... something else. The trick behind all these is that some operation is easy to perform and commutative, but hard to invert.

I will explain it using multiplication, the way it works with ECDH. Since both "public" and "private" start with "p" I will refer to the private key as the "secret" key, so it will have the identifier S. The public key will of course have the identifier P.

Person 1 and person 2 want to talk to each other. Both have agreed on some curve parameters and a deliberately chosen base point B. These are conventions that are necessary so it will work (everybody must do the same calculations, after all!). It does not matter a lot what they choose, as long as they agree.

Person 1 has the secret private key S1 and calculates his private key P1 = B*S1. Person 2 does the same, calculating P2=B*S2.

Now, the public keys are, well... public. That means you can tell the other person that key, or you could even put it on your website where everybody can look it up. So, person 1 knows person 2's public key (but not the private one) and vice versa.

Now person 1 can calculate S1*P2, which is really S1*(B*S2), and person 2 can calculate S2*P1, which is really S2*(B*S1). Thus, they both know B*S1*S2, which they can use as key for a symmetric cipher. None of them knows the other's P.

The trouble is making sure that when someone tells you "this is my public key" it's really the correct one. Someone who intercepts your traffic might exchange your partner's key with his own and do the same on the other end. This is called the Man-in-the-middle attack. One way to thwart that is by signing keys (works that way in TLS), but of course simply embedding the key into the game executable works just fine, too. The obvious disadvantage is that it isn't nearly as flexible or maintainable, but on the other hand it is dead simple. Since you already know the correct key, there is no way someone who is sitting on the network between you and the server could tell you the wrong one (well, unless they infect your computer with malware that patches the executable, bleh).

Edited by samoth
1

Share this post


Link to post
Share on other sites

 


I will spend the weekend trying to fork out some code that can implement this protocol.

 

I would still be concerned that it doesn't actually solve your security problems.

 

You are looking for an encrypted communications channel. That is analogous to hiring an armored vehicle delivery service. Very nice, you have protected one thing. On your server side you have a nice environment that you control. The armored car will be delivering the package to BadGuysRUs, and will be returning a package using the same armored vehicle service.

 
Let's assume you do implement a rock-solid TLS implementation.  Communication between the two endpoints is moderately secure; TLS interception proxies are very common in corporate, academic, and other computing environments. Your connection with the corporate firewall is secure, they re-encode it with the company-mandated certificate, and it works just fine.  Using your own encryption system is much more effort and is extremely difficult to get right, but it can get through a TLS proxy securely.  That is just one item on a long list of security.
 
I have also heard security described as a fence. Communications encryption is a single fencepost. It is high and very strong, but still just a single post that can be easily walked around.
 
That kind of encryption does have many valid uses. If you are talking with a bank to check your account balance and do transfers, it makes it difficult to eavesdrop on the session to learn your bank balance or to add some extra money to a money transfer. The bank assumes that you are in charge of your end of the connection; they don't have protection against keyloggers or compromised web browsers or someone with a gun to your head. It only protects in-transit communication, but for banking that is the critical part.
 
 
Before trying to secure your application, you need to figure out exactly what attacks you are trying to prevent. Encrypting the communication reduces the risk of a "robbing the stagecoach" type of attack. While it is more common for financial institutions, that type of attack is extremely rare in games. The most common attack is aimed against the client itself; why attack the stagecoach when you have complete ownership of one endpoint? The bad guys can just attach a debugger or run inside a sandbox environment and have perfect access to everything.

 

 

Again, this game is open source, so attackers already have an open playing field. My main concern is to try to prevent attacks of the type where "robbing the stagecoach" will allow men in the middle to get away with users' passwords. Now, I know that social engineering is a much more effective way to get users' passwords, but I can't do anything about that. I can however protect against MiM attacks.

I'm also not using TLS, I'm taking samoth's advice and trying to aim for Elliptic Curve Diffie Hellman. I'll probably post the code here for peer review, and if it doesn't look good, I'll just revert to hplus' advice and use AES.

 

hplus0603: It would actually be physically impossible for anyone to take a dump of my DB! :) At least as of yet, because I'm running the login server and city (game) server on the same box, and thus the DB isn't connected to the intarwebs.

0

Share this post


Link to post
Share on other sites

Again, this game is open source, so attackers already have an open playing field.


That's a common misconception, and it's very dangerous. Open, auditable implementations makes software *more* secure. OpenSSL is a very secure TLS implementation, and has been open source since day 1.
0

Share this post


Link to post
Share on other sites

 

That's better, but it's not particularly good, because now, someone can just take a dump of your database (which contains hash(password) mapped to customer-name) and impostor any customer name, without knowing the password -- they don't need to know the password; they just need to know hash(password)!

 

if they can already get a dump of your entire database, i think there are more problems to worry about then the person being able to simply act as an imposter of a customer.

0

Share this post


Link to post
Share on other sites

if they can already get a dump of your entire database, i think there are more problems to worry about then the person being able to simply act as an imposter of a customer


This is also a common, and dangerous, misconception about system security.
0

Share this post


Link to post
Share on other sites

Ok, time for peer review!

Here's my code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using GonzoNet;

namespace CryptoSample
{
    public class PacketHandlers
    {
        //Not sure why, but both keys must derive from the same master for decryption to work.
        private static CngKey MasterKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
        public static ECDiffieHellmanCng ClientKey = new ECDiffieHellmanCng(MasterKey), 
            ServerKey = new ECDiffieHellmanCng(MasterKey);

        private static byte[] SessionKey, IV;
        private static Guid ChallengeResponse = Guid.NewGuid();
        public static Guid ClientNOnce = Guid.NewGuid();

        //This will be generated when the client sends the first packet.
        public static byte[] ClientIV;

        /// <summary>
        /// Helper method to generate an Initialization Vector for the client.
        /// </summary>
        public static void GenerateClientIV()
        {
            AesCryptoServiceProvider AES = new AesCryptoServiceProvider();
            AES.GenerateIV();
            ClientIV = AES.IV;
        }

        /// <summary>
        /// A client requested login.
        /// </summary>
        /// <param name="Client">NetworkClient instance.</param>
        /// <param name="Packet">ProcessedPacket instance.</param>
        public static void InitialClientConnect(NetworkClient Client, ProcessedPacket Packet)
        {
            Console.WriteLine("Server receives data - test 1");

            //AES is used to encrypt all further communication between client and server.
            AesCryptoServiceProvider AesCrypto = new AesCryptoServiceProvider();
            AesCrypto.GenerateKey();
            AesCrypto.GenerateIV();
            AES AesEncryptor = new AES(AesCrypto.Key, AesCrypto.IV);
            SessionKey = AesCrypto.Key;
            IV = AesCrypto.IV;

            Guid Nonce = new Guid(Packet.ReadPascalString());

            //Username would normally be used to lookup client's public key in DB (only time such a use is valid).
            string Username = Packet.ReadPascalString();
            ECDiffieHellmanPublicKey ClientPub = StaticStaticDiffieHellman.ImportKey("ClientPublic.dat");

            PacketStream EncryptedPacket = new PacketStream(0x02, 0);
            EncryptedPacket.WriteHeader();

            MemoryStream StreamToEncrypt = new MemoryStream();
            BinaryWriter Writer = new BinaryWriter(StreamToEncrypt);
            Writer.Write((byte)ChallengeResponse.ToByteArray().Length);
            Writer.Write(ChallengeResponse.ToByteArray(), 0, ChallengeResponse.ToByteArray().Length);
            Writer.Write((byte)SessionKey.Length);
            Writer.Write(SessionKey, 0, SessionKey.Length);
            Writer.Write((byte)IV.Length);
            Writer.Write(IV, 0, IV.Length);
            Writer.Flush();

            byte[] EncryptedData = StaticStaticDiffieHellman.Encrypt(ServerKey, ClientPub, Nonce.ToByteArray(), 
                StreamToEncrypt.ToArray());

            EncryptedPacket.WriteUInt16((ushort)(PacketHeaders.UNENCRYPTED + 1 + EncryptedData.Length));

            EncryptedPacket.WriteByte((byte)EncryptedData.Length);
            EncryptedPacket.Write(EncryptedData, 0, EncryptedData.Length);

            Client.Send(EncryptedPacket.ToArray());

            Console.WriteLine("Test 1: passed!");
        }

        /// <summary>
        /// Initial response from server to client.
        /// </summary>
        /// <param name="Client">A NetworkClient instance.</param>
        /// <param name="Packet">A ProcessedPacket instance.</param>
        public static void HandleServerChallenge(NetworkClient Client, ProcessedPacket Packet)
        {
            Console.WriteLine("Client receives encrypted data - test 2");

            byte[] PacketBuf = new byte[Packet.ReadByte()];
            Packet.Read(PacketBuf, 0, (int)PacketBuf.Length);

            ECDiffieHellmanPublicKey ServerPub = StaticStaticDiffieHellman.ImportKey("ServerPublic.dat");

            MemoryStream DecryptedStream = new MemoryStream(StaticStaticDiffieHellman.Decrypt(ClientKey, ServerPub, 
                ClientNOnce.ToByteArray(), PacketBuf));
            BinaryReader Reader = new BinaryReader(DecryptedStream);

            Guid ChallengeResponse = new Guid(Reader.ReadBytes(Reader.ReadByte()));
            SessionKey = Reader.ReadBytes(Reader.ReadByte());
            IV = Reader.ReadBytes(Reader.ReadByte());

            //Yay, we have key and IV, we can now start encryption with AES!
            AES AesEncryptor = new AES(SessionKey, IV);

            PacketStream EncryptedPacket = new PacketStream(0x03, 0);
            EncryptedPacket.WriteHeader();

            MemoryStream StreamToEncrypt = new MemoryStream();
            BinaryWriter Writer = new BinaryWriter(StreamToEncrypt);
            Writer.Write((byte)ChallengeResponse.ToByteArray().Length);
            Writer.Write(ChallengeResponse.ToByteArray(), 0, ChallengeResponse.ToByteArray().Length);

            //Encrypt data using key and IV from server, hoping that it'll be decrypted correctly at the other end...
            byte[] EncryptedData = AesEncryptor.Encrypt(StreamToEncrypt.ToArray());

            EncryptedPacket.WriteUInt16((ushort)(PacketHeaders.UNENCRYPTED + EncryptedData.Length + 1));
            EncryptedPacket.WriteByte((byte)EncryptedData.Length);
            EncryptedPacket.Write(EncryptedData, 0, EncryptedData.Length);

            Client.Send(EncryptedPacket.ToArray());

            Console.WriteLine("Test 2: passed!");
        }

        public static void HandleChallengeResponse(NetworkClient Client, ProcessedPacket Packet)
        {
            Console.WriteLine("Server receives challenge response - test 3");

            byte[] PacketBuf = new byte[Packet.ReadByte()];
            Packet.Read(PacketBuf, 0, (int)PacketBuf.Length);

            AES AesEncryptor = new AES(SessionKey, IV);
            MemoryStream DecryptedStream = new MemoryStream(AesEncryptor.Decrypt(PacketBuf));
            BinaryReader Reader = new BinaryReader(DecryptedStream);

            byte[] CResponseBuf = Reader.ReadBytes(Reader.ReadByte());
            Guid CResponse = new Guid(CResponseBuf);

            if (CResponse.CompareTo(ChallengeResponse) == 0)
                Console.WriteLine("Received correct challenge response, client was authenticated!");

            Console.WriteLine("Test 3: passed!");
        }
    }
}

ECDH:

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.IO;

namespace CryptoSample
{
    /// <summary>
    /// Contains methods for en/decryption and ex/importing keys.
    /// From: http://stackoverflow.com/questions/3196297/minimal-message-size-public-key-encryption-in-net
    /// </summary>
    public static class StaticStaticDiffieHellman
    {
        private static Aes DeriveKeyAndIv(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce)
        {
            privateKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
            privateKey.HashAlgorithm = CngAlgorithm.Sha256;
            privateKey.SecretAppend = nonce;
            byte[] keyAndIv = privateKey.DeriveKeyMaterial(publicKey);
            byte[] key = new byte[16];
            Array.Copy(keyAndIv, 0, key, 0, 16);
            byte[] iv = new byte[16];
            Array.Copy(keyAndIv, 16, iv, 0, 16);

            Aes aes = new AesManaged();
            aes.Key = key;
            aes.IV = iv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            return aes;
        }

        public static byte[] Encrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] data)
        {
            Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
            return aes.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
        }

        public static byte[] Decrypt(ECDiffieHellmanCng privateKey, ECDiffieHellmanPublicKey publicKey, byte[] nonce, byte[] encryptedData)
        {
            Aes aes = DeriveKeyAndIv(privateKey, publicKey, nonce);
            return aes.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
        }

        public static void ExportKey(string Path, ECDiffieHellmanPublicKey Key)
        {
            using (BinaryWriter Writer = new BinaryWriter(File.Create(Path)))
            {
                Writer.Write((byte)Key.ToByteArray().Length);
                Writer.Write(Key.ToByteArray());
            }
        }

        public static ECDiffieHellmanPublicKey ImportKey(string Path)
        {
            ECDiffieHellmanPublicKey Key;
            using (BinaryReader Reader = new BinaryReader(File.Open(Path, FileMode.Open)))
            {
                Key = ECDiffieHellmanCngPublicKey.FromByteArray(Reader.ReadBytes(Reader.ReadByte()), CngKeyBlobFormat.EccPublicBlob);
                return Key;
            }
        }
    }
}

AES:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CryptoSample
{
    using System;
    using System.Text;
    using System.Security.Cryptography;

    /// <summary>
    /// AES class is derived from the MSDN .NET CreateEncryptor() example
    /// http://msdn.microsoft.com/en-us/library/09d0kyb3.aspx
    /// </summary>
    class AES
    {
        // Symmetric algorithm interface is used to store the AES service provider
        private SymmetricAlgorithm AESProvider;

        // Crytographic transformers are used to encrypt and decrypt byte arrays
        private ICryptoTransform encryptor;
        private ICryptoTransform decryptor;

        /// <summary>
        /// Constructor for AES class that takes byte arrays for the key and IV
        /// </summary>
        /// <param name="key">Cryptographic key</param>
        /// <param name="IV">Cryptographic initialization vector</param>
        public AES(byte[] key, byte[] IV)
        {
            // Initialize AESProvider with AES service provider
            AESProvider = new AesCryptoServiceProvider();

            // Set the key and IV for AESProvider
            AESProvider.Key = key;
            AESProvider.IV = IV;

            // Initialize cryptographic transformers from AESProvider
            encryptor = AESProvider.CreateEncryptor();
            decryptor = AESProvider.CreateDecryptor();
        }

        /// <summary>
        /// Constructor for AES class that generates the key and IV from salted passwords
        /// </summary>
        /// <param name="keyPassword">Password used to generate the key</param>
        /// <param name="IVPassword">Password used to generate the IV</param>
        /// <param name="salt">Salt used to secure the passwords</param>
        public AES(string keyPassword, string IVPassword, string salt)
        {
            // Initialize AESProvider with AES service provider
            AESProvider = new AesCryptoServiceProvider();

            // Initialize a hasher with the default MD5 algorithm
            MD5 md5 = System.Security.Cryptography.MD5.Create();

            // Generate the key and IV for AESProvider from hashed, salted passwords
            AESProvider.Key = md5.ComputeHash(UnicodeEncoding.Unicode.GetBytes(keyPassword + salt));
            AESProvider.IV = md5.ComputeHash(UnicodeEncoding.Unicode.GetBytes(IVPassword + salt));

            // Initialize cryptographic transformers from AESProvider
            encryptor = AESProvider.CreateEncryptor();
            decryptor = AESProvider.CreateDecryptor();
        }

        /// <summary>
        /// Encrypts a string with AES
        /// </summary>
        /// <param name="plainText">String to encrypt</param>
        /// <returns>Encrypted string</returns>
        public string Encrypt(string plainText)
        {
            // Convert string to bytes
            byte[] plainBytes = UnicodeEncoding.Unicode.GetBytes(plainText);

            // Encrypt bytes
            byte[] secureBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);

            // Return encrypted bytes as a string
            return UnicodeEncoding.Unicode.GetString(secureBytes);
        }

        /// <summary>
        /// Encrypts a byte array with AES
        /// </summary>
        /// <param name="plainBytes">Data to encrypt</param>
        /// <returns>Encrypted byte array</returns>
        public byte[] Encrypt(byte[] plainBytes)
        {
            // Encrypt bytes
            return encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
        }

        /// <summary>
        /// Decrypts a string with AES
        /// </summary>
        /// <param name="secureText">Encrypted string to decrypt</param>
        /// <returns>Decrypted string</returns>
        public string Decrypt(string secureText)
        {
            // Convert encrypted string to bytes
            byte[] secureBytes = UnicodeEncoding.Unicode.GetBytes(secureText);

            // Decrypt bytes
            byte[] plainBytes = decryptor.TransformFinalBlock(secureBytes, 0, secureBytes.Length);

            // Return decrypted bytes as a string
            return UnicodeEncoding.Unicode.GetString(plainBytes);
        }

        /// <summary>
        /// Decrypts data with AES
        /// </summary>
        /// <param name="secureBytes">Encrypted data to decrypt</param>
        /// <returns>Decrypted data</returns>
        public byte[] Decrypt(byte[] secureBytes)
        {
            // Decrypt bytes
            return decryptor.TransformFinalBlock(secureBytes, 0, secureBytes.Length);
        }
    }
}

Comments, please! Is this safe?

0

Share this post


Link to post
Share on other sites
Safe against what?

Also, quoting myself:

OpenSSL is a very secure TLS implementation


That sounds quite ironic :-) But the truth is that OpenSSL is still the least bad of the public implementations available. Kind of like how democracy is the worst form of government, except for all the alternatives.
0

Share this post


Link to post
Share on other sites

Safe against what?

Also, quoting myself:
 

OpenSSL is a very secure TLS implementation


That sounds quite ironic :-) But the truth is that OpenSSL is still the least bad of the public implementations available. Kind of like how democracy is the worst form of government, except for all the alternatives.

 

Yeah, that turned out to be just a tiny bit awkward huh? biggrin.png

Edited by Promit
0

Share this post


Link to post
Share on other sites

hplus: I guess what I meant was - are there any obvious flaws/weaknesses in the code? It looks to me like it should do what samoth prescribed, but I'd like an informed answer!

As far as AES en/decryption, that seems to work just fine in the sample, but to be honest I don't even care about that. The protocol doesn't contain any sensitive information as such. The only thing I really care about is secure authentication (again, without using TLS because it takes half a PhD to figure out how to use it).

 

Also, is it OK to send the nonce unencrypted from client to server?

 

0

Share this post


Link to post
Share on other sites

 

if they can already get a dump of your entire database, i think there are more problems to worry about then the person being able to simply act as an imposter of a customer


This is also a common, and dangerous, misconception about system security.

 

I can give far more appreciation to this statement after recent occurrences.  actually it made me find a bug in my own networking code that theoretically could be abused in a similar manner.

0

Share this post


Link to post
Share on other sites

are there any obvious flaws/weaknesses in the code?


That's impossible to answer without knowing what kind of attacker you're trying to defend against.
What capabilities does the attacker have? Can he read data between you and the server? Can he read data on your client? Can he read data on the server? Can he insert data between client and server?
Or, I guess, "she." Edited by hplus0603
0

Share this post


Link to post
Share on other sites

Like I said: I don't care about protocol encryption, only secure authentication.

At least I know that there's no way in hell anyone can get a user's password, or even hash, because now it isn't being sent across.

What do you mean "can he read data on your client"?

 

I was thinking about that, and I was wondering: does it matter if I hardcode the server's public key into the client before releasing a binary, or simply distribute the server's public key as a standalone file? The way I see it, if an attacker wants the server's public key, he's going to be able to get it either way.

 

Now that I think about it, EC Diffie Hellman is still vulnerable to attack, because an attacker could use malware to get another user's key in-memory, and then he'd have the ability to impersonate that user. But TLS would also be vulnerable to this kind of attack, no?

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0