cocagne / pysrp

Python implementation of the Secure Remote Password protocol (SRP)
MIT License
113 stars 42 forks source link

shared key for symetric encryption #3

Open olivierch opened 10 years ago

olivierch commented 10 years ago

I did not see how could be obtained a large key that was known at the end of exchanges and known only by both sides. We cannot perform symmetric encryption without it. Could you explain that?

cocagne commented 10 years ago

It's inherent to the math underlying the SRP algorithm. If you haven't already, I suggest you take a look at the end of the pysrp documentation. There's a description of the algorithm there that will hopefully shed some light on the issue. Ultimately and at the end of the exchange, both endpoints are left with a unique value that is known only to them and that may be used for symmetric encryption. I'm not really sure how to explain further. Is you question based more on and understanding of the mathematics or a understanding of the implementation?

On Fri, Sep 27, 2013 at 12:33 AM, Olivier Chaussavoine < notifications@github.com> wrote:

I did not see how could be obtained a large key that was known at the end of exchanges and known only by both sides. We cannot perform symmetric encryption without it. Could you explain that?

— Reply to this email directly or view it on GitHubhttps://github.com/cocagne/pysrp/issues/3 .

olivierch commented 10 years ago

You are right, I found the method get_session_key() that solved the problem. I was wondering which kind of symmetric cipher could be used when this key is set, I chose Crypto.Cipher.AES and initialized it like this:

from Crypto.Cipher import AES
from Crypto.Hash import SHA256

def getAES(pwd,iv):
    _hash = SHA256.new()
    _hash.update(pwd)
    return AES.new(_hash.digest(),AES.MODE_CBC,iv[:16])

where iv is returned by the method get_session_key(). The password is hashed in order to obtain a key with 32 bytes. I just use the methods encrypt() and decrypt() of the object returned by getAES(pwd,iv).

Have you an opinion on this implementation?

cocagne commented 10 years ago

AES is a good choice for the encryption algorithm. It's pretty standard and has a good security rating. You should use the SRP session key as the AES key and the IV should, ideally, be random data. The IV isn't secret information and you can send it across the network to the receiver so they can use the same value.

A word of caution here though. The likelihood that you'll make a mistake with your use of encryption is close to 100%. It's extremely difficult to achieve good security even for cryptography experts. If your use-case really does require tight security, I'd suggest that you first try and closely follow the encryption approach used by some other protocol. You could, for example, use an encryption model that closely follows TLS packet encryption. Second and after you've done the first, try and find someone with a good crypto background to vet your implementation.

Good luck.

Tom

On Fri, Sep 27, 2013 at 3:34 AM, Olivier Chaussavoine < notifications@github.com> wrote:

You are right, I found the method get_session_key() that solved the problem. I was wondering which kind of symmetric cipher could be used when this key is set, I chose Crypto.Cipher.AES and initialized it like this:

from Crypto.Cipher import AESfrom Crypto.Hash import SHA256 def getAES(pwd,iv): _hash = SHA256.new() _hash.update(pwd) return AES.new(_hash.digest(),AES.MODE_CBC,iv[:16])

where iv is returned by the method get_session_key(). The password is hashed in order to obtain a key with 32 bytes. I just use the methods encrypt() and decrypt() of the object returned by getAES(pwd,iv).

Have you an opinion on this implementation?

— Reply to this email directly or view it on GitHubhttps://github.com/cocagne/pysrp/issues/3#issuecomment-25230442 .

olivierch commented 10 years ago

I beleived the result obtained by get_session_key() was random and only known by both sides. Am I wrong?

Your second argument is very wise and respectfull, but I do very simple things with that: 1) srp challenge giving me a session key, 2) symetric encryption with AES using this session key.

2013/10/1 cocagne notifications@github.com

AES is a good choice for the encryption algorithm. It's pretty standard and has a good security rating. You should use the SRP session key as the AES key and the IV should, ideally, be random data. The IV isn't secret information and you can send it across the network to the receiver so they can use the same value.

A word of caution here though. The likelihood that you'll make a mistake with your use of encryption is close to 100%. It's extremely difficult to achieve good security even for cryptography experts. If your use-case really does require tight security, I'd suggest that you first try and closely follow the encryption approach used by some other protocol. You could, for example, use an encryption model that closely follows TLS packet encryption. Second and after you've done the first, try and find someone with a good crypto background to vet your implementation.

