Cryptography - am I doing it right?

Started by
26 comments, last by Washu 10 years ago

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

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Advertisement

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?

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.

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

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

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?

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?


That is correct, and is why I asked about the kind of threat you're trying to defend against.
To defend against that threat, send each user a small calculator-like token that you can enter one number (challenge) into, and it will hash it with some private key, and then respond with the response.
Not very convenient way to log on for a computer game, though :-)
enum Bool { True, False, FileNotFound };

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?
This is an attack you cannot really defend against because you have no control over what malware users will install on their computer or how stupid users are in general. Two thirds of users will tell their password to anyone who is asking it, too (if their password isn't "password" or "fuckyou" anyway).

To defend against that threat, send each user a small calculator-like token that you can enter one number (challenge) into, and it will hash it with some private key, and then respond with the response.
That basically sounds like the next generation of the RSA token which despite being totally useless, was surprisingly nevertheless widely used in the industry for at least... a decade? That one was (still is...?) based on generating a presumably strong random number from a timestamp. Which was of course prone to the "how to synchronize 1 million cheap quartz clocks" problem, so the system always had to accept several answers as "correct".

I'm not sure what the RSA token looks like nowadays since the one major company where I've seen it in use has abandoned it last year, but it was really ridiculous. People who forgot their token at home (they had to take it home, leaving it at the working place was forbidden) couldn't work, unless someone else would just "borrow" their token to them. Stunningly this even worked fine (!!!) which shows what a kind of perfect bullshit security this was. Actually, entering a number from someone else's token shouldn't work at all, if it works the way you'd expect.

Though of course it was something which would cause a lengthy talk with their supervisor (if someone denounced them) for breaching policy number whatever-it-is, but usually people just gave a fuck.

That would, in the end, only result in someone telling one stupid user "You know, I am from... uh.... the National Token Surveillance Department (NTSD) and need to verify your token for correct operation according to the 2014 Token Interaction Act, can you just tell me the correct sequence once, thank you for your cooperation". biggrin.png

Diffie Hellman


Also, Diffie-Hellman is vulnerable against a man in the middle simply interspersing himself, and running one D-H exchange to the client, and another D-H exchange to the server, decrypting and re-encrypting everything in between. You need public/private keys to exchange AND SIGN the session key to defend against that. Which, btw, is included in TLS.

That basically sounds like the next generation of the RSA token


Not quite (although that's also an amusing story.) If the token has a key in itself, it can sign whatever data you put in. So, if the server sends a challenge to the token, and asks for a signature back, then you can presume that the key is present on the other end. Which transfers "guarding your computer against malicious flash ads" into "guarding your wallet/drawer against physical access."

Implementations I've seen suffer from weak signature strength, though -- users can't be bothered entering more than two four-digit numbers, for a total of 20 bits of security, apparently :-)

can you just tell me the correct sequence once


Indeed, once the bar is there at all, 95% of all attacks will be through social engineering.
enum Bool { True, False, FileNotFound };
General response, as I don't know the details of the .Net cryptography libraries.

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"?

From what you've mentioned, it sounds like your threat model is an attacker trying to steal client credentials:
  • Passively listening on the network
  • Actively interfering with the network, e.g. a man in the middle
You don't appear to be trying to solve the problem where the attacker has compromised the server or the client itself, or where the user is socially engineered by a determined attacker. Is that accurate?

I'm afraid your code would fail review based on the first comment:

Not sure why, but both keys must derive from the same master for decryption to work.

On the other hand, at least it is honest!

I'd be worried about your use of GUIDs for nonces and challenges - they are not cryptographically secure.

The server appears to require the client's private key, which probably means you haven't thought through how this will work in practise.

It isn't clear if you're doing much basic input validation. I cannot see the ProcessedPacket class, but it looks like in places you're willing to pass values read from the network directly into array allocations. In the cases I saw, these values they are bytes, so there isn't likely to be a serious DOS problem. However, if you are doing that for multi-byte values anywhere (ReadPascalString, perhaps?) you might allow a malicious client to make you to do an awful lot of work just by sending packets with large usernames / GUID strings.

And for comments purely on the code, you appear to have mixed client and server specific code together, which makes everything quite confusing. It is difficult to remember which variables and functions are going to be used by which peer, and you're doing unnecessary work initialising some variables that won't (or at least, shouldn't) be used. The common code could be moved to a helper class, and then the client and server can have their own classes. The code is heavily tied to the network, which complicates testing.

Ideally, you might be able to write test cases in isolation (hypothetical code):
public void testStuff()
{
    byte [] serverKey = MatskCrypto.loadPrivateKey("server.key");
    MatskServer server = new MatskServer(serverKey);
    string storedPassword = MatskAuth.hashWithSalt("top_secret", "delicious_salt");
    server.addClientCredentials("somebody@example.com", storedPassword);

    byte [] clientKey = MatskCrypto.loadPrivateKey("client.key");
    MatskClient client = new MatskClient(clientKey);

    byte [] validCipherText = client.prepareAuthenticationRequest("somebody@example.com", "top_secret");
    assertTrue(server.verifyAuthenticationRequest(cipherText));

    byte [] badUsername = client.prepareAuthenticationRequest("nobody@example.com", "top_secret");
    assertFalse(server.verifyAuthenticationRequest(cipherText));

    byte [] badPassword = client.prepareAuthenticationRequest("somebody@example.com", "bottom_secret");
    assertFalse(server.verifyAuthenticationRequest(cipherText));
}


I'd be worried about your use of GUIDs for nonces and challenges - they are not cryptographically secure.

Ah, thanks! What can I use instead?


Also, Diffie-Hellman is vulnerable against a man in the middle simply interspersing himself, and running one D-H exchange to the client, and another D-H exchange to the server, decrypting and re-encrypting everything in between. You need public/private keys to exchange AND SIGN the session key to defend against that. Which, btw, is included in TLS.

I thought EC DH solved this issue?


However, if you are doing that for multi-byte values anywhere (ReadPascalString, perhaps?) you might allow a malicious client to make you to do an awful lot of work just by sending packets with large usernames / GUID strings.

The internal packet buffer has an upper limit of 11kb, but point taken! ;)

This topic is closed to new replies.

Advertisement