Hash MD5, Sha1 and Length

A

Arne Vajhøj

rossum said:
It is a minor error to use the GetNonZeroBytes() method in this
context as it reduces the randomness of the salt.

It removes one possible value out of 2^128.

It does not matter.
Base64 increases both processing time and the storage space needed.

Given that you want to hash recursively for 0.25 seconds, then
the processing time spend on Base64 encoding will be insignificant.

Arne
 
S

shapper

Correct, security costs.  If you want the security then you pay the
cost.  To quote Bruce Schneier: "There are already enough insecure
fast systems, we don't need another one."  How much will it cost if an
attacker gets access to the passwords?  How much will it cost to add
some more CPU to the server farm?  Those are questions for the OP to
answer.  If a court case ensues after loss of data then it is
important to be able to show that best security practice was being
used.




That is the sort of thing that the OP has to assess.  We do not know
that value of the data protected by the passwords.

rossum

In this case the most important data I have for each user its their
email ... In this case security is not a "big issue" ...
But I would like to have a secure and correct way to do this ...

I can then, from project to project, change the REPEATS from 25 000 to
10 000 or just 1 according to the level of security and processor cost
for each situation ...

Rossum,

Thank you for the code. I am just going to add a few things test it
and then post what I did here.
 
S

shapper

Hello,

I added a few things to your code:

I defined HashAlgorithm, Repeats and SaltSize as inputs.

I didn't define them as properties since on your code you didn't use
any property and have the class sealed.
I am not sure if there is a security reason for this or something
else ...

I defined these parameters so I can use different configurations in
different projects if I need it.

I also cleanup the data input (password) on CalculateHash before I
return the hash.

And I tried to implement IDisposable but I am not exactly sure how to
do it and how to use it correctly.
I know it has to do with disposing managed and unmanaged resources but
I am not sure how to integrate it in this class.
 
S

shapper

Hello,

I added a few things to your code:

I defined HashAlgorithm, Repeats and SaltSize as inputs.

I didn't define them as properties since on your code you didn't use
any property and have the class sealed.
I am not sure if there is a security reason for this or something
else ...

I defined these parameters so I can use different configurations in
different projects if I need it.

I also cleanup the data input (password) on CalculateHash before I
return the hash.

And I tried to implement IDisposable but I am not exactly sure how to
do it and how to use it correctly.
I know it has to do with disposing managed and unmanaged resources but
I am not sure how to integrate it in this class.

And I forgot to post the code I have know (I tested it and got the
same results when I ran you code):


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

namespace HashTestNamespace {

static class HashTest {

public static void Main() {

// 1 Test matching
byte[] password1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
byte[] salt = HashStuff.GenerateSalt(16);
byte[] firstHash = HashStuff.CalculateHash(new SHA256Managed(),
25000, password1, salt);
byte[] password2 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
if (HashStuff.CheckHash(new SHA256Managed(), 25000, password2,
salt, firstHash)) {
Console.WriteLine("Matching test OK");
} else {
Console.WriteLine("Matching test failed.");
}

// 2 Test not matching
password1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
salt = HashStuff.GenerateSalt(16);
firstHash = HashStuff.CalculateHash(new SHA256Managed(), 25000,
password1, salt);
password2 = new byte[] { 1, 1, 2, 3, 4, 5, 6, 7 };
if (HashStuff.CheckHash(new SHA256Managed(), 25000, password2,
salt, firstHash)) {
Console.WriteLine("Not matching test failed.");
} else {
Console.WriteLine("Not matching test OK.");
}

// 3 Timer test
Stopwatch sw = new Stopwatch();
int numTests = 200;
sw.Start();
for (int i = 0; i < numTests; ++i) {
password1[0] = (byte)i;
firstHash = HashStuff.CalculateHash(new SHA256Managed(),
25000, password1, salt);
} // end for
sw.Stop();
Console.WriteLine("{0} tests took {1} mSec.", numTests,
sw.ElapsedMilliseconds);
Console.WriteLine("Average {0} seconds per hash.",
sw.ElapsedMilliseconds / (numTests * 1000.0));

// Keep console on screen
Console.Write("Press [Enter] to continue... ");
Console.ReadLine();

} // end Main()

} // end class HashTest

public sealed class HashStuff : IDisposable {

private bool disposed = false;
private static RNGCryptoServiceProvider rng = new
RNGCryptoServiceProvider();

private HashStuff() { } // Prevent instantiation

public static byte[] GenerateSalt(Int32 size) {
byte[] salt = new byte[size];
rng.GetBytes(salt);
return salt;
}

public static byte[] CalculateHash(HashAlgorithm algorithm, Int32
repeats, byte[] data, byte[] salt) {
// Check parameters for nulls and salt size here.
byte[] hash;
using (algorithm) {
hash = algorithm.ComputeHash(Concat(data, salt));
for (int i = 0; i < repeats; ++i) {
hash = algorithm.ComputeHash(Concat(hash, salt));
}
} // end using

// Clear data
for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0;
}

return hash;
}

public static Boolean CheckHash(HashAlgorithm algorithm, Int32
repeats, byte[] data, byte[] salt, byte[] expected) {

byte[] actual = CalculateHash(algorithm, repeats, data, salt);
for (int i = 0; i < actual.Length; ++i) {
if (actual != expected) { return false; }
}
return true;

}

private static byte[] Concat(byte[] lhs, byte[] rhs) {
byte[] result = new byte[lhs.Length + rhs.Length];
Array.Copy(lhs, result, lhs.Length);
Array.Copy(rhs, 0, result, lhs.Length, rhs.Length);
return result;
}

// Dispose
public void Dispose() {
this.Dispose();
GC.SuppressFinalize(this);
} // Dispose

public void Dispose(bool disposing) {

if (disposed)
return;

if (disposing) {
// Free any managed resources
}

// Free any unmanaged resources
disposed = true;
}

} // end class HashStuff
}
 
