3

I've a scenario to create OTP token for a system. So am trying to use Random function with seed in the following way.

Public Shared Function generateOTP(seed As Integer) As Integer
    Dim rand As Random
    rand = New Random(seed)
    Return rand.Next(100000, 999999)
End Function

Its working fine in the way as I expected. When I pass a value say 10 it returns the same 6 digit number always.

But my question is is there is any chance the same random number could be generated for a different number? Another question is, does the random number will be same for the same number in all computers the application is running. I mean is there is any processor dependency or time dependency for the random function with seed?

Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
Sandeep Thomas
  • 4,303
  • 14
  • 61
  • 132
  • 1
    If you want to be sure of the same set of values from a given seed, you should consider the Mersenne Twister – Ňɏssa Pøngjǣrdenlarp Jan 22 '16 at 16:40
  • @Plutonix It will be great if you explain a little. – Sandeep Thomas Jan 22 '16 at 16:51
  • [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister) – Ňɏssa Pøngjǣrdenlarp Jan 22 '16 at 16:55
  • A random number generator *must* be allowed to generate the same first random number from a different seed. If it is not allowed then it is not random enough. Password generators should use a cryptographically strong random number generator. Don't make your own, use the RNGCryptoServiceProvider class. – Hans Passant Jan 22 '16 at 17:05
  • I just reread the post and this seems like an XY - do you *really* want random numbers or *unique* numbers? Maybe a Base26 hash of some cryptographically random bytes would work better. I am trying to picture how generating the same values would work - why would you want to generate the same token as before for a new case? The first one might still be in use/active – Ňɏssa Pøngjǣrdenlarp Jan 22 '16 at 17:11

2 Answers2

2

I've a scenario to create OTP token for a system

If that is the case, I dont understand why you would want to use the same set of randoms, ever. Perhaps it is for use in a 2 party system, so you need the server and client to use the same values? If so, read on.


There is already an algorithm for One Time Passwords (RFC4226). All the posts here are C# and/or sketchy (...having already created foo and bar, for step 4 do this: (some code)). I cannot find one that encodes the token/value the same way that the RFC calls for.

With some hints, helps and alterations to the references listed below, and mainly based on the RFC Appendix C, here is a VB version.

Note that the RFC describes a 2 party system. A client generates an OTP from a shared secret key (ignore the contradiction) and a "moving value" or sequence of values. The server then can validate by using the secret key and the next value. If it validates, then it moves the counter to the next in the series. Apparently, these OTPs are immediately used otherwise the server could not validate Client B until Client A's is "redeemed".

No one seems interested in that part, just the part about representing a hash in 6 digits with a very low chance of duplication. The RFC also has a provision for a check digit which no one seems interested in.

This one is also mainly interested in the core action of a 6 digit hash, so it is not RFC4226 compliant. The aspect of getting the value from a series of values could be added.

Partial Public Class CryptoTools
    Private Const TicksPerSec = 10000000
    Private Const TicksPerMilli = 10000
    
    ' generate your own key
    Private Shared SecretKey As String = "PoNodJHz4jjVG6...UuiIvmA=="
    

The secret key is described later. (This comment is really to reduce indentation.)

Public Shared Function GetOneTimePass(somevalue As UInt64,
                                     Optional size As Int32 = 6) As String
    ' undo how the key was made
    Dim Key() As Byte = Convert.FromBase64String(SecretKey)
    Dim hashData As Byte()

    ' encode the value to Data...called text[] in RFC
    ' this part seems missing in most SO answers
    Dim Data(7) As Byte
    For n As Int32 = Data.Length - 1 To 0 Step -1
        Data(n) = CByte(CLng(somevalue) And &HFF)
        somevalue = somevalue >> 8
    Next

    ' create hasher with our key; hash
    Using hasher As New HMACSHA1(Key)
        hashData = hasher.ComputeHash(Data)
    End Using

    ' THIS is the part everyone likes
    ' see links and https://www.rfc-editor.org/rfc/rfc4226#section-5.4
    Dim offset = hashData(hashData.Length - 1) And &HF
    Dim binCode = (hashData(offset) And &H7F) << 24 Or
                (hashData(offset + 1) And &HFF) << 16 Or
               (hashData(offset + 2) And &HFF) << 8 Or
               (hashData(offset + 3) And &HFF)

    Dim otp As UInt64
    ' RFC: 6 is minimim size, 10 is my max
    Dim sz = If(size < 6, 6,
               If(size > 10, 10, size))

    otp = Convert.ToUInt64(binCode Mod Math.Pow(10, sz))

    ' optional check digit
    ' no one seems to implement; not included
    Dim check = CalculateCheckSum(otp, size)
    'Return otp.ToString().PadLeft(sz, "0"c) & String.Format("-{0}", check.ToString)
    ' pad to minimum length

    ' work in progress: only works out to 7 chars
    'Console.WriteLine(ConvertToBase26(otp))

    Return otp.ToString().PadLeft(sz, "0"c)
