3
votes

I'm writing a C# frontend to a legacy database that uses VB6 Rnd() and Randomize() methods for user password encryption. The encryption function is very simplistic and really not all that secure, but it's what all current passwords are stored with.

What I'd like to be able 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 that can be duplicated in C#. But I don't want to require current users to have had their password reset before they can use the new frontend.

Is there any way I can reimplement that algorithm in C# so that it produces identical results to the legacy VB6 code?

3
Sure it's possible, but some sample code of the VB6 would be needed to help with the translation.JaredPar
It's already linked in the question.Tom Mayfield
Rather than attempt to exactly match the unspecified behaviour of VB6, why not write a control in VB6 that does what you want, and then build a runtime-callable-wrapper around that code so that you can call it from your C# program?Eric Lippert

3 Answers

2
votes

It should be possible. The tricky part will be emulating the calls to Visual Basic's Randomize statement and Rnd function.

I just found a knowledge base article that looks like it might have the information that you'll need:

How Visual Basic Generates Pseudo-Random Numbers for the RND Function

EDIT...

After some investigation, it appears that the Randomize and Rnd implementations in recent versions of Visual Basic use exactly the same algorithms as VB6.

So, the good news is that you don't need to figure out and re-implement the VB6 algorithms yourself. Just import the Microsoft.VisualBasic namespace 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 actual algorithms used, you can always take a look in Reflector!)

1
votes

You can generate the same sequence from VB6 and C#. Just watch our for rounding errors (the results from C# are more precise). Make sure to call VBMath.Rnd(-1) before passing in 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);
        }
    }
}
0
votes

Sample VB code :

Randomize()
Dim x as Single = Rnd()

(roughly) equivalent C# code :

Random r = new Random();
double x = r.NextDouble();

The Random class constructor initializes the random number generator with the current time, which is what Randomize does. You can also pass a seed to the constructor, which is equivalent to calling Randomize with a seed parameter