random.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. // Package random holds a few functions for working with random numbers
  2. package random
  3. import (
  4. cryptorand "crypto/rand"
  5. "encoding/base64"
  6. "fmt"
  7. "io"
  8. )
  9. // StringFn create a random string for test purposes using the random
  10. // number generator function passed in.
  11. //
  12. // Do not use these for passwords.
  13. func StringFn(n int, randReader io.Reader) string {
  14. const (
  15. vowel = "aeiou"
  16. consonant = "bcdfghjklmnpqrstvwxyz"
  17. digit = "0123456789"
  18. )
  19. var (
  20. pattern = []string{consonant, vowel, consonant, vowel, consonant, vowel, consonant, digit}
  21. out = make([]byte, n)
  22. p = 0
  23. )
  24. _, err := io.ReadFull(randReader, out)
  25. if err != nil {
  26. panic(fmt.Sprintf("internal error: failed to read from random reader: %v", err))
  27. }
  28. for i := range out {
  29. source := pattern[p]
  30. p = (p + 1) % len(pattern)
  31. // this generation method means the distribution is slightly biased. However these
  32. // strings are not for passwords so this is deemed OK.
  33. out[i] = source[out[i]%byte(len(source))]
  34. }
  35. return string(out)
  36. }
  37. // String create a random string for test purposes.
  38. //
  39. // Do not use these for passwords.
  40. func String(n int) string {
  41. return StringFn(n, cryptorand.Reader)
  42. }
  43. // Password creates a crypto strong password which is just about
  44. // memorable. The password is composed of printable ASCII characters
  45. // from the URL encoding base64 alphabet (A-Za-z0-9_-).
  46. //
  47. // Requires password strength in bits.
  48. // 64 is just about memorable
  49. // 128 is secure
  50. func Password(bits int) (password string, err error) {
  51. bytes := bits / 8
  52. if bits%8 != 0 {
  53. bytes++
  54. }
  55. var pw = make([]byte, bytes)
  56. n, err := cryptorand.Read(pw)
  57. if err != nil {
  58. return "", fmt.Errorf("password read failed: %w", err)
  59. }
  60. if n != bytes {
  61. return "", fmt.Errorf("password short read: %d", n)
  62. }
  63. password = base64.RawURLEncoding.EncodeToString(pw)
  64. return password, nil
  65. }