End Function

Public Shared Function GetTimeToken() As UInt64
    Dim t As UInt64 = Convert.ToUInt64(DateTime.Now.Ticks / TicksPerMilli)
    Return t
End Function

Public Shared Function GetRandomUInt64() As UInt64
    Dim rBytes(7) As Byte
    Using crng As New RNGCryptoServiceProvider()
        crng.GetBytes(rBytes)
        Return BitConverter.ToUInt64(rBytes, 0)
    End Using
End Function

##Usage:

Dim token As UInt64 = CryptoTools.GetTimeToken()
Dim otp As String = CryptoTools.GetOneTimePass(token)

Dim rToken As UInt64 = CryptoTools.GetRandomUInt64()
Dim otp As String = CryptoTools.GetOneTimePass(rToken)

##Notes

  • The recommended size of the secret key for the HMACSHA1 hasher is 64 bytes. To create your key, use RNGCryptoServiceProvider to create an array of 64 random bytes then Convert.ToBase64String().
  • This requires a UInt64. For a "real" 2-party 4226 version, the value should be the next in a sequence of values. Otherwise...
  • The value can come from anywhere: it can be the time or a random number. GetTimeToken() is a utility function to get the time as a UInt64. GetRandomUInt64() returns a crypto random UInt64 for use as the starting value.
  • Most of the SO links above do not seem to encode the value/time/data quite the way the RFC calls for, if at all. One post mentions adding salt. But encoding the value passed as per RFC 4226 Appendix C seems critical to the result (which might explain the salt).
  • I've only run a little over 2000 iterations, but saw no dupes ever (even when the values differed by only a millisecond).
  • The RFC specifies a 6 digit minimum, which this implements, but can be longer. This allows 6-10 digits, but over 6 is hard to read, consider inserting a separator for 8 or more.

The RFC states that it is desirable that the HOTP value be 'numeric only' (Sec 4). Not that it cannot be alpha-numeric. Personally, it seems that if the result was encoded in the form of WYQ77-8WYB9 it would be easier (except maybe on phones). I can't get the result to convert to more than 7 characters.

As stated earlier, this is not RFC4226 compliant, just quite like it. Test it to death. It looks right, it seems right, it matches other versions and/or the RFC, but is presented As Is.


Added the GetRandomUInt64() and GetTimeToken() methods.

References:

Community
  • 1
  • 1
Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
0

is is there is any chance the same random number could be generated for a different number?

yes, underlying algorithm could be changed in newer .NET version, so after update another sequence will be generated

does the random number will be same for the same number in all computers?

if all computers use the same library version - yes.

one thing to note: standard random does not have additional "entropy thread". some libraries will start additional thread which will re-seed random's state and this state is mixed into seed value (so, you will have different sequences even for the same seed)

Iłya Bursov
  • 23,342
  • 4
  • 33
  • 57
  • But if our code runs in framework 4.0 and it never gonna change.. Sorry I didnt understand you mentioned about "after update" Does Library version will changes in same framework? – Sandeep Thomas Jan 22 '16 at 16:53
  • @sforsandeep yes, 4.0 is major version, actual assemblies (dll) files could change. also 4.0 has 4.0.1, 4.0.2 and 4.03 – Iłya Bursov Jan 22 '16 at 17:04