P

Peter Duniho

Sealing is not required, but if you have no reason to subclass your
class it prevents an attacker writing their own subclass and inserting
it into production code:

@Override
public byte[] CalculateHash(byte[] password, byte[] salt) {
copy_to_my_secret_data_capture_file(username, password);
return super.CalculateHash(password, salt);
}
[...]

I'll suggest that sealing a class to prevent such an attack is pointless.
The "sealed" keyword is useful as a maintenance construct, to prevent
certain kinds of uses of one's code so as to avoid bugs.

But if you've got an _attacker_, trying to co-opt some class, the "sealed"
keyword isn't going to prevent sub-classing or otherwise reusing the base
class. They can just hack around it in a variety of ways.

Pete
 
S

shapper

Sealing is not required, but if you have no reason to subclass your
class it prevents an attacker writing their own subclass and inserting
it into production code:
  @Override
  public byte[] CalculateHash(byte[] password, byte[] salt) {
    copy_to_my_secret_data_capture_file(username, password);
    return super.CalculateHash(password, salt);
  }
[...]

I'll suggest that sealing a class to prevent such an attack is pointless. 
The "sealed" keyword is useful as a maintenance construct, to prevent  
certain kinds of uses of one's code so as to avoid bugs.

But if you've got an _attacker_, trying to co-opt some class, the "sealed"  
keyword isn't going to prevent sub-classing or otherwise reusing the base 
class.  They can just hack around it in a variety of ways.

Pete

Hello,

I recreated the class with the last advices posted, gave it a short
name Sash, and I got a few problems:

1) When testing I get an error in CalculateHash where algorithm is
null.
This is strange because just before I call GenerateSash the same
way and it works fine.

2) On disposable I get an error:
// Error: 'System.Security.Cryptography.HashAlgorithm.Dispose
(bool)' is inaccessible due to its protection level
// if (algorithm != null) algorithm.Dispose();

Why is this?
I tried to remove readonly from algorithm field but it didn't
solved it.

3) Rossum, when you used "private final ..." I think that was a
confusing from Java, correct?
If I am not wrong the equivalent for fields is "private
readonly" ... This is what I used but I am not sure.

Here is the entire code I am using including some comments and
testing:

