random.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // Copyright 2011-2014 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. "crypto/hmac"
  7. "crypto/rand"
  8. "crypto/sha256"
  9. "io"
  10. )
  11. // idLen is a length of captcha id string.
  12. // (20 bytes of 62-letter alphabet give ~119 bits.)
  13. const idLen = 20
  14. // idChars are characters allowed in captcha id.
  15. var idChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
  16. // rngKey is a secret key used to deterministically derive seeds for
  17. // PRNGs used in image and audio. Generated once during initialization.
  18. var rngKey [32]byte
  19. func init() {
  20. if _, err := io.ReadFull(rand.Reader, rngKey[:]); err != nil {
  21. panic("captcha: error reading random source: " + err.Error())
  22. }
  23. }
  24. // Purposes for seed derivation. The goal is to make deterministic PRNG produce
  25. // different outputs for images and audio by using different derived seeds.
  26. const (
  27. imageSeedPurpose = 0x01
  28. audioSeedPurpose = 0x02
  29. )
  30. // deriveSeed returns a 16-byte PRNG seed from rngKey, purpose, id and digits.
  31. // Same purpose, id and digits will result in the same derived seed for this
  32. // instance of running application.
  33. //
  34. // out = HMAC(rngKey, purpose || id || 0x00 || digits) (cut to 16 bytes)
  35. //
  36. func deriveSeed(purpose byte, id string, digits []byte) (out [16]byte) {
  37. var buf [sha256.Size]byte
  38. h := hmac.New(sha256.New, rngKey[:])
  39. h.Write([]byte{purpose})
  40. io.WriteString(h, id)
  41. h.Write([]byte{0})
  42. h.Write(digits)
  43. sum := h.Sum(buf[:0])
  44. copy(out[:], sum)
  45. return
  46. }
  47. // RandomDigits returns a byte slice of the given length containing
  48. // pseudorandom numbers in range 0-9. The slice can be used as a captcha
  49. // solution.
  50. func RandomDigits(length int) []byte {
  51. return randomBytesMod(length, 10)
  52. }
  53. // randomBytes returns a byte slice of the given length read from CSPRNG.
  54. func randomBytes(length int) (b []byte) {
  55. b = make([]byte, length)
  56. if _, err := io.ReadFull(rand.Reader, b); err != nil {
  57. panic("captcha: error reading random source: " + err.Error())
  58. }
  59. return
  60. }
  61. // randomBytesMod returns a byte slice of the given length, where each byte is
  62. // a random number modulo mod.
  63. func randomBytesMod(length int, mod byte) (b []byte) {
  64. if length == 0 {
  65. return nil
  66. }
  67. if mod == 0 {
  68. panic("captcha: bad mod argument for randomBytesMod")
  69. }
  70. maxrb := 255 - byte(256%int(mod))
  71. b = make([]byte, length)
  72. i := 0
  73. for {
  74. r := randomBytes(length + (length / 4))
  75. for _, c := range r {
  76. if c > maxrb {
  77. // Skip this number to avoid modulo bias.
  78. continue
  79. }
  80. b[i] = c % mod
  81. i++
  82. if i == length {
  83. return
  84. }
  85. }
  86. }
  87. }
  88. // randomId returns a new random id string.
  89. func randomId() string {
  90. b := randomBytesMod(idLen, byte(len(idChars)))
  91. for i, c := range b {
  92. b[i] = idChars[c]
  93. }
  94. return string(b)
  95. }