예제 #1
0
func DateEntropy(dateMatch match.DateMatch) float64 {
	var entropy float64
	if dateMatch.Year < 100 {
		entropy = math.Log2(NUM_DAYS * NUM_MONTHS * 100)
	} else {
		entropy = math.Log2(NUM_DAYS * NUM_MONTHS * NUM_YEARS)
	}

	if dateMatch.Separator != "" {
		entropy += 2 //add two bits for separator selection [/,-,.,etc]
	}
	return entropy
}
예제 #2
0
func SequenceEntropy(match match.Match, dictionaryLength int, ascending bool) float64 {
	firstChar := match.Token[0]
	baseEntropy := float64(0)
	if string(firstChar) == "a" || string(firstChar) == "1" {
		baseEntropy = float64(0)
	} else {
		baseEntropy = math.Log2(float64(dictionaryLength))
		//TODO: should this be just the first or any char
		if unicode.IsUpper(rune(firstChar)) {
			baseEntropy++
		}
	}

	if !ascending {
		baseEntropy++
	}
	return baseEntropy + math.Log2(float64(len(match.Token)))
}
예제 #3
0
func SpatialEntropy(match match.Match, turns int, shiftCount int) float64 {
	var s, d float64
	if match.DictionaryName == "qwerty" || match.DictionaryName == "dvorak" {
		//todo: verify qwerty and dvorak have the same length and degree
		s = float64(len(adjacency.BuildQwerty().Graph))
		d = adjacency.BuildQwerty().CalculateAvgDegree()
	} else {
		s = float64(KEYPAD_STARTING_POSITIONS)
		d = KEYPAD_AVG_DEGREE
	}

	possibilities := float64(0)

	length := float64(len(match.Token))

	//TODO: Should this be <= or just < ?
	//Estimate the number of possible patterns w/ length L or less with t turns or less
	for i := float64(2); i <= length+1; i++ {
		possibleTurns := math.Min(float64(turns), i-1)
		for j := float64(1); j <= possibleTurns+1; j++ {
			x := zxcvbn_math.NChoseK(i-1, j-1) * s * math.Pow(d, j)
			possibilities += x
		}
	}

	entropy := math.Log2(possibilities)
	//add extra entropu for shifted keys. ( % instead of 5 A instead of a)
	//Math is similar to extra entropy for uppercase letters in dictionary matches.

	if S := float64(shiftCount); S > float64(0) {
		possibilities = float64(0)
		U := length - S

		for i := float64(0); i < math.Min(S, U)+1; i++ {
			possibilities += zxcvbn_math.NChoseK(S+U, i)
		}

		entropy += math.Log2(possibilities)
	}

	return entropy
}
예제 #4
0
func extraUpperCaseEntropy(match match.Match) float64 {
	word := match.Token

	allLower := true

	for _, char := range word {
		if unicode.IsUpper(char) {
			allLower = false
			break
		}
	}
	if allLower {
		return float64(0)
	}

	//a capitalized word is the most common capitalization scheme,
	//so it only doubles the search space (uncapitalized + capitalized): 1 extra bit of entropy.
	//allcaps and end-capitalized are common enough too, underestimate as 1 extra bit to be safe.

	for _, regex := range []string{START_UPPER, END_UPPER, ALL_UPPER} {
		matcher := regexp.MustCompile(regex)

		if matcher.MatchString(word) {
			return float64(1)
		}
	}
	//Otherwise calculate the number of ways to capitalize U+L uppercase+lowercase letters with U uppercase letters or
	//less. Or, if there's more uppercase than lower (for e.g. PASSwORD), the number of ways to lowercase U+L letters
	//with L lowercase letters or less.

	countUpper, countLower := float64(0), float64(0)
	for _, char := range word {
		if unicode.IsUpper(char) {
			countUpper++
		} else if unicode.IsLower(char) {
			countLower++
		}
	}
	totalLenght := countLower + countUpper
	var possibililities float64

	for i := float64(0); i <= math.Min(countUpper, countLower); i++ {
		possibililities += float64(zxcvbn_math.NChoseK(totalLenght, i))
	}

	if possibililities < 1 {
		return float64(1)
	}

	return float64(math.Log2(possibililities))
}
예제 #5
0
func ExtraLeetEntropy(match match.Match, password string) float64 {
	var subsitutions float64
	var unsub float64
	subPassword := password[match.I:match.J]
	for index, char := range subPassword {
		if string(char) != string(match.Token[index]) {
			subsitutions++
		} else {
			//TODO: Make this only true for 1337 chars that are not subs?
			unsub++
		}
	}

	var possibilities float64

	for i := float64(0); i <= math.Min(subsitutions, unsub)+1; i++ {
		possibilities += zxcvbn_math.NChoseK(subsitutions+unsub, i)
	}

	if possibilities <= 1 {
		return float64(1)
	}
	return math.Log2(possibilities)
}
예제 #6
0
/*
Returns minimum entropy

    Takes a list of overlapping matches, returns the non-overlapping sublist with
    minimum entropy. O(nm) dp alg for length-n password with m candidate matches.
*/
func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntropyMatch {
	bruteforceCardinality := float64(entropy.CalcBruteForceCardinality(password))
	upToK := make([]float64, len(password))
	backPointers := make([]match.Match, len(password))

	for k := 0; k < len(password); k++ {
		upToK[k] = get(upToK, k-1) + math.Log2(bruteforceCardinality)

		for _, match := range matches {
			if match.J != k {
				continue
			}

			i, j := match.I, match.J
			//			see if best entropy up to i-1 + entropy of match is less that current min at j
			upTo := get(upToK, i-1)
			calculatedEntropy := match.Entropy
			match.Entropy = calculatedEntropy
			candidateEntropy := upTo + calculatedEntropy

			if candidateEntropy < upToK[j] {
				upToK[j] = candidateEntropy
				match.Entropy = candidateEntropy
				backPointers[j] = match
			}
		}
	}

	//walk backwards and decode the best sequence
	var matchSequence []match.Match
	passwordLen := len(password)
	passwordLen--
	for k := passwordLen; k >= 0; {
		match := backPointers[k]
		if match.Pattern != "" {
			matchSequence = append(matchSequence, match)
			k = match.I - 1

		} else {
			k--
		}

	}
	sort.Sort(match.Matches(matchSequence))

	makeBruteForceMatch := func(i, j int) match.Match {
		return match.Match{Pattern: "bruteforce",
			I:       i,
			J:       j,
			Token:   password[i : j+1],
			Entropy: math.Log2(math.Pow(bruteforceCardinality, float64(j-i)))}

	}

	k := 0
	var matchSequenceCopy []match.Match
	for _, match := range matchSequence {
		i, j := match.I, match.J
		if i-k > 0 {
			matchSequenceCopy = append(matchSequenceCopy, makeBruteForceMatch(k, i-1))
		}
		k = j + 1
		matchSequenceCopy = append(matchSequenceCopy, match)
	}

	if k < len(password) {
		matchSequenceCopy = append(matchSequenceCopy, makeBruteForceMatch(k, len(password)-1))
	}
	var minEntropy float64
	if len(password) == 0 {
		minEntropy = float64(0)
	} else {
		minEntropy = upToK[len(password)-1]
	}

	crackTime := roundToXDigits(entropyToCrackTime(minEntropy), 3)
	return MinEntropyMatch{Password: password,
		Entropy:          roundToXDigits(minEntropy, 3),
		MatchSequence:    matchSequenceCopy,
		CrackTime:        crackTime,
		CrackTimeDisplay: displayTime(crackTime),
		Score:            crackTimeToScore(crackTime)}

}
예제 #7
0
func DictionaryEntropy(match match.Match, rank float64) float64 {
	baseEntropy := math.Log2(rank)
	upperCaseEntropy := extraUpperCaseEntropy(match)
	//TODO: L33t
	return baseEntropy + upperCaseEntropy
}
예제 #8
0
func RepeatEntropy(match match.Match) float64 {
	cardinality := CalcBruteForceCardinality(match.Token)
	entropy := math.Log2(cardinality * float64(len(match.Token)))

	return entropy
}
예제 #9
0
func YearEntropy(dateMatch match.DateMatch) float64 {
	return math.Log2(NUM_YEARS)
}