8

Generate 6-digit code for phone verification, The following is a very simple approach that I have used

package main

import ( 
    "fmt"
    "math/rand"
    "time"
)        

var randowCodes = [...]byte{
    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
}        

func main() {
    var r *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))

    for i := 0; i < 3; i++ {
        var pwd []byte = make([]byte, 6)

        for j := 0; j < 6; j++ {
            index := r.Int() % len(randowCodes)

            pwd[j] = randowCodes[index]
        }

        fmt.Printf("%s\n", string(pwd))                                                                  
    }    
} 

Do you have a better way to do this?

fishu
  • 317
  • 1
  • 4
  • 12
  • This question would be more appropriate on http://codereview.stackexchange.com/ – cartant Sep 14 '16 at 03:09
  • 1
    Possible duplicate of [How to generate a random string of a fixed length in golang?](http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang) – icza Sep 14 '16 at 08:19

7 Answers7

14

You may use "crypto/rand" package: which implements a cryptographically secure pseudorandom number generator. (try on The Go Playground):

package main

import (
    "crypto/rand"
    "fmt"
    "io"
)

func main() {
    for i := 0; i < 3; i++ {
        fmt.Println(EncodeToString(6))
    }
}

func EncodeToString(max int) string {
    b := make([]byte, max)
    n, err := io.ReadAtLeast(rand.Reader, b, max)
    if n != max {
        panic(err)
    }
    for i := 0; i < len(b); i++ {
        b[i] = table[int(b[i])%len(table)]
    }
    return string(b)
}

var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}

output:

640166
195174
221966

And see: How to generate a random string of a fixed length in golang?

Community
  • 1
  • 1
  • 5
    The results are no longer random due to the random byte being mod by %10. It is much more likely to see digit 0 to 5 than 6 to 9 because 256 is not evenly divisible by the modulus, the max value is 255%10 = 5 which means for a complete byte, 0-5 appears 26 times each but digits 6-9 appear only 25 times. – Tom Anderson Jun 21 '21 at 03:51
  • this is generating same numbers on each run, not dynamic. – Deepak Pathak Mar 19 '22 at 04:40
8

I've forked user6169399's answer, using crypto/rand with const string and small modifications, this is the result:

import (
    "crypto/rand"
)

const otpChars = "1234567890"

func GenerateOTP(length int) (string, error) {
    buffer := make([]byte, length)
    _, err := rand.Read(buffer)
    if err != nil {
        return "", err
    }

    otpCharsLength := len(otpChars)
    for i := 0; i < length; i++ {
        buffer[i] = otpChars[int(buffer[i])%otpCharsLength]
    }

    return string(buffer), nil
}
Jossef Harush Kadouri
  • 32,361
  • 10
  • 130
  • 129
1
  1. make use of math/rand:
func genCaptchaCode() string {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))

    var codes [6]byte
    for i := 0; i < 6; i++ {
        codes[i] = uint8(48 + r.Intn(10))
    }

    return string(codes[:])
}
  1. make use of crypto/rand (more security):
func genCaptchaCode() (string, error) {
    codes := make([]byte, 6)
    if _, err := rand.Read(codes); err != nil {
        return "", err
    }

    for i := 0; i < 6; i++ {
        codes[i] = uint8(48 + (codes[i] % 10))
    }

    return string(codes), nil
}
David
  • 3,843
  • 33
  • 36
1

I think it is the easiest way:

// generate new recovery code
t := fmt.Sprint(time.Now().Nanosecond())

fmt.Println(t[:6])

output:

524339
743142
243470

function use:

func GenerateCode() string {
    return fmt.Sprint(time.Now().Nanosecond())[:6]
}

output:

302663
477258
678557
0

You can use rand.Int() from crypto/rand to also generate randomness

import (
    "crypto/rand"
)

func GenerateOTPCode(length int) (string, error) {
    seed := "012345679"
    byteSlice := make([]byte, length)

    for i := 0; i < length; i++ {
        max := big.NewInt(int64(len(seed)))
        num, err := rand.Int(rand.Reader, max)
        if err != nil {
            return "", err
        }

        byteSlice[i] = seed[num.Int64()]
    }

    return string(byteSlice), nil
}
0

I needed a bit simpler and flat solution so I came up with this

// Since Go 1.20 rand.Seed() deprecated

rand.New(rand.NewSource(time.Now().UnixNano()))

// generates a random number in the range of [0, 900000)
// We add 100000 to the result to ensure the minimum value is 100000
r := rand.Intn(900000) + 100000

fmt.Println(r)

a side note: rand.Intn(900000) never generates 900000 the max number is 899999 so the code never generates 1,000,000

AH.Pooladvand
  • 1,944
  • 2
  • 12
  • 26
0

To account for the observation given by @Tom Anderson, a more appropriate answer using real uniform distribution and a simpler code would be:

func GenerateOTP(maxDigits uint32) string {
    bi, err := rand.Int(
        rand.Reader,
        big.NewInt(int64(math.Pow(10, float64(maxDigits)))),
    )
    if err != nil {
        panic(err)
    }
    return fmt.Sprintf("%0*d", maxDigits, bi)
}

go playground