Good luck.

Tom

On Fri, Sep 27, 2013 at 3:34 AM, Olivier Chaussavoine < notifications@github.com> wrote:

You are right, I found the method get_session_key() that solved the problem. I was wondering which kind of symmetric cipher could be used when this key is set, I chose Crypto.Cipher.AES and initialized it like this:

from Crypto.Cipher import AESfrom Crypto.Hash import SHA256 def getAES(pwd,iv): _hash = SHA256.new() _hash.update(pwd) return AES.new(_hash.digest(),AES.MODE_CBC,iv[:16])

where iv is returned by the method get_session_key(). The password is hashed in order to obtain a key with 32 bytes. I just use the methods encrypt() and decrypt() of the object returned by getAES(pwd,iv).

Have you an opinion on this implementation?

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25230442> .

— Reply to this email directly or view it on GitHubhttps://github.com/cocagne/pysrp/issues/3#issuecomment-25422744 .

Olivier Chaussavoine

cocagne commented 10 years ago

Oh, ok. I think I follow you now. You're intentionally using a derivative of the user's password as the AES encryption key and using the SRP session key to avoid having to explicitly send the IV over the network. It sounds reasonable and I can't think of anything specifically wrong with that approach but I would have a few concerns about taking this approach for one of my projects. Specifically:

The last point there is a little fuzzy but crypto is so easy to get wrong it's almost always a good idea to follow the common practice unless you have a compelling reason to do otherwise. That said though, your approach might be fine. It looks like it should be but I'm far from a security expert.

Tom

On Tue, Oct 1, 2013 at 11:53 AM, Olivier Chaussavoine < notifications@github.com> wrote:

I beleived the result obtained by get_session_key() was random and only known by both sides. Am I wrong?

Your second argument is very wise and respectfull, but I do very simple things with that: 1) srp challenge giving me a session key, 2) symetric encryption with AES using this session key.

2013/10/1 cocagne notifications@github.com

AES is a good choice for the encryption algorithm. It's pretty standard and has a good security rating. You should use the SRP session key as the AES key and the IV should, ideally, be random data. The IV isn't secret information and you can send it across the network to the receiver so they can use the same value.

A word of caution here though. The likelihood that you'll make a mistake with your use of encryption is close to 100%. It's extremely difficult to achieve good security even for cryptography experts. If your use-case really does require tight security, I'd suggest that you first try and closely follow the encryption approach used by some other protocol. You could, for example, use an encryption model that closely follows TLS packet encryption. Second and after you've done the first, try and find someone with a good crypto background to vet your implementation.

Good luck.

Tom

On Fri, Sep 27, 2013 at 3:34 AM, Olivier Chaussavoine < notifications@github.com> wrote:

You are right, I found the method get_session_key() that solved the problem. I was wondering which kind of symmetric cipher could be used when this key is set, I chose Crypto.Cipher.AES and initialized it like this:

from Crypto.Cipher import AESfrom Crypto.Hash import SHA256 def getAES(pwd,iv): _hash = SHA256.new() _hash.update(pwd) return AES.new(_hash.digest(),AES.MODE_CBC,iv[:16])

where iv is returned by the method get_session_key(). The password is hashed in order to obtain a key with 32 bytes. I just use the methods encrypt() and decrypt() of the object returned by getAES(pwd,iv).

Have you an opinion on this implementation?

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25230442> .

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25422744> .

Olivier Chaussavoine

— Reply to this email directly or view it on GitHubhttps://github.com/cocagne/pysrp/issues/3#issuecomment-25467976 .

olivierch commented 10 years ago

You got it! You let me think it could be possible to avoid storing a clear password in the server database. That was not my requirement. Do we just need to compute the verifier and store it with uname in the database for all future sessions, or each time a new session is initiated? I chose the second solution storing only uname and password in the database. I am not a security expert either, and more afraid by taking the risk of sending a clear IV on the network than storing clear passwords on my database.

Olivier

2013/10/2 cocagne notifications@github.com