public class Program {

public static void Main(String[] args) {

Sash sash = new Sash(new SHA256Managed(), 25000);
try {

// Test matching
Byte[] password1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
Byte[] salt = sash.GenerateSalt(64);
Byte[] firstHash = sash.CalculateHash(password1, salt);
Byte[] password2 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
if (sash.CheckHash(password2, salt, firstHash)) {
Console.WriteLine("Matching test OK");
} else {
Console.WriteLine("Matching test failed.");
}

// Test not matching
password1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
salt = sash.GenerateSalt(64);
firstHash = sash.CalculateHash(password1, salt);
password2 = new byte[] { 1, 1, 2, 3, 4, 5, 6, 7 };
if (sash.CheckHash(password2, salt, firstHash)) {
Console.WriteLine("Not matching test failed.");
} else {
Console.WriteLine("Not matching test OK.");
}

// Timer test
Stopwatch sw = new Stopwatch();
int numTests = 200;
sw.Start();
for (int i = 0; i < numTests; ++i) {
password1[0] = (byte)i;
firstHash = sash.CalculateHash(password1, salt);
} // end for
sw.Stop();

Console.WriteLine("{0} tests took {1} mSec.", numTests,
sw.ElapsedMilliseconds);
Console.WriteLine("Average {0} seconds per hash.",
sw.ElapsedMilliseconds / (numTests * 1000.0));
Console.Write("Press [Enter] to continue... ");
Console.ReadLine();

} finally {
sash.Dispose();
}

} // end Main()

} // end class HashTest



