Personal book of passwords

site.go 3.8KB

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