Как я могу сгенерировать случайный int, используя пакет "crypto/rand"?

Скажем, я хотел бы создать безопасный случайный int между 0 и 27, используя:

func Int(rand io.Reader, max *big.Int) (n *big.Int, err error)

в пакете "crypto/rand".

Как мне это сделать?

Я действительно не понимаю, как это работает, почему он не возвращает один из встроенных в Go объектов вместо указателя на какой-то большой. Тип типа?

EDIT:

Будет ли это считаться достаточно безопасным для токенов?

func getToken(length int) string {
    token := ""
    codeAlphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    codeAlphabet += "abcdefghijklmnopqrstuvwxyz"
    codeAlphabet += "0123456789"

    for i := 0; i < length; i++ {
        token += string(codeAlphabet[cryptoRandSecure(int64(len(codeAlphabet)))])
    }
    return token
}

func cryptoRandSecure(max int64) int64 {
    nBig, err := rand.Int(rand.Reader, big.NewInt(max))
    if err != nil {
        log.Println(err)
    }
    return nBig.Int64()
}

func main() {
    fmt.Println(getToken(32))
}

Это приведет к следующему:

qZDbuPwNQGrgVmZCU9A7FUWbp8eIfn0Z

EwZVoQ5D5SEfdhiRsDfH6dU6tAovILCZ

cOqzODVP0GwbiNBwtmqLA78rFgV9d3VT

Ответ 1

Если вы создаете защищенные токены для идентификаторов сеансов, токенов-маркеров OAuth, CSRF или подобных: вы хотите сгенерировать токен (в идеале) 256 бит (32 байта) или не менее 192 бит (24 байта).

Токен со значениями между (0-27) может быть грубым-принудительным менее чем за секунду и не может считаться безопасным.

например.

package main

import (
    "crypto/rand"
    "encoding/base64"
)

// GenerateRandomBytes returns securely generated random bytes.
// It will return an error if the system secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomBytes(n int) ([]byte, error) {
    b := make([]byte, n)
    _, err := rand.Read(b)
    // Note that err == nil only if we read len(b) bytes.
    if err != nil {
        return nil, err
    }

    return b, nil
}

// GenerateRandomString returns a URL-safe, base64 encoded
// securely generated random string.
func GenerateRandomString(s int) (string, error) {
    b, err := GenerateRandomBytes(s)
    return base64.URLEncoding.EncodeToString(b), err
}

func main() {
    // Example: this will give us a 44 byte, base64 encoded output
    token, err := GenerateRandomString(32)
    if err != nil {
        // Serve an appropriately vague error to the
        // user, but log the details internally.
    }
}

Выход base64 безопасен для заголовков, HTTP-форм, тел JSON и т.д.

Если вам нужно целое число, это может помочь объяснить ваш прецедент, так как было бы странно, если бы система требовала tokens как ints.

Ответ 2

Вот какой рабочий код:

package main

import (
    "fmt"
    "crypto/rand"
    "math/big"
)

func main() {
    nBig, err := rand.Int(rand.Reader, big.NewInt(27))
    if err != nil {
        panic(err)
    }
    n := nBig.Int64()
    fmt.Printf("Here is a random %T in [0,27) : %d\n", n, n)
}

Но чтобы создать случайный токен, я бы сделал что-то вроде этого:

package main

import (
    "crypto/rand"
    "encoding/base32"
    "fmt"
)

func main() {
    token := getToken(10)
    fmt.Println("Here is a random token : ", token)
}

func getToken(length int) string {
    randomBytes := make([]byte, 32)
    _, err := rand.Read(randomBytes)
    if err != nil {
        panic(err)
    }
    return base32.StdEncoding.EncodeToString(randomBytes)[:length]
}

Ответ 3

Если вам нужно только небольшое число (т.е. [0, 255]), вы можете просто прочитать байты из пакета Reader:

b := []byte{0}
if _, err := rand.Reader.Read(b); err != nil {
    panic(err)
}
n := b[0]
fmt.Println(n)

Игровая площадка: http://play.golang.org/p/4VO52LiEVh (пример там не работает, я не знаю, работает ли она по назначению или это игровая площадка ошибка).