// Sash
/// <summary>
/// Implements salted hash for managed has algorithms
/// </summary>
public sealed class Sash : IDisposable {

#region Fields

private Boolean disposed = false;
private readonly Int32 repeats;
private readonly HashAlgorithm algorithm;

private static Int32 defaultRepeats = 10000;
private static HashAlgorithm defaultAlgorithm = new SHA256Managed
();
private static RNGCryptoServiceProvider rng = new
RNGCryptoServiceProvider();

#endregion // Fields

#region Constructors

// Sash
/// <summary>
/// Initialize sash with custom has algorithm and repeats
/// </summary>
public Sash(HashAlgorithm algorithm, Int32 repeats) {

// Check parameters
if (algorithm == null) throw new ArgumentNullException
("algorithm", "Hash algorithm cannot be null");
if (repeats <= 0) throw new ArgumentOutOfRangeException
("repeats", "Repeats must be positive");

// Define fields
algorithm = algorithm;
repeats = repeats;

} // Sash

// Sash
/// <summary>
/// Initialize sash with defaul algorithm, SHA256MAnaged, and
custom repeats value
/// </summary>
public Sash(Int32 repeats) : this(defaultAlgorithm, repeats)
{
} // Sash

// Sash
/// <summary>
/// Initialize sash with custom algorithm and default repeats
equal to 10000
/// </summary>
public Sash(HashAlgorithm algorithm) : this(algorithm,
defaultRepeats) {
} // Sash

// Sash
/// <summary>
/// Initialize sash with defaul algorithm, SHA256MAnaged, and
default repeats equal to 10000
/// </summary>
public Sash() : this(defaultAlgorithm, defaultRepeats) {
} // Sash

#endregion // Constructors

#region Methods

// GenerateSalt
/// <summary>
/// Generate random salt
/// </summary>
public Byte[] GenerateSalt(Int32 size) {
Byte[] salt = new Byte[size];
rng.GetBytes(salt);
return salt;
} // GenerateSalt

// CalculateHash
/// <summary>
/// Calculate hash from a hash algorithm and salt
/// </summary>
public Byte[] CalculateHash(Byte[] data, Byte[] salt) {

// Check parameters
if (data == null) throw new ArgumentNullException("data", "Data
cannot be null");
if (data.Length == 0) throw new ArgumentOutOfRangeException
("data", "Data cannot be empty");
if (salt == null) throw new ArgumentNullException("salt", "Salt
cannot be null");
if (salt.Length == 0) throw new ArgumentOutOfRangeException
("salt", "Salt cannot be empty");

// Calculate hash
Byte[] hash;
using (algorithm) {
hash = algorithm.ComputeHash(Concat(data, salt));
for (Int32 i = 0; i < repeats; ++i) {
hash = algorithm.ComputeHash(Concat(hash, salt));
}
}

// Clear data
for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0;
}

return hash;

} // CalculateHash

// CheckHash
/// <summary>
/// Check hash against expected
/// </summary>
public Boolean CheckHash(Byte[] data, Byte[] salt, Byte[]
expected) {

// Check parameters
if (data == null) throw new ArgumentNullException("data", "Data
cannot be null");
if (data.Length == 0) throw new ArgumentOutOfRangeException
("data", "Data cannot be empty");
if (salt == null) throw new ArgumentNullException("salt", "Salt
cannot be null");
if (salt.Length == 0) throw new ArgumentOutOfRangeException
("salt", "Salt cannot be empty");
if (expected == null) throw new ArgumentNullException
("expected", "Expected cannot be null");
if (expected.Length == 0) throw new ArgumentOutOfRangeException
("expected", "Expected cannot be empty");

// CalculateHash
Byte[] actual = CalculateHash(data, salt);
for (Int32 i = 0; i < actual.Length; ++i) {
if (actual != expected) { return false; }
}
return true;

} // CheckHash

// Dispose
/// <summary>
/// Dispose resources
/// </summary>
public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
} // Dispose

// Dispose
/// <summary>
/// Dispose resources
/// </summary>
public void Dispose(Boolean disposing) {

// Already disposed
if (disposed) return;

// Dispose managed resources
if (disposing) {

// Error: 'System.Security.Cryptography.HashAlgorithm.Dispose
(bool)' is inaccessible due to its protection level
// if (algorithm != null) algorithm.Dispose();
}

// Dispose unmanaged resources

// Redefine disposed
disposed = true;

} // Dispose

#region Private

// Concat
/// <summary>
/// Concat two byte arrays
/// </summary>
private Byte[] Concat(byte[] lhs, byte[] rhs) {
Byte[] result = new Byte[lhs.Length + rhs.Length];
Array.Copy(lhs, result, lhs.Length);
Array.Copy(rhs, 0, result, lhs.Length, rhs.Length);
return result;
} // Concat

#endregion // Private

#endregion // Methods

} // Sash

Thanks,
Miguel
 
S

shapper

See comments above.  You probably need to check the documentation for
HashAlgorithm.

Hi,

I made the corrections, removed using and went to HashAlgorithm
documentation on MSDN:
http://msdn.microsoft.com/en-us/lib...ryptography.hashalgorithm_members(VS.71).aspx

If I am not wrong it should be used the Clear method instead of the
protected Disposed method:
Clear: Releases all resources used by the HashAlgorithm.
Dispose: Releases the unmanaged resources used by the HashAlgorithm
and optionally releases the managed resources.

I ran the code and got the following results:
Matching test OK
Not matching test OK
200 tests took 123145 mSec
Average 0.615725 seconds per hash

I just kept the 250 000 repeats as you used.
But in this project I will use a lower value to not be so heavy to the
CPU and because there is not really important data to protect but in
future projects this will be really useful.

Any advice for the length I should keep on the database for the
Password and Salt columns? I will make both varbinary.

This is the working code:

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

namespace SashImplementation {

public class Program {

public static void Main(String[] args) {

Sash sash = new Sash(new SHA256Managed(), 250000);
try {

// Test matching
Byte[] password1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
Byte[] salt = sash.GenerateSalt(64);
Byte[] firstHash = sash.CalculateHash(password1, salt);
Byte[] password2 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
if (sash.CheckHash(password2, salt, firstHash)) {
Console.WriteLine("Matching test OK");
} else {
Console.WriteLine("Matching test failed.");
}

// Test not matching
password1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
salt = sash.GenerateSalt(64);
firstHash = sash.CalculateHash(password1, salt);
password2 = new byte[] { 1, 1, 2, 3, 4, 5, 6, 7 };
if (sash.CheckHash(password2, salt, firstHash)) {
Console.WriteLine("Not matching test failed.");
} else {
Console.WriteLine("Not matching test OK.");
}

// Timer test
Stopwatch sw = new Stopwatch();
int numTests = 200;
sw.Start();
for (int i = 0; i < numTests; ++i) {
password1[0] = (byte)i;
firstHash = sash.CalculateHash(password1, salt);
} // end for
sw.Stop();

Console.WriteLine("{0} tests took {1} mSec.", numTests,
sw.ElapsedMilliseconds);
Console.WriteLine("Average {0} seconds per hash.",
sw.ElapsedMilliseconds / (numTests * 1000.0));

// Keep console on screen
Console.Write("Press [Enter] to continue... ");
Console.ReadLine();


} finally {
sash.Dispose();
}

} // end Main()

} // end class HashTest

// Sash
/// <summary>
/// Implements salted hash for managed has algorithms
/// </summary>
public sealed class Sash : IDisposable {

#region Fields

private Boolean disposed = false;
private readonly Int32 repeats;
private readonly HashAlgorithm algorithm;

private static Int32 defaultRepeats = 10000;
private static HashAlgorithm defaultAlgorithm = new SHA256Managed
();
private static RNGCryptoServiceProvider rng = new
RNGCryptoServiceProvider();

#endregion // Fields

#region Constructors

// Sash
/// <summary>
/// Initialize sash with custom hash algorithm and repeats
/// </summary>
public Sash(HashAlgorithm algorithm, Int32 repeats) {

// Check parameters
if (algorithm == null) throw new ArgumentNullException
("algorithm", "Hash algorithm cannot be null");
if (repeats <= 0) throw new ArgumentOutOfRangeException
("repeats", "Repeats must be positive");

// Define fields
this.algorithm = algorithm;
this.repeats = repeats;

} // Sash

// Sash
/// <summary>
/// Initialize sash with default algorithm, SHA256Managed, and
custom repeats value
/// </summary>
public Sash(Int32 repeats)
: this(defaultAlgorithm, repeats) {
} // Sash

// Sash
/// <summary>
/// Initialize sash with custom algorithm and default repeats
equal to 10000
/// </summary>
public Sash(HashAlgorithm algorithm)
: this(algorithm, defaultRepeats) {
} // Sash

// Sash
/// <summary>
/// Initialize sash with defaul algorithm, SHA256Managed, and
default repeats equal to 10000
/// </summary>
public Sash()
: this(defaultAlgorithm, defaultRepeats) {
} // Sash

#endregion // Constructors

#region Methods

// GenerateSalt
/// <summary>
/// Generate random salt
/// </summary>
public Byte[] GenerateSalt(Int32 size) {
Byte[] salt = new Byte[size];
rng.GetBytes(salt);
return salt;
} // GenerateSalt

// CalculateHash
/// <summary>
/// Calculate hash from a hash algorithm and salt
/// </summary>
public Byte[] CalculateHash(Byte[] data, Byte[] salt) {

// Check parameters
if (data == null) throw new ArgumentNullException("data", "Data
cannot be null");
if (data.Length == 0) throw new ArgumentOutOfRangeException
("data", "Data cannot be empty");
if (salt == null) throw new ArgumentNullException("salt", "Salt
cannot be null");
if (salt.Length == 0) throw new ArgumentOutOfRangeException
("salt", "Salt cannot be empty");

// Calculate hash
Byte[] hash;
hash = algorithm.ComputeHash(Concat(data, salt));
for (Int32 i = 0; i < repeats; ++i) {
hash = algorithm.ComputeHash(Concat(hash, salt));
}

// Clear data
for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0;
}

return hash;

} // CalculateHash

// CheckHash
/// <summary>
/// Check hash against expected
/// </summary>
public Boolean CheckHash(Byte[] data, Byte[] salt, Byte[]
expected) {

// Check parameters
if (data == null) throw new ArgumentNullException("data", "Data
cannot be null");
if (data.Length == 0) throw new ArgumentOutOfRangeException
("data", "Data cannot be empty");
if (salt == null) throw new ArgumentNullException("salt", "Salt
cannot be null");
if (salt.Length == 0) throw new ArgumentOutOfRangeException
("salt", "Salt cannot be empty");
if (expected == null) throw new ArgumentNullException
("expected", "Expected cannot be null");
if (expected.Length != algorithm.HashSize / 8) throw new
ArgumentOutOfRangeException("expected", "Expected length should be one
eight of algorithm hash size");

// CalculateHash
Byte[] actual = CalculateHash(data, salt);
for (Int32 i = 0; i < actual.Length; ++i) {
if (actual != expected) { return false; }
}
return true;

} // CheckHash

// Dispose
/// <summary>
/// Dispose resources
/// </summary>
public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
} // Dispose

// Dispose
/// <summary>
/// Dispose resources
/// </summary>
public void Dispose(Boolean disposing) {

// Already disposed
if (disposed) return;

// Dispose managed resources
if (disposing) {
if (algorithm != null) algorithm.Clear();
}

// Dispose unmanaged resources

// Redefine disposed
disposed = true;

} // Dispose

#region Private

// Concat
/// <summary>
/// Concat two byte arrays
/// </summary>
private Byte[] Concat(byte[] lhs, byte[] rhs) {
Byte[] result = new Byte[lhs.Length + rhs.Length];
Array.Copy(lhs, result, lhs.Length);
Array.Copy(rhs, 0, result, lhs.Length, rhs.Length);
return result;
} // Concat

#endregion // Private

#endregion // Methods

} // Sash

} // SashImplementation
 
A

Arne Vajhøj

rossum said:
Correct, security costs. If you want the security then you pay the
cost.

True.

But:
- using SHA-256 instead of MD5
- using a per user random salt
- enforce long passwords
- enforce more possible characters in passwords
all gives a lot increase security for a very small cost (you
pay 10% more in CPU usage and brute force becomes 10000 times
more expensive).

The repeated hashing is a different type of change because
it cost a lot (10000 times more CPU usage and brute force
becomes 10000 times more expensive).
To quote Bruce Schneier: "There are already enough insecure
fast systems, we don't need another one." How much will it cost if an
attacker gets access to the passwords? How much will it cost to add
some more CPU to the server farm? Those are questions for the OP to
answer. If a court case ensues after loss of data then it is
important to be able to show that best security practice was being
used.

That is the sort of thing that the OP has to assess. We do not know
that value of the data protected by the passwords.

One important note is that the original poster does not know
the value of the data being protected either.

If someone gets away with the hashed passwords from the database
they will most likely also have gotten away with all the other
data in the database.

What good hashing protects against is the hackers finding the
plain text password and use it at other sites where the users
has used the same password.

The original poster has no way of knowing whether the users
are smart enough to only use the same password at one site
or if they just use this password at unimportant web sites or
if they use the same password at their bank and credit card
company.

Arne
 
A

Arne Vajhøj

shapper said:
In this case the most important data I have for each user its their
email ... In this case security is not a "big issue" ...

The big issue is if your users use the same password at their
internet banking as at your site.
But I would like to have a secure and correct way to do this ...

That is good.
I can then, from project to project, change the REPEATS from 25 000 to
10 000 or just 1 according to the level of security and processor cost
for each situation ...

You could.

Or you could try and enforce long random passwords which has a
similar effect but are much cheaper.

Note though that users tend to dislike such attempts.

Arne
 
A

Arne Vajhøj

Peter said:
Sealing is not required, but if you have no reason to subclass your
class it prevents an attacker writing their own subclass and inserting
it into production code:

@Override
public byte[] CalculateHash(byte[] password, byte[] salt) {
copy_to_my_secret_data_capture_file(username, password);
return super.CalculateHash(password, salt);
}
[...]

I'll suggest that sealing a class to prevent such an attack is
pointless. The "sealed" keyword is useful as a maintenance construct,
to prevent certain kinds of uses of one's code so as to avoid bugs.

But if you've got an _attacker_, trying to co-opt some class, the
"sealed" keyword isn't going to prevent sub-classing or otherwise
reusing the base class. They can just hack around it in a variety of ways.

Absolutely.

Decompile with reflector, delete sealed keyword and recompile takes
just seconds.

Arne
 
J

Jesse Houwing

* rossum wrote, On 16-9-2009 21:19:
That would explain the problems with trying to call Dispose directly.

You'll probably see that Dispose calls Clear as well. But dispose is
probably implemented explicitly, which means that in order to call
Dispose you first need to cast the object to IDisposable. It's nice to
know this in case you need to clean up a whole list of Disposable
objects. I sometimes tend to pass them in a foreach loop.

The proper syntax would be:

HashAlgorithm x
try
{
x = new ...();
...
...
...
}
finally
{
((IDisposable)x).Dispose(); // Same as x.Clear();
}

You whould also be able to use

using(HashAlgorithm x = new ...())
{
...
...
...
}

Which will clean up after itself.
Yes, repeats needs to be changed each time the code is moved to a
new/upgraded machine.

Can't you just start a StopWatch and return after x time has elapsed?
That would make it suitable for any machine adding little overhead.

What I don't understand is why Thread.Sleep(.5sec) won't suffice...

Is this to make sure a really random hash is created? Or is it just to
make sure that you cannot compute too many hashes at the same time? If
the latter, shouldn't this method by synced or locked in any way? You
could just start 80 threads/requests in parallel and much of the use of
this delay is voided. If any of the threads returns with a response, you
know you've found the right combination.

Do you have a link that explains why you should do this? And it makes me
wonder why the HashProvider hasn't got a parameter that says, keep
hashing till x time has elapsed if it's standard practice.
Just trying to learn here :).
Password lengths should be specified in either company policy or user
instructions. Six or eight characters is a minimum with a maximum of
fifteen or twenty for passwords or 100 for passphrases. You may also
want to enforce the use of mixed case, digits and non-alphanum
characters.

The length of the salt can be chosen by yourself. (max 32 bytes as
Rossum says. The length of the resulting Hashed password depends on the
algorithm used. I'd just create the ma length of the longest posible
hash and pad any smaller hashes with 'blank spaces'.
 
S

shapper

The big issue is if your users use the same password at their
internet banking as at your site.

That is a good point. I haven't think about it ...
Or you could try and enforce long random passwords which has a
similar effect but are much cheaper.
Note though that users tend to dislike such attempts.

Enforce a random password to the user? Even I don't like it :)

I think it would be more suitable to force the user to have a password
with length bigger than 10 characters and in some cases to have
numbers, letters a punctuation mark like a dot or something ...
 
S

shapper

Password lengths should be specified in either company policy or user
instructions.  Six or eight characters is a minimum with a maximum of
fifteen or twenty for passwords or 100 for passphrases.  You may also
want to enforce the use of mixed case, digits and non-alphanum
characters.

When you say passphrases you mean that when using passwords a space
shouldn't be allowed?
I am just wondering if I should include that in validation of the
form.
I do think that you should stay with a fixed length salt.  If not then
the minumum length is 8 bytes (64 bits) and the maximum 32 bytes (256
bits)

Yes yes I will keep salt a fixed length. I was just wondering the
minimum and maximum size of passwords and salt also to be able to
define a proper length on the SQL table columns.

Thank You,
Miguel
 
S

Scott Seligman

rossum said:
Yes, a password has no spaces while a passphrase may do so.
Passphrases are usually longer, which should make them more secure.

Why shouldn't a password have spaces?

For that matter, why have limit on password lengths?
 
S

shapper

Why shouldn't a password have spaces?

That's what I thought ... For me a password is a password.
With spaces, numbers, punctuation, etc ... I can restrict the the
allowed characters but I always saw it as a password.
For that matter, why have limit on password lengths?

Usually I set a minimum limit and for maximum minimum usually set a
reasonable value to store in a table column without having a lot of
records with a lot of empty space ...
.... I usually go for 40.
 
S

Scott Seligman

shapper said:
Usually I set a minimum limit and for maximum minimum usually set a
reasonable value to store in a table column without having a lot of
records with a lot of empty space ...
... I usually go for 40.

While I do think there are arguments for sanity-check type upper limits
(mostly having to do with moving the passwords around, I don't want to
try and hash a 1gb password, for instance), you shouldn't be storing
them in a database at all, only the hash of passwords.
 
S

Scott Seligman

rossum said:
Human memory. It is easier to memorise a long phrase than a long
string of nonsense.

Sure, but how does limiting the password length or
preventing the user from entering spaces help?
 
A

Arne Vajhøj

shapper said:
That is a good point. I haven't think about it ...

It is important.
Enforce a random password to the user? Even I don't like it :)

I think it would be more suitable to force the user to have a password
with length bigger than 10 characters and in some cases to have
numbers, letters a punctuation mark like a dot or something ...

A password of 10 characters is not really long after todays
security standards.

You should try and calculate how many possible
passwords some schemes can generate.

A 12 character long random password from A-Za-z0-9 has
3E21 possible values.

A password of an English word with a 2 digits suffix
has about 1 million possible values.

Arne
 
A

Arne Vajhøj

Scott said:
Why shouldn't a password have spaces?

For that matter, why have limit on password lengths?

Words do not have spaces - phrases does.

So by adding the option of spaces the password becomes
a passphrase.

It is about English not about software.

Arne
 
S

Scott Seligman

=?ISO-8859-1?Q?Arne_Vajh=F8j?= said:
Words do not have spaces - phrases does.

So by adding the option of spaces the password becomes
a passphrase.

A word does not have a special number, mixed case or numbers.
It is about English not about software.

Okay, have fun.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top