random.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. // Copyright 2011 Dmitry Chestnykh. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package captcha
  5. import (
  6. crand "crypto/rand"
  7. "io"
  8. "rand"
  9. "time"
  10. )
  11. // idLen is a length of captcha id string.
  12. const idLen = 20
  13. // idChars are characters allowed in captcha id.
  14. var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
  15. func init() {
  16. rand.Seed(time.Nanoseconds())
  17. }
  18. // RandomDigits returns a byte slice of the given length containing
  19. // pseudorandom numbers in range 0-9. The slice can be used as a captcha
  20. // solution.
  21. func RandomDigits(length int) []byte {
  22. return randomBytesMod(length, 10)
  23. }
  24. // randomBytes returns a byte slice of the given length read from CSPRNG.
  25. func randomBytes(length int) (b []byte) {
  26. b = make([]byte, length)
  27. if _, err := io.ReadFull(crand.Reader, b); err != nil {
  28. panic("captcha: error reading random source: " + err.String())
  29. }
  30. return
  31. }
  32. // randomBytesMod returns a byte slice of the given length, where each byte is
  33. // a random number modulo mod.
  34. func randomBytesMod(length int, mod byte) (b []byte) {
  35. b = make([]byte, length)
  36. maxrb := byte(256 - (256 % int(mod)))
  37. i := 0
  38. for {
  39. r := randomBytes(length + (length / 4))
  40. for _, c := range r {
  41. if c >= maxrb {
  42. // Skip this number to avoid modulo bias.
  43. continue
  44. }
  45. b[i] = c % mod
  46. i++
  47. if i == length {
  48. return
  49. }
  50. }
  51. }
  52. panic("unreachable")
  53. }
  54. // randomId returns a new random id string.
  55. func randomId() string {
  56. b := randomBytesMod(idLen, byte(len(idChars)))
  57. for i, c := range b {
  58. b[i] = idChars[c]
  59. }
  60. return string(b)
  61. }
  62. // rnd returns a non-crypto pseudorandom int in range [from, to].
  63. func rnd(from, to int) int {
  64. return rand.Intn(to+1-from) + from
  65. }
  66. // rndf returns a non-crypto pseudorandom float64 in range [from, to].
  67. func rndf(from, to float64) float64 {
  68. return (to-from)*rand.Float64() + from
  69. }