Skip to content

Commit

Permalink
refactor: Initialize all Strength properties via constructor
Browse files Browse the repository at this point in the history
- Deprecated the default constructor of Strength in favor of initializing all properties through a parameterized constructor.
- Deprecated Scoring's mostGuessableMatchSequence method due to the changes in Strength's initialization.

This change encourages the use of the constructor for setting all properties of the Strength class and discourages partial or post-instantiation modifications.
  • Loading branch information
vvatanabe committed Sep 2, 2023
1 parent a184426 commit 641ccff
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 30 deletions.
24 changes: 24 additions & 0 deletions src/main/java/com/nulabinc/zxcvbn/MatchSequence.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.nulabinc.zxcvbn;

import com.nulabinc.zxcvbn.matchers.Match;
import java.util.Collections;
import java.util.List;

public class MatchSequence {

private final List<Match> sequence;
private final double guesses;

public MatchSequence(List<Match> sequence, double guesses) {
this.sequence = Collections.unmodifiableList(sequence);
this.guesses = guesses;
}

public List<Match> getSequence() {
return sequence;
}

public double getGuesses() {
return guesses;
}
}
58 changes: 48 additions & 10 deletions src/main/java/com/nulabinc/zxcvbn/Scoring.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,63 @@ public Scoring(Context context) {
this.context = context;
}

/**
* Calculates the most guessable match sequence for a password.
*
* @deprecated Use {@link #calculateMostGuessableMatchSequence} instead for better clarity and
* maintainability.
*/
@Deprecated
public Strength mostGuessableMatchSequence(CharSequence password, List<Match> matches) {
return mostGuessableMatchSequence(password, matches, false);
}

/**
* Calculates the most guessable match sequence for a password with an option to exclude additive.
*
* @deprecated Use {@link #calculateMostGuessableMatchSequence} instead for better clarity and
* maintainability.
*/
@Deprecated
public Strength mostGuessableMatchSequence(
CharSequence password, List<Match> matches, boolean excludeAdditive) {
MatchSequence matchSequence =
calculateMostGuessableMatchSequence(password, matches, excludeAdditive);
return new Strength(password, matchSequence.getGuesses(), matchSequence.getSequence(), 0);
}

/**
* Calculates the most guessable match sequence for a password.
*
* @param password The password to evaluate.
* @param matches A list of matches detected in the password.
* @return A MatchSequence containing the most guessable sequence and associated guesses.
*/
public MatchSequence calculateMostGuessableMatchSequence(
CharSequence password, List<Match> matches) {
return calculateMostGuessableMatchSequence(password, matches, false);
}

/**
* Calculates the most guessable match sequence for a password with an option to exclude additive.
*
* @param password The password to evaluate.
* @param matches A list of matches detected in the password.
* @param excludeAdditive If true, excludes additive computations from the guess estimation.
* @return A MatchSequence containing the most guessable sequence and associated guesses.
*/
public MatchSequence calculateMostGuessableMatchSequence(
CharSequence password, List<Match> matches, boolean excludeAdditive) {
List<List<Match>> matchesByEndPosition = groupMatchesByEndPosition(password.length(), matches);
Optimal optimal = computeOptimal(context, password, matchesByEndPosition, excludeAdditive);
List<Match> optimalMatchSequence = unwindOptimal(password.length(), optimal);
double guesses =
password.length() == 0
? 1
: optimal.getOverallMetric(password.length() - 1, optimalMatchSequence.size());
Strength strength = new Strength();
strength.setPassword(password);
strength.setGuesses(guesses);
strength.setGuessesLog10(log10(guesses));
strength.setSequence(optimalMatchSequence);
return strength;
double guesses = 0;
if (password.length() == 0) {
guesses = 1;
} else {
guesses = optimal.getOverallMetric(password.length() - 1, optimalMatchSequence.size());
}
return new MatchSequence(optimalMatchSequence, guesses);
}

