Converting VB6 Randomize to C #
I'm writing a C # interface to a legacy database that uses VB6's Rnd () and Randomize () methods to encrypt a user's password. The encryption function is very simplistic and really not everything is secure, but this is what all current passwords are stored.
What I would like to do is authenticate legacy users from a C # application. I can write new encryption (or preferably hashing) code for VB6 so that all future users have a more secure password and can be duplicated in C #. But I don't want current users to have their password reset before they can use the new interface.
Is there any way to implement this algorithm in C # so that it gives identical results to legacy VB6 code?
source to share
You can generate the same sequence from VB6 and C #. Just watch our rounding errors (results are more accurate with C #). Be sure to call VBMath.Rnd(-1)
before transferring a new seed to VBMath.Randomize()
.
[TestFixture]
public class VbaRandomTests
{
// Random numbers generated from a known seed from VB6
[TestCase(1, new[] { 0.333575300f, 0.068163870f, 0.593829300f, 0.766039500f, 0.189289400f, 0.537398600f, 0.326994400f, 0.393937000f, 0.073419150f, 0.831542500f, 0.854963000f, 0.828829900f, 0.962344000f, 0.833957400f, 0.090149820f, 0.645974500f, 0.192794900f, 0.346950500f, 0.188133400f, 0.691135000f })]
[TestCase(32, new[] { 0.579913200f, 0.579150200f, 0.310870300f, 0.864916400f, 0.142658500f, 0.927291200f, 0.407316600f, 0.402970200f, 0.296319500f, 0.412841300f, 0.361066500f, 0.560519300f, 0.017275630f, 0.919162500f, 0.084534590f, 0.912820200f, 0.642257800f, 0.248561900f, 0.733299400f, 0.305637000f })]
[TestCase(327680, new[] { 0.882708600f, 0.733264000f, 0.661029000f, 0.376940400f, 0.919086800f, 0.660506500f, 0.020170630f, 0.126908200f, 0.437005600f, 0.053283210f, 0.252240800f, 0.449496400f, 0.662844500f, 0.044955970f, 0.519654200f, 0.169961300f, 0.183334400f, 0.687831900f, 0.227989400f, 0.384067200f })]
public void generates_same_results_as_VB6(int seed, float[] values)
{
VBMath.Rnd(-1);
VBMath.Randomize(seed);
float[] results = new float[values.Length];
for (int index = 0; index < results.Length; index++)
{
results[index] = VBMath.Rnd();
}
CollectionAssert.AreEqual(values, results, new FloatEpsilonComparer(0.0000001f));
}
private class FloatEpsilonComparer
: IComparer<float>, IComparer
{
private readonly float _epsilon;
public FloatEpsilonComparer(float epsilon)
{
_epsilon = epsilon;
}
public int Compare(float x, float y)
{
float difference = x - y;
if (Math.Abs(difference) < _epsilon)
{
return 0;
}
if (x < y)
{
return -1;
}
return 1;
}
public int Compare(object x, object y)
{
float xF = Convert.ToSingle(x);
float yF = Convert.ToSingle(y);
return Compare(xF, yF);
}
}
}
source to share
It should be possible. The tricky part will emulate the Visual Basic operator calls Randomize
and Rnd
.
I just found a knowledge base article that looks like it might have the information you need:
How Visual Basic generates pseudo-random numbers for the RND function
EDIT ...
After some research it was found that the implementation Randomize
and Rnd
in the latest versions of Visual Basic use the same algorithms as VB6.
So, the good news is that you don't have to define and reimplement VB6 algorithms yourself. Just import the namespace Microsoft.VisualBasic
and you can call the built-in methods from C #:
using Microsoft.VisualBasic;
// ...
float x = VBMath.Rnd(-1);
VBMath.Randomize(password.Length);
float y = VBMath.Rnd();
// etc
(And if you're still curious about the algorithms used, you can always take a look at Reflector!)
source to share
Sample VB code:
Randomize()
Dim x as Single = Rnd()
(roughly) equivalent C # code:
Random r = new Random();
double x = r.NextDouble();
The class constructor Random
initializes the random number generator with the current time, which is what Randomize does. You can also pass the seed to the constructor, which is equivalent to calling Randomize with the seed parameter
source to share