Oh, ok. I think I follow you now. You're intentionally using a derivative of the user's password as the AES encryption key and using the SRP session key to avoid having to explicitly send the IV over the network. It sounds reasonable and I can't think of anything specifically wrong with that approach but I would have a few concerns about taking this approach for one of my projects. Specifically:

  • This approach requires either storing the raw user's password in the server's database or both the SRP verifier and the SHA256 hash of the password. Were you to use the session key as the AES key, only the SRP verifier would be required.
  • It is significantly easier to recover the user's password from a compromised SHA256 hash of the user's password than it is to recover it from the SRP verifier (which is salted)
  • It goes against the common practice of using the SRP session key as the symmetric encryption key.

The last point there is a little fuzzy but crypto is so easy to get wrong it's almost always a good idea to follow the common practice unless you have a compelling reason to do otherwise. That said though, your approach might be fine. It looks like it should be but I'm far from a security expert.

Tom

On Tue, Oct 1, 2013 at 11:53 AM, Olivier Chaussavoine < notifications@github.com> wrote:

I beleived the result obtained by get_session_key() was random and only known by both sides. Am I wrong?

Your second argument is very wise and respectfull, but I do very simple things with that: 1) srp challenge giving me a session key, 2) symetric encryption with AES using this session key.

2013/10/1 cocagne notifications@github.com

AES is a good choice for the encryption algorithm. It's pretty standard and has a good security rating. You should use the SRP session key as the AES key and the IV should, ideally, be random data. The IV isn't secret information and you can send it across the network to the receiver so they can use the same value.

A word of caution here though. The likelihood that you'll make a mistake with your use of encryption is close to 100%. It's extremely difficult to achieve good security even for cryptography experts. If your use-case really does require tight security, I'd suggest that you first try and closely follow the encryption approach used by some other protocol. You could, for example, use an encryption model that closely follows TLS packet encryption. Second and after you've done the first, try and find someone with a good crypto background to vet your implementation.

Good luck.

Tom

On Fri, Sep 27, 2013 at 3:34 AM, Olivier Chaussavoine < notifications@github.com> wrote:

You are right, I found the method get_session_key() that solved the problem. I was wondering which kind of symmetric cipher could be used when this key is set, I chose Crypto.Cipher.AES and initialized it like this:

from Crypto.Cipher import AESfrom Crypto.Hash import SHA256 def getAES(pwd,iv): _hash = SHA256.new() _hash.update(pwd) return AES.new(_hash.digest(),AES.MODE_CBC,iv[:16])

where iv is returned by the method get_session_key(). The password is hashed in order to obtain a key with 32 bytes. I just use the methods encrypt() and decrypt() of the object returned by getAES(pwd,iv).

Have you an opinion on this implementation?

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25230442> .

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25422744> .

Olivier Chaussavoine

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25467976> .

— Reply to this email directly or view it on GitHubhttps://github.com/cocagne/pysrp/issues/3#issuecomment-25510951 .

Olivier Chaussavoine

cocagne commented 10 years ago

You only need to calculate the verifier once and it may be used for all future SRP authentication attempts. As for the IV, it's not a secret value so you can send it over the network in the clear and without concern. In fact, when AES encryption is being used in DTLS (the UDP version of SSL), each packet has a random IV prepended to the encrypted data block. All you need to do is make sure the IV comes from a good source of random data. On Linux systems, you can just read the appropriate number of bytes from /dev/urandom. There's a section on Initialization Vectors used in block ciphers on Wikipedia that might help: http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Initialization_vector_.28IV.29

As for storing the passwords directly in the database, please try to avoid this if at all possible. It seems innocuous but remember that people tend to reuse passwords even when you tell them not to. If your database is somehow hacked, it could lead to bigger, seemingly unrelated problems when the attackers test those username,password combinations on other sites. Financial websites aside, even just gaining access to someone's web e-mail account could give an attacker enough personal information for identity theft.

Cheers,

Tom

On Wed, Oct 2, 2013 at 1:25 AM, Olivier Chaussavoine < notifications@github.com> wrote:

You got it! You let me think it could be possible to avoid storing a clear password in the server database. That was not my requirement. Do we just need to compute the verifier and store it with uname in the database for all future sessions, or each time a new session is initiated? I chose the second solution storing only uname and password in the database. I am not a security expert either, and more afraid by taking the risk of sending a clear IV on the network than storing clear passwords on my database.

Olivier

2013/10/2 cocagne notifications@github.com

