Jump to content

  • Log In with Google      Sign In   
  • Create Account

Cryptography - am I doing it right?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
30 replies to this topic

#1 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 27 March 2014 - 10:24 AM

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?



Sponsor:

#2 hplus0603   Moderators   -  Reputation: 5730

Like
3Likes
Like

Posted 27 March 2014 - 10:49 AM

In short: You are doing it wrong, because you're not using TLS.

In slightly longer: The fact that you even have to ask means that you're likely doing it wrong. This means that you haven't spent the years necessary to learn about cryptography to build a robust cryptographic system. (These systems are only ever as strong as the weakest link.)

Also: Is this really an attack that you need to worry about? 99.99% of all attacks on games are social engineering ("give me your password and I'll improve your ranking") or computer malware ("install this hotkey helper that totally doesn't read your keyboard to send your password to me") not technical man-in-the-middle injections.

Note that not even Diffie-Hellman, on its own, is sufficiently secure. The reason is that a MitM can run Diffie-Hellman towards the client on one side, and then run a separate Diffie-Hellman towards the server on the other side, and read the cleartext in the middle.

If you care about men in the middle: Use TLS. Generate a certificate per user. For non-HTTPS implementations of TLS, you can use a self-signed root certificate. Part of initial sign-up could be to download this certificate, which could be automated by your UI.
enum Bool { True, False, FileNotFound };

#3 Bacterius   Crossbones+   -  Reputation: 9305

Like
0Likes
Like

Posted 27 March 2014 - 04:16 PM

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.


The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#4 Ohforf sake   Members   -  Reputation: 1832

Like
0Likes
Like

Posted 27 March 2014 - 04:18 PM

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.

#5 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 28 March 2014 - 06:07 AM

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. :)

 



#6 Bacterius   Crossbones+   -  Reputation: 9305

Like
1Likes
Like

Posted 28 March 2014 - 07:23 AM

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.


The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#7 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 28 March 2014 - 08:19 AM

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?



#8 hplus0603   Moderators   -  Reputation: 5730

Like
2Likes
Like

Posted 28 March 2014 - 09:30 AM

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")
enum Bool { True, False, FileNotFound };

#9 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 28 March 2014 - 10:32 AM

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?



#10 samoth   Crossbones+   -  Reputation: 5038

Like
3Likes
Like

Posted 28 March 2014 - 11:34 AM

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?
At the very least, you should hash the password with a timestamp or something else that makes the hash value unique while still allowing the hash to be verified. Basically, this is none different from using a salt, only the reason why you use it is different. Otherwise, it is really trivial to exploit the system for anyone who listens to your communication (such as on an open WiFi spot in a café) by remembering that hash value. He doesn't even need to look the password up in a rainbow table, he can just replay the hash as-is.

Lastly, if I'm going to use encryption at all, what's wrong with using RSA key exchange?
Nothing as such. Except it's not trivial to get right either, and you'll likely do it wrong (or worse than using TLS properly).

 

