Personal book of passwords

site.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package main
  2. import (
  3. "crypto/sha512"
  4. "fmt"
  5. "log"
  6. "math"
  7. "regexp"
  8. "strings"
  9. )
  10. type Site struct {
  11. Host string `json:host`
  12. MinimumLength int `json:minimumLength`
  13. MaximumLength int `json:maximumLength`
  14. SpecialCharacters string `json:specialCharacters`
  15. NumberOfSpecialCharacters int `json:numberOfSpecialCharacters`
  16. NumberOfUpperCase int `json:numberOfUpperCase`
  17. NumberOfDigits int `json:numberOfDigits`
  18. Revision int `json:revision`
  19. Password string `json:",omitempty"`
  20. }
  21. // Generate the passphrase
  22. func (s *Site) generatePassphrase(profile, passphrase string) []byte {
  23. clearText := fmt.Sprintf(
  24. "%s-%s-%s-%s",
  25. strings.ToLower(profile),
  26. strings.ToLower(passphrase),
  27. strings.ToLower(s.Host),
  28. s.Revision)
  29. sha := sha512.New()
  30. sha.Write([]byte(clearText))
  31. return s.applyCriteria(sha.Sum(nil))
  32. }
  33. func (s *Site) applyCriteria(sha []byte) []byte {
  34. hash := []byte(fmt.Sprintf("%x", sha))
  35. hashUpper := []byte(fmt.Sprintf("%X", sha))
  36. if !containsUppercase(hash, s.NumberOfUpperCase) {
  37. i := 0
  38. r := regexp.MustCompile(`[a-z]+`)
  39. var matches [][]int
  40. if matches = r.FindAllIndex(hash, -1); matches != nil {
  41. for _, v := range matches {
  42. if i < s.NumberOfUpperCase {
  43. c := string(hashUpper[v[0]])
  44. hash[v[0]] = []byte(c)[0]
  45. i += 1
  46. }
  47. }
  48. }
  49. }
  50. if !containsDigits(hash, s.NumberOfDigits) {
  51. i := 0
  52. r := regexp.MustCompile(`[a-z]+`)
  53. var matches [][]int
  54. if matches = r.FindAllIndex(hash, -1); matches != nil {
  55. for _, v := range matches {
  56. if i < s.NumberOfDigits {
  57. ceiling := float64(10)
  58. hash[v[0]] = byte(ceiling - math.Mod(float64(i), ceiling) - 1)
  59. i += 1
  60. }
  61. }
  62. }
  63. }
  64. if !containsSpecialCharacters(hash, s.SpecialCharacters, s.NumberOfSpecialCharacters) {
  65. i := 0
  66. r := regexp.MustCompile(`[0-9]+`)
  67. l := len(s.SpecialCharacters)
  68. var matches [][]int
  69. if matches = r.FindAllIndex(hash, -1); matches != nil {
  70. for _, v := range matches {
  71. if i < s.NumberOfSpecialCharacters {
  72. i += 1
  73. idx := l - int(math.Mod(float64(i), float64(l)))
  74. hash[v[0]] = []byte(s.SpecialCharacters)[idx]
  75. }
  76. }
  77. }
  78. }
  79. // If there is a maximum length truncate the hash
  80. if s.MaximumLength > -1 {
  81. hash = hash[:s.MaximumLength]
  82. }
  83. // Ensure the length is adequate
  84. if !validateLength(hash, s.MinimumLength, s.MaximumLength) {
  85. log.Println("Does not meed the length requirements")
  86. }
  87. return hash
  88. }
  89. // Determine if the hash currently contains the appropriate amount of digits
  90. func containsDigits(source []byte, minOccurrences int) bool {
  91. r := regexp.MustCompile(`\d`)
  92. var matches [][]byte
  93. if matches = r.FindAll(source, -1); matches == nil {
  94. return false
  95. }
  96. return len(matches) >= minOccurrences
  97. }
  98. // Determine if the hash currently contains the appropriate amount of uppercase characters
  99. func containsUppercase(source []byte, minOccurrences int) bool {
  100. r := regexp.MustCompile(`[A-Z]+`)
  101. var matches [][]byte
  102. if matches = r.FindAll(source, -1); matches == nil {
  103. return false
  104. }
  105. return len(matches) >= minOccurrences
  106. }
  107. // Determine if the hash currently contains the appropriate amount of special characters from the allowed
  108. // character set
  109. func containsSpecialCharacters(source []byte, specialCharacters string, minOccurrences int) bool {
  110. s := specialCharacters
  111. s = strings.Replace(s, "\\", "\\\\", -1)
  112. s = strings.Replace(s, ".", "\\.", -1)
  113. s = strings.Replace(s, " ", "\\s", -1)
  114. s = strings.Replace(s, "-", "\\-", -1)
  115. s = strings.Replace(s, "[", "\\[", -1)
  116. s = strings.Replace(s, "]", "\\]", -1)
  117. r := regexp.MustCompile(`[` + s + `]+`)
  118. var matches [][]byte
  119. if matches = r.FindAll(source, -1); matches == nil {
  120. return false
  121. }
  122. return len(matches) >= minOccurrences
  123. }
  124. // Determine if the hash currently abides by the length restrictions
  125. func validateLength(source []byte, minimum, maximum int) bool {
  126. if minimum > -1 && len(source) < minimum {
  127. return false
  128. }
  129. if maximum > -1 && len(source) > maximum {
  130. return false
  131. }
  132. return true
  133. }