private static List<List<Match>> groupMatchesByEndPosition(int length, List<Match> matches) {
Expand Down
101 changes: 101 additions & 0 deletions src/main/java/com/nulabinc/zxcvbn/Strength.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,56 @@ public class Strength {
private List<Match> sequence;
private long calcTime;

/**
* Default constructor.
*
* @deprecated This constructor is discouraged from use as it does not ensure all fields are
* initialized properly. Instead, use the {@link #Strength(CharSequence, double, List, long)}
* constructor to provide all necessary data.
*/
@Deprecated
public Strength() {
this.sequence = new ArrayList<>();
}

/**
* Constructs a Strength object with the given parameters.
*
* @param password The password for which strength is calculated.
* @param guesses Estimated number of guesses needed to crack the password.
* @param sequence A list of matching patterns found in the password.
* @param calcTime Time taken to calculate the password's strength.
*/
public Strength(CharSequence password, double guesses, List<Match> sequence, long calcTime) {
this.password = password;
this.guesses = guesses;
this.guessesLog10 = Scoring.log10(guesses);

if (sequence == null) {
sequence = new ArrayList<>();
}
this.sequence = sequence;

AttackTimes attackTimes = TimeEstimates.estimateAttackTimes(guesses);
this.crackTimeSeconds = attackTimes.getCrackTimeSeconds();
this.crackTimesDisplay = attackTimes.getCrackTimesDisplay();
this.score = attackTimes.getScore();
this.feedback = Feedback.getFeedback(attackTimes.getScore(), sequence);

this.calcTime = calcTime;
}

public CharSequence getPassword() {
return password;
}

/**
* Sets the password.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setPassword(CharSequence password) {
this.password = password;
}
Expand All @@ -32,6 +74,13 @@ public double getGuesses() {
return guesses;
}

/**
* Sets the estimated number of guesses.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setGuesses(double guesses) {
this.guesses = guesses;
}
Expand All @@ -40,6 +89,13 @@ public double getGuessesLog10() {
return guessesLog10;
}

/**
* Sets the logarithm (base 10) of the estimated number of guesses.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setGuessesLog10(double guessesLog10) {
this.guessesLog10 = guessesLog10;
}
Expand All @@ -48,6 +104,13 @@ public AttackTimes.CrackTimeSeconds getCrackTimeSeconds() {
return crackTimeSeconds;
}

/**
* Sets the crack time in seconds.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setCrackTimeSeconds(AttackTimes.CrackTimeSeconds crackTimeSeconds) {
this.crackTimeSeconds = crackTimeSeconds;
}
Expand All @@ -56,6 +119,13 @@ public AttackTimes.CrackTimesDisplay getCrackTimesDisplay() {
return crackTimesDisplay;
}

/**
* Sets the display times for crack attempts.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setCrackTimesDisplay(AttackTimes.CrackTimesDisplay crackTimesDisplay) {
this.crackTimesDisplay = crackTimesDisplay;
}
Expand All @@ -64,6 +134,13 @@ public int getScore() {
return score;
}

/**
* Sets the score.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setScore(int score) {
this.score = score;
}
Expand All @@ -72,6 +149,13 @@ public Feedback getFeedback() {
return feedback;
}

/**
* Sets the feedback.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setFeedback(Feedback feedback) {
this.feedback = feedback;
}
Expand All @@ -80,14 +164,31 @@ public List<Match> getSequence() {
return sequence;
}

/**
* Sets the sequence of matches.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setSequence(List<Match> sequence) {
if (sequence == null) {
sequence = new ArrayList<>();
}
this.sequence = sequence;
}

public long getCalcTime() {
return calcTime;
}

/**
* Sets the calculation time.
*
* @deprecated Use constructor for initialization. Modifying after instantiation is not
* recommended.
*/
@Deprecated
public void setCalcTime(long calcTime) {
this.calcTime = calcTime;
}
Expand Down
19 changes: 5 additions & 14 deletions src/main/java/com/nulabinc/zxcvbn/Zxcvbn.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,12 @@ public Strength measure(CharSequence password, List<String> sanitizedInputs) {
lowerSanitizedInputs = Collections.emptyList();
}
long start = time();
Matching matching = createMatching(lowerSanitizedInputs);
Matching matching = new Matching(context, lowerSanitizedInputs);
List<Match> matches = matching.omnimatch(password);
Scoring scoring = new Scoring(this.context);
Strength strength = scoring.mostGuessableMatchSequence(password, matches);
strength.setCalcTime(time() - start);
AttackTimes attackTimes = TimeEstimates.estimateAttackTimes(strength.getGuesses());
strength.setCrackTimeSeconds(attackTimes.getCrackTimeSeconds());
strength.setCrackTimesDisplay(attackTimes.getCrackTimesDisplay());
strength.setScore(attackTimes.getScore());
strength.setFeedback(Feedback.getFeedback(strength.getScore(), strength.getSequence()));
return strength;
}

protected Matching createMatching(List<String> lowerSanitizedInputs) {
return new Matching(this.context, lowerSanitizedInputs);
Scoring scoring = new Scoring(context);
MatchSequence matchSequence = scoring.calculateMostGuessableMatchSequence(password, matches);
long end = time() - start;
return new Strength(password, matchSequence.getGuesses(), matchSequence.getSequence(), end);
}

private long time() {
Expand Down
14 changes: 9 additions & 5 deletions src/main/java/com/nulabinc/zxcvbn/matchers/RepeatMatcher.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.nulabinc.zxcvbn.matchers;

import com.nulabinc.zxcvbn.Context;
import com.nulabinc.zxcvbn.MatchSequence;
import com.nulabinc.zxcvbn.Matching;
import com.nulabinc.zxcvbn.Scoring;
import com.nulabinc.zxcvbn.Strength;
import com.nulabinc.zxcvbn.WipeableString;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -91,13 +91,17 @@ private CharSequence deriveBaseTokenFromGreedyMatchResult(String greedyMatchResu

private Match createRepeatMatch(CharSequence baseToken, String matchResult, int start, int end) {
List<Match> omnimatch = matching.omnimatch(baseToken);
Strength baseAnalysis = scoring.mostGuessableMatchSequence(baseToken, omnimatch);
List<Match> baseMatches = baseAnalysis.getSequence();
double baseGuesses = baseAnalysis.getGuesses();
MatchSequence baseAnalysis = scoring.calculateMostGuessableMatchSequence(baseToken, omnimatch);
CharSequence wipeableBaseToken = new WipeableString(baseToken);
int repeatCount = matchResult.length() / wipeableBaseToken.length();
return MatchFactory.createRepeatMatch(
start, end, matchResult, wipeableBaseToken, baseGuesses, baseMatches, repeatCount);
start,
end,
matchResult,
wipeableBaseToken,
baseAnalysis.getGuesses(),
baseAnalysis.getSequence(),
repeatCount);
}

private static class ChosenMatch {
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/nulabinc/zxcvbn/ScoringTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void testRepeatGuesses() throws Exception {
Scoring scoring = new Scoring(context);
double baseGuesses =
scoring
.mostGuessableMatchSequence(
.calculateMostGuessableMatchSequence(
baseToken,
new Matching(context, Collections.<String>emptyList()).omnimatch(baseToken))
.getGuesses();
Expand Down

0 comments on commit 641ccff

Please sign in to comment.