dateMatchers.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package matching
  2. import (
  3. "regexp"
  4. "strconv"
  5. "strings"
  6. "github.com/nbutton23/zxcvbn-go/entropy"
  7. "github.com/nbutton23/zxcvbn-go/match"
  8. )
  9. const (
  10. DATESEP_MATCHER_NAME = "DATESEP"
  11. DATEWITHOUTSEP_MATCHER_NAME = "DATEWITHOUT"
  12. )
  13. func FilterDateSepMatcher(m match.Matcher) bool {
  14. return m.ID == DATESEP_MATCHER_NAME
  15. }
  16. func FilterDateWithoutSepMatcher(m match.Matcher) bool {
  17. return m.ID == DATEWITHOUTSEP_MATCHER_NAME
  18. }
  19. func checkDate(day, month, year int64) (bool, int64, int64, int64) {
  20. if (12 <= month && month <= 31) && day <= 12 {
  21. day, month = month, day
  22. }
  23. if day > 31 || month > 12 {
  24. return false, 0, 0, 0
  25. }
  26. if !((1900 <= year && year <= 2019) || (0 <= year && year <= 99)) {
  27. return false, 0, 0, 0
  28. }
  29. return true, day, month, year
  30. }
  31. func dateSepMatcher(password string) []match.Match {
  32. dateMatches := dateSepMatchHelper(password)
  33. var matches []match.Match
  34. for _, dateMatch := range dateMatches {
  35. match := match.Match{
  36. I: dateMatch.I,
  37. J: dateMatch.J,
  38. Entropy: entropy.DateEntropy(dateMatch),
  39. DictionaryName: "date_match",
  40. Token: dateMatch.Token,
  41. }
  42. matches = append(matches, match)
  43. }
  44. return matches
  45. }
  46. func dateSepMatchHelper(password string) []match.DateMatch {
  47. var matches []match.DateMatch
  48. matcher := regexp.MustCompile(DATE_RX_YEAR_SUFFIX)
  49. for _, v := range matcher.FindAllString(password, len(password)) {
  50. splitV := matcher.FindAllStringSubmatch(v, len(v))
  51. i := strings.Index(password, v)
  52. j := i + len(v)
  53. day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
  54. month, _ := strconv.ParseInt(splitV[0][2], 10, 16)
  55. year, _ := strconv.ParseInt(splitV[0][6], 10, 16)
  56. match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j, Token: password[i:j]}
  57. matches = append(matches, match)
  58. }
  59. matcher = regexp.MustCompile(DATE_RX_YEAR_PREFIX)
  60. for _, v := range matcher.FindAllString(password, len(password)) {
  61. splitV := matcher.FindAllStringSubmatch(v, len(v))
  62. i := strings.Index(password, v)
  63. j := i + len(v)
  64. day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
  65. month, _ := strconv.ParseInt(splitV[0][6], 10, 16)
  66. year, _ := strconv.ParseInt(splitV[0][2], 10, 16)
  67. match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j, Token: password[i:j]}
  68. matches = append(matches, match)
  69. }
  70. var out []match.DateMatch
  71. for _, match := range matches {
  72. if valid, day, month, year := checkDate(match.Day, match.Month, match.Year); valid {
  73. match.Pattern = "date"
  74. match.Day = day
  75. match.Month = month
  76. match.Year = year
  77. out = append(out, match)
  78. }
  79. }
  80. return out
  81. }
  82. type DateMatchCandidate struct {
  83. DayMonth string
  84. Year string
  85. I, J int
  86. }
  87. type DateMatchCandidateTwo struct {
  88. Day string
  89. Month string
  90. Year string
  91. I, J int
  92. }
  93. func dateWithoutSepMatch(password string) []match.Match {
  94. dateMatches := dateWithoutSepMatchHelper(password)
  95. var matches []match.Match
  96. for _, dateMatch := range dateMatches {
  97. match := match.Match{
  98. I: dateMatch.I,
  99. J: dateMatch.J,
  100. Entropy: entropy.DateEntropy(dateMatch),
  101. DictionaryName: "date_match",
  102. Token: dateMatch.Token,
  103. }
  104. matches = append(matches, match)
  105. }
  106. return matches
  107. }
  108. //TODO Has issues with 6 digit dates
  109. func dateWithoutSepMatchHelper(password string) (matches []match.DateMatch) {
  110. matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH)
  111. for _, v := range matcher.FindAllString(password, len(password)) {
  112. i := strings.Index(password, v)
  113. j := i + len(v)
  114. length := len(v)
  115. lastIndex := length - 1
  116. var candidatesRoundOne []DateMatchCandidate
  117. if length <= 6 {
  118. //2-digit year prefix
  119. candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[2:], v[0:2], i, j))
  120. //2-digityear suffix
  121. candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex-2], v[lastIndex-2:], i, j))
  122. }
  123. if length >= 6 {
  124. //4-digit year prefix
  125. candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[4:], v[0:4], i, j))
  126. //4-digit year sufix
  127. candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex-3], v[lastIndex-3:], i, j))
  128. }
  129. var candidatesRoundTwo []DateMatchCandidateTwo
  130. for _, c := range candidatesRoundOne {
  131. if len(c.DayMonth) == 2 {
  132. candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:0], c.DayMonth[1:1], c.Year, c.I, c.J))
  133. } else if len(c.DayMonth) == 3 {
  134. candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:2], c.DayMonth[2:2], c.Year, c.I, c.J))
  135. candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:0], c.DayMonth[1:3], c.Year, c.I, c.J))
  136. } else if len(c.DayMonth) == 4 {
  137. candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:2], c.DayMonth[2:4], c.Year, c.I, c.J))
  138. }
  139. }
  140. for _, candidate := range candidatesRoundTwo {
  141. intDay, err := strconv.ParseInt(candidate.Day, 10, 16)
  142. if err != nil {
  143. continue
  144. }
  145. intMonth, err := strconv.ParseInt(candidate.Month, 10, 16)
  146. if err != nil {
  147. continue
  148. }
  149. intYear, err := strconv.ParseInt(candidate.Year, 10, 16)
  150. if err != nil {
  151. continue
  152. }
  153. if ok, _, _, _ := checkDate(intDay, intMonth, intYear); ok {
  154. matches = append(matches, match.DateMatch{Token: password, Pattern: "date", Day: intDay, Month: intMonth, Year: intYear, I: i, J: j})
  155. }
  156. }
  157. }
  158. return matches
  159. }
  160. func buildDateMatchCandidate(dayMonth, year string, i, j int) DateMatchCandidate {
  161. return DateMatchCandidate{DayMonth: dayMonth, Year: year, I: i, J: j}
  162. }
  163. func buildDateMatchCandidateTwo(day, month string, year string, i, j int) DateMatchCandidateTwo {
  164. return DateMatchCandidateTwo{Day: day, Month: month, Year: year, I: i, J: j}
  165. }