|
package main
import (
"crypto/sha512"
"fmt"
"log"
"regexp"
"strings"
)
type Site struct {
Host string `json:host`
MinimumLength int `json:minimumLength`
MaximumLength int `json:maximumLength`
SpecialCharacters string `json:specialCharacters`
NumberOfSpecialCharacters int `json:numberOfSpecialCharacters`
NumberOfUpperCase int `json:numberOfUpperCase`
NumberOfDigits int `json:numberOfDigits`
Revision int `json:revision`
Password string `json:",omitempty"`
}
// Generate the passphrase
func (s *Site) generatePassphrase(profile, passphrase string) []byte {
clearText := fmt.Sprintf(
"%s-%s-%s-%s",
strings.ToLower(profile),
strings.ToLower(passphrase),
strings.ToLower(s.Host),
s.Revision)
sha := sha512.New()
sha.Write([]byte(clearText))
return s.applyCriteria(sha.Sum(nil))
}
func (s *Site) applyCriteria(sha []byte) []byte {
hash := []byte(fmt.Sprintf("%x", sha))
if !containsUppercase(hash, s.NumberOfUpperCase) {
i := 0
r := regexp.MustCompile(`[a-z]+`)
var matches [][]int
if matches = r.FindAllIndex(hash, -1); matches != nil {
for _, v := range matches {
if i < s.NumberOfUpperCase {
c := strings.ToUpper(string(hash[v[0]]))
hash[v[0]] = []byte(c)[0]
i += 1
}
}
}
}
if !containsDigits(hash, s.NumberOfDigits) {
i := 0
r := regexp.MustCompile(`[a-z]+`)
var matches [][]int
if matches = r.FindAllIndex(hash, -1); matches != nil {
for _, v := range matches {
if i < s.NumberOfDigits {
hash[v[0]] = byte(i)
i += 1
}
}
}
}
if !containsSpecialCharacters(hash, s.SpecialCharacters, s.NumberOfSpecialCharacters) {
i := 0
r := regexp.MustCompile(`[0-9]+`)
var matches [][]int
if matches = r.FindAllIndex(hash, -1); matches != nil {
for _, v := range matches {
if i < s.NumberOfSpecialCharacters {
i += 1
hash[v[0]] = []byte(s.SpecialCharacters)[len(s.SpecialCharacters)-i]
}
}
}
}
// If there is a maximum length truncate the hash
if s.MaximumLength > -1 {
hash = hash[:s.MaximumLength]
}
// Ensure the length is adequate
if !validateLength(hash, s.MinimumLength, s.MaximumLength) {
log.Println("Does not meed the length requirements")
}
return hash
}
// Determine if the hash currently contains the appropriate amount of digits
func containsDigits(source []byte, minOccurrences int) bool {
r := regexp.MustCompile(`\d`)
var matches [][]byte
if matches = r.FindAll(source, -1); matches == nil {
return false
}
return len(matches) >= minOccurrences
}
// Determine if the hash currently contains the appropriate amount of uppercase characters
func containsUppercase(source []byte, minOccurrences int) bool {
r := regexp.MustCompile(`[A-Z]+`)
var matches [][]byte
if matches = r.FindAll(source, -1); matches == nil {
return false
}
return len(matches) >= minOccurrences
}
// Determine if the hash currently contains the appropriate amount of special characters from the allowed
// character set
func containsSpecialCharacters(source []byte, specialCharacters string, minOccurrences int) bool {
s := specialCharacters
s = strings.Replace(s, "\\", "\\\\", -1)
s = strings.Replace(s, ".", "\\.", -1)
s = strings.Replace(s, " ", "\\s", -1)
s = strings.Replace(s, "-", "\\-", -1)
s = strings.Replace(s, "[", "\\[", -1)
s = strings.Replace(s, "]", "\\]", -1)
r := regexp.MustCompile(`[` + s + `]+`)
var matches [][]byte
if matches = r.FindAll(source, -1); matches == nil {
return false
}
return len(matches) >= minOccurrences
}
// Determine if the hash currently abides by the length restrictions
func validateLength(source []byte, minimum, maximum int) bool {
if minimum > -1 && len(source) < minimum {
return false
}
if maximum > -1 && len(source) > maximum {
return false
}
return true
}
|