Oh, ok. I think I follow you now. You're intentionally using a derivative of the user's password as the AES encryption key and using the SRP session key to avoid having to explicitly send the IV over the network. It sounds reasonable and I can't think of anything specifically wrong with that approach but I would have a few concerns about taking this approach for one of my projects. Specifically:

  • This approach requires either storing the raw user's password in the server's database or both the SRP verifier and the SHA256 hash of the password. Were you to use the session key as the AES key, only the SRP verifier would be required.
  • It is significantly easier to recover the user's password from a compromised SHA256 hash of the user's password than it is to recover it from the SRP verifier (which is salted)
  • It goes against the common practice of using the SRP session key as the symmetric encryption key.

The last point there is a little fuzzy but crypto is so easy to get wrong it's almost always a good idea to follow the common practice unless you have a compelling reason to do otherwise. That said though, your approach might be fine. It looks like it should be but I'm far from a security expert.

Tom

On Tue, Oct 1, 2013 at 11:53 AM, Olivier Chaussavoine < notifications@github.com> wrote:

I beleived the result obtained by get_session_key() was random and only known by both sides. Am I wrong?

Your second argument is very wise and respectfull, but I do very simple things with that: 1) srp challenge giving me a session key, 2) symetric encryption with AES using this session key.

2013/10/1 cocagne notifications@github.com

AES is a good choice for the encryption algorithm. It's pretty standard and has a good security rating. You should use the SRP session key as the AES key and the IV should, ideally, be random data. The IV isn't secret information and you can send it across the network to the receiver so they can use the same value.

A word of caution here though. The likelihood that you'll make a mistake with your use of encryption is close to 100%. It's extremely difficult to achieve good security even for cryptography experts. If your use-case really does require tight security, I'd suggest that you first try and closely follow the encryption approach used by some other protocol. You could, for example, use an encryption model that closely follows TLS packet encryption. Second and after you've done the first, try and find someone with a good crypto background to vet your implementation.

Good luck.

Tom

On Fri, Sep 27, 2013 at 3:34 AM, Olivier Chaussavoine < notifications@github.com> wrote:

You are right, I found the method get_session_key() that solved the problem. I was wondering which kind of symmetric cipher could be used when this key is set, I chose Crypto.Cipher.AES and initialized it like this:

from Crypto.Cipher import AESfrom Crypto.Hash import SHA256 def getAES(pwd,iv): _hash = SHA256.new() _hash.update(pwd) return AES.new(_hash.digest(),AES.MODE_CBC,iv[:16])

where iv is returned by the method get_session_key(). The password is hashed in order to obtain a key with 32 bytes. I just use the methods encrypt() and decrypt() of the object returned by getAES(pwd,iv).

Have you an opinion on this implementation?

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25230442> .

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25422744> .

Olivier Chaussavoine

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25467976> .

— Reply to this email directly or view it on GitHub< https://github.com/cocagne/pysrp/issues/3#issuecomment-25510951> .

Olivier Chaussavoine

— Reply to this email directly or view it on GitHubhttps://github.com/cocagne/pysrp/issues/3#issuecomment-25515678 .

simbo1905 commented 9 years ago

To be clear to anyone reading this the correct approach is to:

  1. Register the users SRP salt and SRP verifier over TSL/SSL to protect the verifier against being intercepted for an offline brute force attack. Also you want to protect the user id which if known can allow for online guessing attacks trying 'obvious' passwords: so use TSL/SSL for both the registration and the login for extra protection.
  2. For bonus marks use a client side password meter widget to show the user if their password is strong to encourage them to use a string password which cannot be guessed quickly with such an 'obvious password' slow online attack.
  3. For follow on cryptography with AES use the SRP shared session key (which should be a hash of the SRP 'S' value) as the AES key. That's the shared secret. AES also needs a fresh none secret random value every usage of the shared key. In layman's terms (I am not a cryptographer) internally to AES it does the equivalent of salting the key with the per-usage random to get a unique pre-usage key. That prevents the user uploading lots of similar data which have common bit patterns which leak info about the shared secret key. In WWII they jammed enemy comms to force them to retransmit using the same key just to use this attack to crack their codes. So every use of a shared key needs a unique random publicly shared value to protect the shared secret key additional with randomness.

End.