The main reason I really don't want to use TLS is because it just seems overly complex.
That is, in my opinion, the only valid excuse for not always using OpenSSL/TLS for everything (otherwise I'd say use OpenSSL/TLS even when actually no encryption is needed at all).

You already need half a PhD to figure out how to use it at all, which is a shame because the universally given answer "do not run your own crypto stuff, you will do it wrong" is almost certainly true. Without even looking at your code and without even knowing your skill level, one can tell with very high confidence that you did it wrong.

 

That said, because I find TLS and OpenSSL obnoxious for their complexity, I would recommend to use NaCL if you can get it to compile (compiling is admittedly a challenge!), or recommend that you do an ECDH key exchange both for authentication.and establishing a session key, with the curve25519 library (same author). That one is one source file, and a single function call and so easy that even a brain-damaged monkey couldn't do wrong, and both speed and key sizes are "very manageable". As in, 32 bytes per key, and upwards of 20,000 ops per second.

 

For example, a scheme like this one is pretty acceptable (not 100% secure, but as good as it gets without getting overly complicated, and by all means good enough for a game):

  • Store the server's public key (32 bytes) in the client executable
  • When the user creates an account (via a web interface or whatever means), hash username+password a few dozen times. This is the user's private key. Generate a public key from that (one function call) and immediately throw the private key away after that. Store the public key in the database on the server.
  • Whenever the user wants to log in, send the username. The server sends back a message encrypted using a key derived from its private key and the user's public key. Meanwhile the client program hashes the entered password to figure out the user's private key.
  • The client decrypts the message using the user's private key and the server's public key stored in the executable. The decrypted message contains a random session key and a challenge response valid for that connection.
  • The client uses the session key to encrypt all further traffic and sends the challenge response. The server won't talk to anyone who doesn't send the correct challenge response, and someone who doesn't know the user's private key (i.e. the password) won't know the session key and won't be able to understand any of the communication or send meaningful commands.

This system is of course not 100% secure, but it is simple and immune to all common and reasonably realistic technical attacks (there are a few additional minor details about what "encrypt" means and such, but in principle it's not much more complicated than that). Social attacks of course still work, but they work on every system, regardless of how clever you think they are... you simply can't prevent a user from giving away his password or from installing that porn-trojan or that keylogger on his computer.

 

Someone might root the server and steal users' passwords while they create accounts, but that's something you can always do when you are in total control the server. It's no problem insofar as you already control the complete server, so stealing a user's game password is no concern.

Further, a disgruntled employee might run off with the server's private key, or a hacker might steal it, much like a hacker might steal the user password database. While the server's private key will allow for impersonating/cloning the server or for a MITM attack, it is only useful provided that you can also successfully subvert the DNS system or that you work at an ISP or such, so you can conceivably run a MITM (for most people who are not ISP employees or secret service agents, this is a rather theoretical attack without much of a practical value). Otherwise, the server's private key is completely worthless, it won't let you log into the game. Nor will the stolen user database, since they're public keys only.

 

For a game, I daresay this is absolutely sufficient, but if you're worried, you can still make it more complicated (letting keys expire, sign them, whatever). Personally, I think that's using a sledgehammer to crack a nut (...for a game).

 

Though of course, on a second thought... the most common DSL router in the EU (which powers e.g. about 50% of the internet access in Germany) can be trivially exploited and still 3/4 of users haven't upgraded their firmware two months later if you can believe what's said on the news. From that point of view, it might be conceivable for a runaway employee who isn't the leader of a top international crime organization and who isn't a CIA employee to run a MITM on users via their router. However, those users would likely be the same users who will tell you their password, too. laugh.png



#11 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 28 March 2014 - 12:29 PM

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# here.

 

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?



#12 hplus0603   Moderators   -  Reputation: 5730

Like
0Likes
Like

Posted 28 March 2014 - 05:12 PM

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.
enum Bool { True, False, FileNotFound };

#13 frob   Moderators   -  Reputation: 22792

Like
1Likes
Like

Posted 28 March 2014 - 05:35 PM


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.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I write about assorted stuff.


#14 samoth   Crossbones+   -  Reputation: 5038

Like
1Likes
Like

Posted 28 March 2014 - 08:41 PM

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, 28 March 2014 - 08:42 PM.


#15 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 29 March 2014 - 04:20 AM

 


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.



#16 hplus0603   Moderators   -  Reputation: 5730

Like
0Likes
Like

Posted 29 March 2014 - 11:18 AM

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.
enum Bool { True, False, FileNotFound };

#17 slicer4ever   Crossbones+   -  Reputation: 3990

Like
0Likes
Like

Posted 30 March 2014 - 07:26 AM

 

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.


Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.

#18 hplus0603   Moderators   -  Reputation: 5730

Like
0Likes
Like

Posted 30 March 2014 - 11:04 AM

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.
enum Bool { True, False, FileNotFound };

#19 MatsK   Members   -  Reputation: 263

Like
0Likes
Like

Posted 11 April 2014 - 06:02 AM

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?



#20 hplus0603   Moderators   -  Reputation: 5730

Like
0Likes
Like

Posted 11 April 2014 - 09:31 AM

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.
enum Bool { True, False, FileNotFound };




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS