From 0347d6eb8c7c58dd0ba58c960b64e89445a78ad2 Mon Sep 17 00:00:00 2001 From: vvatanabe Date: Sat, 19 Aug 2023 18:13:45 +0900 Subject: [PATCH] style: format all with google-java-format --- build.gradle | 1 + .../RandomPasswordMeasureBenchmark.java | 53 +- .../java/com/nulabinc/zxcvbn/AttackTimes.java | 222 ++-- .../java/com/nulabinc/zxcvbn/Context.java | 27 +- .../java/com/nulabinc/zxcvbn/Feedback.java | 428 +++---- src/main/java/com/nulabinc/zxcvbn/Guess.java | 13 +- .../java/com/nulabinc/zxcvbn/Matcher.java | 5 +- .../java/com/nulabinc/zxcvbn/Matching.java | 36 +- .../java/com/nulabinc/zxcvbn/Pattern.java | 29 +- .../java/com/nulabinc/zxcvbn/Scoring.java | 294 ++--- .../com/nulabinc/zxcvbn/StandardContext.java | 22 +- .../nulabinc/zxcvbn/StandardDictionaries.java | 56 +- .../nulabinc/zxcvbn/StandardKeyboards.java | 49 +- .../java/com/nulabinc/zxcvbn/Strength.java | 194 ++- .../com/nulabinc/zxcvbn/TimeEstimates.java | 95 +- .../com/nulabinc/zxcvbn/WipeableString.java | 488 ++++---- src/main/java/com/nulabinc/zxcvbn/Zxcvbn.java | 88 +- .../com/nulabinc/zxcvbn/ZxcvbnBuilder.java | 48 +- .../nulabinc/zxcvbn/guesses/BaseGuess.java | 37 +- .../zxcvbn/guesses/BruteforceGuess.java | 25 +- .../nulabinc/zxcvbn/guesses/DateGuess.java | 20 +- .../zxcvbn/guesses/DictionaryGuess.java | 111 +- .../zxcvbn/guesses/EstimateGuess.java | 73 +- .../nulabinc/zxcvbn/guesses/RegexGuess.java | 62 +- .../nulabinc/zxcvbn/guesses/RepeatGuess.java | 14 +- .../zxcvbn/guesses/SequenceGuess.java | 40 +- .../nulabinc/zxcvbn/guesses/SpatialGuess.java | 60 +- .../nulabinc/zxcvbn/io/ClasspathResource.java | 140 +-- .../java/com/nulabinc/zxcvbn/io/Resource.java | 3 +- .../matchers/AlignedAdjacentGraphBuilder.java | 52 +- .../matchers/AlignedKeyboardLoader.java | 17 +- .../nulabinc/zxcvbn/matchers/BaseMatcher.java | 51 +- .../nulabinc/zxcvbn/matchers/DateMatcher.java | 3 +- .../nulabinc/zxcvbn/matchers/Dictionary.java | 50 +- .../zxcvbn/matchers/DictionaryLoader.java | 43 +- .../nulabinc/zxcvbn/matchers/Keyboard.java | 356 +++--- .../zxcvbn/matchers/KeyboardLoader.java | 60 +- .../nulabinc/zxcvbn/matchers/L33tSubDict.java | 102 +- .../zxcvbn/matchers/MatchFactory.java | 163 +-- .../matchers/SlantedAdjacentGraphBuilder.java | 54 +- .../matchers/SlantedKeyboardLoader.java | 17 +- src/main/java9/module-info.java | 6 +- .../zxcvbn/ApproachComparisonTest.java | 329 +++--- .../com/nulabinc/zxcvbn/EdgeCaseTest.java | 62 +- .../com/nulabinc/zxcvbn/FeedbackTest.java | 421 ++++--- .../zxcvbn/JSScriptEngineBuilder.java | 42 +- .../com/nulabinc/zxcvbn/JavaPortTest.java | 165 +-- .../com/nulabinc/zxcvbn/MatchingTest.java | 1047 +++++++++-------- .../java/com/nulabinc/zxcvbn/MeasureTest.java | 103 +- .../java/com/nulabinc/zxcvbn/ScoringTest.java | 652 +++++----- .../nulabinc/zxcvbn/WipeableStringTest.java | 201 ++-- .../nulabinc/zxcvbn/ZxcvbnBuilderTest.java | 89 +- 52 files changed, 3527 insertions(+), 3291 deletions(-) diff --git a/build.gradle b/build.gradle index 792f418..8855694 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ plugins { id 'com.github.kt3k.coveralls' version '2.12.0' id "me.champeau.jmh" version "0.6.6" id "com.github.spotbugs" version "5.1.2" + id 'com.github.sherter.google-java-format' version '0.9' } group = 'com.nulab-inc' diff --git a/src/jmh/java/com/nulabinc/zxcvbn/RandomPasswordMeasureBenchmark.java b/src/jmh/java/com/nulabinc/zxcvbn/RandomPasswordMeasureBenchmark.java index 76ba73b..2a1c14b 100644 --- a/src/jmh/java/com/nulabinc/zxcvbn/RandomPasswordMeasureBenchmark.java +++ b/src/jmh/java/com/nulabinc/zxcvbn/RandomPasswordMeasureBenchmark.java @@ -1,5 +1,8 @@ package com.nulabinc.zxcvbn; +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -12,10 +15,6 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import java.io.IOException; -import java.util.Random; -import java.util.concurrent.TimeUnit; - @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Thread) @@ -24,29 +23,31 @@ @Fork(1) public class RandomPasswordMeasureBenchmark { - @Param({"8", "32", "128", "512", "1024"}) - private int passwordLength; - private String password; - Zxcvbn zxcvbn; + @Param({"8", "32", "128", "512", "1024"}) + private int passwordLength; - @Setup - public void setup() throws IOException { - zxcvbn = new ZxcvbnBuilder() - .dictionaries(StandardDictionaries.loadAllDictionaries()) - .keyboards(StandardKeyboards.loadAllKeyboards()) - .build(); + private String password; + Zxcvbn zxcvbn; - Random random = new Random(42); - StringBuilder sb = new StringBuilder(passwordLength); - for (int i = 0; i < passwordLength; i++) { - char c = (char) (random.nextInt() % Character.MAX_VALUE); - sb.append(c); - } - password = sb.toString(); - } + @Setup + public void setup() throws IOException { + zxcvbn = + new ZxcvbnBuilder() + .dictionaries(StandardDictionaries.loadAllDictionaries()) + .keyboards(StandardKeyboards.loadAllKeyboards()) + .build(); - @Benchmark - public Strength measure() { - return zxcvbn.measure(password); + Random random = new Random(42); + StringBuilder sb = new StringBuilder(passwordLength); + for (int i = 0; i < passwordLength; i++) { + char c = (char) (random.nextInt() % Character.MAX_VALUE); + sb.append(c); } -} \ No newline at end of file + password = sb.toString(); + } + + @Benchmark + public Strength measure() { + return zxcvbn.measure(password); + } +} diff --git a/src/main/java/com/nulabinc/zxcvbn/AttackTimes.java b/src/main/java/com/nulabinc/zxcvbn/AttackTimes.java index e05cbc5..95a1a85 100644 --- a/src/main/java/com/nulabinc/zxcvbn/AttackTimes.java +++ b/src/main/java/com/nulabinc/zxcvbn/AttackTimes.java @@ -2,138 +2,138 @@ public class AttackTimes { - private CrackTimeSeconds crackTimeSeconds; - private CrackTimesDisplay crackTimesDisplay; - private int score; - - public AttackTimes(CrackTimeSeconds crackTimeSeconds, CrackTimesDisplay crackTimesDisplay, int score) { - this.crackTimeSeconds = crackTimeSeconds; - this.crackTimesDisplay = crackTimesDisplay; - this.score = score; + private CrackTimeSeconds crackTimeSeconds; + private CrackTimesDisplay crackTimesDisplay; + private int score; + + public AttackTimes( + CrackTimeSeconds crackTimeSeconds, CrackTimesDisplay crackTimesDisplay, int score) { + this.crackTimeSeconds = crackTimeSeconds; + this.crackTimesDisplay = crackTimesDisplay; + this.score = score; + } + + public CrackTimeSeconds getCrackTimeSeconds() { + return crackTimeSeconds; + } + + public void setCrackTimeSeconds(CrackTimeSeconds crackTimeSeconds) { + this.crackTimeSeconds = crackTimeSeconds; + } + + public CrackTimesDisplay getCrackTimesDisplay() { + return crackTimesDisplay; + } + + public void setCrackTimesDisplay(CrackTimesDisplay crackTimesDisplay) { + this.crackTimesDisplay = crackTimesDisplay; + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } + + public static class CrackTimeSeconds { + private double onlineThrottling100perHour; + private double onlineNoThrottling10perSecond; + private double offlineSlowHashing1e4perSecond; + private double offlineFastHashing1e10PerSecond; + + public CrackTimeSeconds( + double onlineThrottling100perHour, + double onlineNoThrottling10perSecond, + double offlineSlowHashing1e4perSecond, + double offlineFastHashing1e10PerSecond) { + this.onlineThrottling100perHour = onlineThrottling100perHour; + this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; + this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; + this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; } - public CrackTimeSeconds getCrackTimeSeconds() { - return crackTimeSeconds; + public double getOnlineThrottling100perHour() { + return onlineThrottling100perHour; } - public void setCrackTimeSeconds(CrackTimeSeconds crackTimeSeconds) { - this.crackTimeSeconds = crackTimeSeconds; + public void setOnlineThrottling100perHour(double onlineThrottling100perHour) { + this.onlineThrottling100perHour = onlineThrottling100perHour; } - public CrackTimesDisplay getCrackTimesDisplay() { - return crackTimesDisplay; + public double getOnlineNoThrottling10perSecond() { + return onlineNoThrottling10perSecond; } - public void setCrackTimesDisplay(CrackTimesDisplay crackTimesDisplay) { - this.crackTimesDisplay = crackTimesDisplay; + public void setOnlineNoThrottling10perSecond(double onlineNoThrottling10perSecond) { + this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; } - public int getScore() { - return score; + public double getOfflineSlowHashing1e4perSecond() { + return offlineSlowHashing1e4perSecond; } - public void setScore(int score) { - this.score = score; + public void setOfflineSlowHashing1e4perSecond(double offlineSlowHashing1e4perSecond) { + this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; } - public static class CrackTimeSeconds { - private double onlineThrottling100perHour; - private double onlineNoThrottling10perSecond; - private double offlineSlowHashing1e4perSecond; - private double offlineFastHashing1e10PerSecond; - - public CrackTimeSeconds( - double onlineThrottling100perHour, - double onlineNoThrottling10perSecond, - double offlineSlowHashing1e4perSecond, - double offlineFastHashing1e10PerSecond) { - this.onlineThrottling100perHour = onlineThrottling100perHour; - this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; - this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; - this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; - } - - public double getOnlineThrottling100perHour() { - return onlineThrottling100perHour; - } - - public void setOnlineThrottling100perHour(double onlineThrottling100perHour) { - this.onlineThrottling100perHour = onlineThrottling100perHour; - } - - public double getOnlineNoThrottling10perSecond() { - return onlineNoThrottling10perSecond; - } - - public void setOnlineNoThrottling10perSecond(double onlineNoThrottling10perSecond) { - this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; - } - - public double getOfflineSlowHashing1e4perSecond() { - return offlineSlowHashing1e4perSecond; - } - - public void setOfflineSlowHashing1e4perSecond(double offlineSlowHashing1e4perSecond) { - this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; - } - - public double getOfflineFastHashing1e10PerSecond() { - return offlineFastHashing1e10PerSecond; - } - - public void setOfflineFastHashing1e10PerSecond(double offlineFastHashing1e10PerSecond) { - this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; - } + public double getOfflineFastHashing1e10PerSecond() { + return offlineFastHashing1e10PerSecond; } - public static class CrackTimesDisplay { - private String onlineThrottling100perHour; - private String onlineNoThrottling10perSecond; - private String offlineSlowHashing1e4perSecond; - private String offlineFastHashing1e10PerSecond; - - public CrackTimesDisplay( - String onlineThrottling100perHour, - String onlineNoThrottling10perSecond, - String offlineSlowHashing1e4perSecond, - String offlineFastHashing1e10PerSecond) { - this.onlineThrottling100perHour = onlineThrottling100perHour; - this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; - this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; - this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; - } + public void setOfflineFastHashing1e10PerSecond(double offlineFastHashing1e10PerSecond) { + this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; + } + } + + public static class CrackTimesDisplay { + private String onlineThrottling100perHour; + private String onlineNoThrottling10perSecond; + private String offlineSlowHashing1e4perSecond; + private String offlineFastHashing1e10PerSecond; + + public CrackTimesDisplay( + String onlineThrottling100perHour, + String onlineNoThrottling10perSecond, + String offlineSlowHashing1e4perSecond, + String offlineFastHashing1e10PerSecond) { + this.onlineThrottling100perHour = onlineThrottling100perHour; + this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; + this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; + this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; + } - public String getOnlineThrottling100perHour() { - return onlineThrottling100perHour; - } + public String getOnlineThrottling100perHour() { + return onlineThrottling100perHour; + } - public void setOnlineThrottling100perHour(String onlineThrottling100perHour) { - this.onlineThrottling100perHour = onlineThrottling100perHour; - } + public void setOnlineThrottling100perHour(String onlineThrottling100perHour) { + this.onlineThrottling100perHour = onlineThrottling100perHour; + } - public String getOnlineNoThrottling10perSecond() { - return onlineNoThrottling10perSecond; - } + public String getOnlineNoThrottling10perSecond() { + return onlineNoThrottling10perSecond; + } - public void setOnlineNoThrottling10perSecond(String onlineNoThrottling10perSecond) { - this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; - } + public void setOnlineNoThrottling10perSecond(String onlineNoThrottling10perSecond) { + this.onlineNoThrottling10perSecond = onlineNoThrottling10perSecond; + } - public String getOfflineSlowHashing1e4perSecond() { - return offlineSlowHashing1e4perSecond; - } + public String getOfflineSlowHashing1e4perSecond() { + return offlineSlowHashing1e4perSecond; + } - public void setOfflineSlowHashing1e4perSecond(String offlineSlowHashing1e4perSecond) { - this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; - } + public void setOfflineSlowHashing1e4perSecond(String offlineSlowHashing1e4perSecond) { + this.offlineSlowHashing1e4perSecond = offlineSlowHashing1e4perSecond; + } - public String getOfflineFastHashing1e10PerSecond() { - return offlineFastHashing1e10PerSecond; - } + public String getOfflineFastHashing1e10PerSecond() { + return offlineFastHashing1e10PerSecond; + } - public void setOfflineFastHashing1e10PerSecond(String offlineFastHashing1e10PerSecond) { - this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; - } - } - -} \ No newline at end of file + public void setOfflineFastHashing1e10PerSecond(String offlineFastHashing1e10PerSecond) { + this.offlineFastHashing1e10PerSecond = offlineFastHashing1e10PerSecond; + } + } +} diff --git a/src/main/java/com/nulabinc/zxcvbn/Context.java b/src/main/java/com/nulabinc/zxcvbn/Context.java index ce2b849..e556ccc 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Context.java +++ b/src/main/java/com/nulabinc/zxcvbn/Context.java @@ -2,27 +2,26 @@ import com.nulabinc.zxcvbn.matchers.Dictionary; import com.nulabinc.zxcvbn.matchers.Keyboard; - import java.util.Collections; import java.util.Map; public class Context { - private final Map dictionaryMap; - - private final Map keyboardMap; + private final Map dictionaryMap; - public Context(final Map dictionaryMap, final Map keyboardMap) { - this.dictionaryMap = dictionaryMap; - this.keyboardMap = keyboardMap; - } + private final Map keyboardMap; - public Map getDictionaryMap() { - return Collections.unmodifiableMap(this.dictionaryMap); - } + public Context( + final Map dictionaryMap, final Map keyboardMap) { + this.dictionaryMap = dictionaryMap; + this.keyboardMap = keyboardMap; + } - public Map getKeyboardMap() { - return Collections.unmodifiableMap(this.keyboardMap); - } + public Map getDictionaryMap() { + return Collections.unmodifiableMap(this.dictionaryMap); + } + public Map getKeyboardMap() { + return Collections.unmodifiableMap(this.keyboardMap); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/Feedback.java b/src/main/java/com/nulabinc/zxcvbn/Feedback.java index 540e63f..853e90a 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Feedback.java +++ b/src/main/java/com/nulabinc/zxcvbn/Feedback.java @@ -2,7 +2,6 @@ import com.nulabinc.zxcvbn.guesses.DictionaryGuess; import com.nulabinc.zxcvbn.matchers.Match; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -13,246 +12,261 @@ public class Feedback { - private static final String DEFAULT_BUNDLE_NAME = "com/nulabinc/zxcvbn/messages"; + private static final String DEFAULT_BUNDLE_NAME = "com/nulabinc/zxcvbn/messages"; - private static final ResourceBundle.Control CONTROL = - ResourceBundle.Control.getNoFallbackControl( - ResourceBundle.Control.FORMAT_DEFAULT); + private static final ResourceBundle.Control CONTROL = + ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT); - public static final String DEFAULT_SUGGESTIONS_USE_FEW_WORDS = "feedback.default.suggestions.useFewWords"; - public static final String DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS = "feedback.default.suggestions.noNeedSymbols"; - public static final String EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD = "feedback.extra.suggestions.addAnotherWord"; - public static final String SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS = "feedback.spatial.warning.straightRowsOfKeys"; - public static final String SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS = "feedback.spatial.warning.shortKeyboardPatterns"; - public static final String SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN = "feedback.spatial.suggestions.UseLongerKeyboardPattern"; - public static final String REPEAT_WARNING_LIKE_AAA = "feedback.repeat.warning.likeAAA"; - public static final String REPEAT_WARNING_LIKE_ABCABCABC = "feedback.repeat.warning.likeABCABCABC"; - public static final String REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS = "feedback.repeat.suggestions.avoidRepeatedWords"; - public static final String SEQUENCE_WARNING_LIKE_ABCOR6543 = "feedback.sequence.warning.likeABCor6543"; - public static final String SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES = "feedback.sequence.suggestions.avoidSequences"; - public static final String REGEX_WARNING_RECENT_YEARS = "feedback.regex.warning.recentYears"; - public static final String REGEX_SUGGESTIONS_AVOID_RECENT_YEARS = "feedback.regex.suggestions.avoidRecentYears"; - public static final String DATE_WARNING_DATES = "feedback.date.warning.dates"; - public static final String DATE_SUGGESTIONS_AVOID_DATES = "feedback.date.suggestions.avoidDates"; - public static final String DICTIONARY_WARNING_PASSWORDS_TOP10 = "feedback.dictionary.warning.passwords.top10"; - public static final String DICTIONARY_WARNING_PASSWORDS_TOP100 = "feedback.dictionary.warning.passwords.top100"; - public static final String DICTIONARY_WARNING_PASSWORDS_VERY_COMMON = "feedback.dictionary.warning.passwords.veryCommon"; - public static final String DICTIONARY_WARNING_PASSWORDS_SIMILAR = "feedback.dictionary.warning.passwords.similar"; - public static final String DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF = "feedback.dictionary.warning.englishWikipedia.itself"; - public static final String DICTIONARY_WARNING_ETC_NAMES_THEMSELVES = "feedback.dictionary.warning.etc.namesThemselves"; - public static final String DICTIONARY_WARNING_ETC_NAMES_COMMON = "feedback.dictionary.warning.etc.namesCommon"; - public static final String DICTIONARY_SUGGESTIONS_CAPITALIZATION = "feedback.dictionary.suggestions.capitalization"; - public static final String DICTIONARY_SUGGESTIONS_ALL_UPPERCASE = "feedback.dictionary.suggestions.allUppercase"; - public static final String DICTIONARY_SUGGESTIONS_REVERSED = "feedback.dictionary.suggestions.reversed"; - public static final String DICTIONARY_SUGGESTIONS_L33T = "feedback.dictionary.suggestions.l33t"; + public static final String DEFAULT_SUGGESTIONS_USE_FEW_WORDS = + "feedback.default.suggestions.useFewWords"; + public static final String DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS = + "feedback.default.suggestions.noNeedSymbols"; + public static final String EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD = + "feedback.extra.suggestions.addAnotherWord"; + public static final String SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS = + "feedback.spatial.warning.straightRowsOfKeys"; + public static final String SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS = + "feedback.spatial.warning.shortKeyboardPatterns"; + public static final String SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN = + "feedback.spatial.suggestions.UseLongerKeyboardPattern"; + public static final String REPEAT_WARNING_LIKE_AAA = "feedback.repeat.warning.likeAAA"; + public static final String REPEAT_WARNING_LIKE_ABCABCABC = + "feedback.repeat.warning.likeABCABCABC"; + public static final String REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS = + "feedback.repeat.suggestions.avoidRepeatedWords"; + public static final String SEQUENCE_WARNING_LIKE_ABCOR6543 = + "feedback.sequence.warning.likeABCor6543"; + public static final String SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES = + "feedback.sequence.suggestions.avoidSequences"; + public static final String REGEX_WARNING_RECENT_YEARS = "feedback.regex.warning.recentYears"; + public static final String REGEX_SUGGESTIONS_AVOID_RECENT_YEARS = + "feedback.regex.suggestions.avoidRecentYears"; + public static final String DATE_WARNING_DATES = "feedback.date.warning.dates"; + public static final String DATE_SUGGESTIONS_AVOID_DATES = "feedback.date.suggestions.avoidDates"; + public static final String DICTIONARY_WARNING_PASSWORDS_TOP10 = + "feedback.dictionary.warning.passwords.top10"; + public static final String DICTIONARY_WARNING_PASSWORDS_TOP100 = + "feedback.dictionary.warning.passwords.top100"; + public static final String DICTIONARY_WARNING_PASSWORDS_VERY_COMMON = + "feedback.dictionary.warning.passwords.veryCommon"; + public static final String DICTIONARY_WARNING_PASSWORDS_SIMILAR = + "feedback.dictionary.warning.passwords.similar"; + public static final String DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF = + "feedback.dictionary.warning.englishWikipedia.itself"; + public static final String DICTIONARY_WARNING_ETC_NAMES_THEMSELVES = + "feedback.dictionary.warning.etc.namesThemselves"; + public static final String DICTIONARY_WARNING_ETC_NAMES_COMMON = + "feedback.dictionary.warning.etc.namesCommon"; + public static final String DICTIONARY_SUGGESTIONS_CAPITALIZATION = + "feedback.dictionary.suggestions.capitalization"; + public static final String DICTIONARY_SUGGESTIONS_ALL_UPPERCASE = + "feedback.dictionary.suggestions.allUppercase"; + public static final String DICTIONARY_SUGGESTIONS_REVERSED = + "feedback.dictionary.suggestions.reversed"; + public static final String DICTIONARY_SUGGESTIONS_L33T = "feedback.dictionary.suggestions.l33t"; - final private String warning; - final private String[] suggestions; + private final String warning; + private final String[] suggestions; - private Feedback(String warning, String... suggestions) { - this.warning = warning; - this.suggestions = suggestions; - } + private Feedback(String warning, String... suggestions) { + this.warning = warning; + this.suggestions = suggestions; + } - public String getWarning() { - return getWarning(Locale.getDefault()); - } + public String getWarning() { + return getWarning(Locale.getDefault()); + } - public String getWarning(Locale locale) { - if (this.warning == null) { - return ""; - } - ResourceBundle messages = resolveResourceBundle(locale); - return l10n(messages, this.warning); + public String getWarning(Locale locale) { + if (this.warning == null) { + return ""; } + ResourceBundle messages = resolveResourceBundle(locale); + return l10n(messages, this.warning); + } - public List getSuggestions() { - return getSuggestions(Locale.getDefault()); - } + public List getSuggestions() { + return getSuggestions(Locale.getDefault()); + } - public List getSuggestions(Locale locale) { - List suggestionTexts = new ArrayList<>(this.suggestions.length); - ResourceBundle messages = resolveResourceBundle(locale); - for (String suggestion : this.suggestions) { - suggestionTexts.add(l10n(messages, suggestion)); - } - return suggestionTexts; + public List getSuggestions(Locale locale) { + List suggestionTexts = new ArrayList<>(this.suggestions.length); + ResourceBundle messages = resolveResourceBundle(locale); + for (String suggestion : this.suggestions) { + suggestionTexts.add(l10n(messages, suggestion)); } + return suggestionTexts; + } - protected ResourceBundle resolveResourceBundle(Locale locale) { - try { - return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL); - } catch (MissingResourceException e) { - // Fix for issue of Android refs: https://github.com/nulab/zxcvbn4j/issues/21 - return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); - } catch (UnsupportedOperationException e) { - // Fix for issue of JDK 9 refs: https://github.com/nulab/zxcvbn4j/issues/45 - // ResourceBundle.Control is not supported in named modules. - // See https://docs.oracle.com/javase/9/docs/api/java/util/ResourceBundle.html#bundleprovider for more details - return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); - } + protected ResourceBundle resolveResourceBundle(Locale locale) { + try { + return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL); + } catch (MissingResourceException e) { + // Fix for issue of Android refs: https://github.com/nulab/zxcvbn4j/issues/21 + return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); + } catch (UnsupportedOperationException e) { + // Fix for issue of JDK 9 refs: https://github.com/nulab/zxcvbn4j/issues/45 + // ResourceBundle.Control is not supported in named modules. + // See https://docs.oracle.com/javase/9/docs/api/java/util/ResourceBundle.html#bundleprovider + // for more details + return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); } + } - public Feedback withResourceBundle(ResourceBundle messages) { - return new ResourceBundleFeedback(messages, warning, suggestions); - } + public Feedback withResourceBundle(ResourceBundle messages) { + return new ResourceBundleFeedback(messages, warning, suggestions); + } - public Feedback replaceResourceBundle(Map messages) { - return new ReplacedMessagesFeedback(messages, warning, suggestions); - } + public Feedback replaceResourceBundle(Map messages) { + return new ReplacedMessagesFeedback(messages, warning, suggestions); + } - private String l10n(ResourceBundle messages, String messageId) { - return messages != null ? messages.getString(messageId) : messageId; + private String l10n(ResourceBundle messages, String messageId) { + return messages != null ? messages.getString(messageId) : messageId; + } + + static Feedback getFeedback(int score, List sequence) { + if (sequence.size() == 0) { + return getFeedbackWithoutWarnings( + DEFAULT_SUGGESTIONS_USE_FEW_WORDS, DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS); + } + if (score > 2) { + return getEmptyFeedback(); + } + Match longestMatch = sequence.get(0); + if (sequence.size() > 1) { + for (Match match : sequence.subList(1, sequence.size())) { + if (match.tokenLength() > longestMatch.tokenLength()) longestMatch = match; + } } - static Feedback getFeedback(int score, List sequence) { - if (sequence.size() == 0) { - return getFeedbackWithoutWarnings( - DEFAULT_SUGGESTIONS_USE_FEW_WORDS, - DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS); - } - if (score > 2) { - return getEmptyFeedback(); - } - Match longestMatch = sequence.get(0); - if (sequence.size() > 1) { - for (Match match : sequence.subList(1, sequence.size())) { - if (match.tokenLength() > longestMatch.tokenLength()) longestMatch = match; - } - } + return getMatchFeedback(longestMatch, sequence.size() == 1); + } - return getMatchFeedback(longestMatch, sequence.size() == 1); - } + private static Feedback getFeedbackWithoutWarnings(String... suggestions) { + return new Feedback(null, suggestions); + } - private static Feedback getFeedbackWithoutWarnings(String... suggestions) { - return new Feedback(null, suggestions); - } + private static Feedback getEmptyFeedback() { + return new Feedback(null); + } - private static Feedback getEmptyFeedback() { - return new Feedback(null); + private static Feedback getMatchFeedback(Match match, boolean isSoleMatch) { + switch (match.pattern) { + case Dictionary: + return getDictionaryMatchFeedback(match, isSoleMatch); + case Spatial: + return new Feedback( + match.turns == 1 + ? SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS + : SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS, + EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN); + case Repeat: + return new Feedback( + match.baseToken.length() == 1 ? REPEAT_WARNING_LIKE_AAA : REPEAT_WARNING_LIKE_ABCABCABC, + EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS); + case Sequence: + return new Feedback( + SEQUENCE_WARNING_LIKE_ABCOR6543, + EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES); + case Regex: + return new Feedback( + "recent_year".equals(match.regexName) ? REGEX_WARNING_RECENT_YEARS : null, + EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + REGEX_SUGGESTIONS_AVOID_RECENT_YEARS); + case Date: + return new Feedback( + DATE_WARNING_DATES, EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, DATE_SUGGESTIONS_AVOID_DATES); + default: + return getFeedbackWithoutWarnings(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD); } + } - private static Feedback getMatchFeedback(Match match, boolean isSoleMatch) { - switch (match.pattern) { - case Dictionary: - return getDictionaryMatchFeedback(match, isSoleMatch); - case Spatial: - return new Feedback(match.turns == 1 - ? SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS - : SPATIAL_WARNING_SHORT_KEYBOARD_PATTERNS, - EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN - ); - case Repeat: - return new Feedback(match.baseToken.length() == 1 - ? REPEAT_WARNING_LIKE_AAA - : REPEAT_WARNING_LIKE_ABCABCABC, - EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS - ); - case Sequence: - return new Feedback(SEQUENCE_WARNING_LIKE_ABCOR6543, - EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES - ); - case Regex: - return new Feedback("recent_year".equals(match.regexName) - ? REGEX_WARNING_RECENT_YEARS - : null, - EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - REGEX_SUGGESTIONS_AVOID_RECENT_YEARS - ); - case Date: - return new Feedback( - DATE_WARNING_DATES, - EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - DATE_SUGGESTIONS_AVOID_DATES - ); - default: - return getFeedbackWithoutWarnings(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD); + private static Feedback getDictionaryMatchFeedback(Match match, boolean isSoleMatch) { + String warning = null; + if ("passwords".equals(match.dictionaryName)) { + if (isSoleMatch && !match.l33t && !match.reversed) { + if (match.rank <= 10) { + warning = DICTIONARY_WARNING_PASSWORDS_TOP10; + } else if (match.rank <= 100) { + warning = DICTIONARY_WARNING_PASSWORDS_TOP100; + } else { + warning = DICTIONARY_WARNING_PASSWORDS_VERY_COMMON; } + } else if (match.guessesLog10 <= 4) { + warning = DICTIONARY_WARNING_PASSWORDS_SIMILAR; + } + } else if ("english_wikipedia".equals(match.dictionaryName)) { + if (isSoleMatch) { + warning = DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF; + } + } else if (Arrays.asList(new String[] {"surnames", "male_names", "female_names"}) + .contains(match.dictionaryName)) { + if (isSoleMatch) { + warning = DICTIONARY_WARNING_ETC_NAMES_THEMSELVES; + } else { + warning = DICTIONARY_WARNING_ETC_NAMES_COMMON; + } } - private static Feedback getDictionaryMatchFeedback(Match match, boolean isSoleMatch) { - String warning = null; - if ("passwords".equals(match.dictionaryName)) { - if (isSoleMatch && !match.l33t && !match.reversed) { - if (match.rank <= 10) { - warning = DICTIONARY_WARNING_PASSWORDS_TOP10; - } else if (match.rank <= 100) { - warning = DICTIONARY_WARNING_PASSWORDS_TOP100; - } else { - warning = DICTIONARY_WARNING_PASSWORDS_VERY_COMMON; - } - } else if (match.guessesLog10 <= 4) { - warning = DICTIONARY_WARNING_PASSWORDS_SIMILAR; - } - } else if ("english_wikipedia".equals(match.dictionaryName)) { - if (isSoleMatch) { - warning = DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF; - } - } else if (Arrays.asList(new String[]{"surnames", "male_names", "female_names"}).contains(match.dictionaryName)) { - if (isSoleMatch) { - warning = DICTIONARY_WARNING_ETC_NAMES_THEMSELVES; - } else { - warning = DICTIONARY_WARNING_ETC_NAMES_COMMON; - } - } - - List suggestions = new ArrayList<>(); - suggestions.add(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD); + List suggestions = new ArrayList<>(); + suggestions.add(EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD); - CharSequence word = match.token; - WipeableString lower = WipeableString.lowerCase(word); - if (DictionaryGuess.START_UPPER.matcher(word).find()) { - suggestions.add(DICTIONARY_SUGGESTIONS_CAPITALIZATION); - } else if (DictionaryGuess.ALL_UPPER.matcher(word).find() && !lower.equals(word)) { - suggestions.add(DICTIONARY_SUGGESTIONS_ALL_UPPERCASE); - } - if (match.reversed && match.tokenLength() >= 4) { - suggestions.add(DICTIONARY_SUGGESTIONS_REVERSED); - } - if (match.l33t) { - suggestions.add(DICTIONARY_SUGGESTIONS_L33T); - } - lower.wipe(); - return new Feedback(warning, suggestions.toArray(new String[suggestions.size()])); + CharSequence word = match.token; + WipeableString lower = WipeableString.lowerCase(word); + if (DictionaryGuess.START_UPPER.matcher(word).find()) { + suggestions.add(DICTIONARY_SUGGESTIONS_CAPITALIZATION); + } else if (DictionaryGuess.ALL_UPPER.matcher(word).find() && !lower.equals(word)) { + suggestions.add(DICTIONARY_SUGGESTIONS_ALL_UPPERCASE); + } + if (match.reversed && match.tokenLength() >= 4) { + suggestions.add(DICTIONARY_SUGGESTIONS_REVERSED); } + if (match.l33t) { + suggestions.add(DICTIONARY_SUGGESTIONS_L33T); + } + lower.wipe(); + return new Feedback(warning, suggestions.toArray(new String[suggestions.size()])); + } - private static class ResourceBundleFeedback extends Feedback { - private ResourceBundle messages; + private static class ResourceBundleFeedback extends Feedback { + private ResourceBundle messages; - private ResourceBundleFeedback(ResourceBundle messages, String warning, String... suggestions) { - super(warning, suggestions); - this.messages = messages; - } + private ResourceBundleFeedback(ResourceBundle messages, String warning, String... suggestions) { + super(warning, suggestions); + this.messages = messages; + } - @Override - protected ResourceBundle resolveResourceBundle(Locale locale) { - return messages; - } + @Override + protected ResourceBundle resolveResourceBundle(Locale locale) { + return messages; } + } - private static class ReplacedMessagesFeedback extends Feedback { - private final Map messages; + private static class ReplacedMessagesFeedback extends Feedback { + private final Map messages; - private ReplacedMessagesFeedback(Map messages, String warning, String... suggestions) { - super(warning, suggestions); - this.messages = messages; - } + private ReplacedMessagesFeedback( + Map messages, String warning, String... suggestions) { + super(warning, suggestions); + this.messages = messages; + } - @Override - protected ResourceBundle resolveResourceBundle(Locale locale) { - try { - if (messages.containsKey(locale)) { - return messages.get(locale); - } - return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL); - } catch (MissingResourceException e) { - return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); - } catch (UnsupportedOperationException e) { - return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); - } + @Override + protected ResourceBundle resolveResourceBundle(Locale locale) { + try { + if (messages.containsKey(locale)) { + return messages.get(locale); } + return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, CONTROL); + } catch (MissingResourceException e) { + return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); + } catch (UnsupportedOperationException e) { + return ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale); + } } + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/Guess.java b/src/main/java/com/nulabinc/zxcvbn/Guess.java index 9a1ef73..1e66cd2 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Guess.java +++ b/src/main/java/com/nulabinc/zxcvbn/Guess.java @@ -4,12 +4,11 @@ public interface Guess { - public static final int BRUTEFORCE_CARDINALITY = 10; - public static final int MIN_SUBMATCH_GUESSES_SINGLE_CHAR = 10; - public static final int MIN_SUBMATCH_GUESSES_MULTI_CHAR = 50; - public static final int MIN_YEAR_SPACE = 20; - public static final int REFERENCE_YEAR = 2000; + public static final int BRUTEFORCE_CARDINALITY = 10; + public static final int MIN_SUBMATCH_GUESSES_SINGLE_CHAR = 10; + public static final int MIN_SUBMATCH_GUESSES_MULTI_CHAR = 50; + public static final int MIN_YEAR_SPACE = 20; + public static final int REFERENCE_YEAR = 2000; - - public double exec(Match match); + public double exec(Match match); } diff --git a/src/main/java/com/nulabinc/zxcvbn/Matcher.java b/src/main/java/com/nulabinc/zxcvbn/Matcher.java index f29d893..a4cb77b 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Matcher.java +++ b/src/main/java/com/nulabinc/zxcvbn/Matcher.java @@ -1,9 +1,8 @@ package com.nulabinc.zxcvbn; import com.nulabinc.zxcvbn.matchers.Match; - import java.util.List; public interface Matcher { - public List execute(CharSequence password); -} \ No newline at end of file + public List execute(CharSequence password); +} diff --git a/src/main/java/com/nulabinc/zxcvbn/Matching.java b/src/main/java/com/nulabinc/zxcvbn/Matching.java index d1d4c5a..6bddfa7 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Matching.java +++ b/src/main/java/com/nulabinc/zxcvbn/Matching.java @@ -3,7 +3,6 @@ import com.nulabinc.zxcvbn.matchers.Dictionary; import com.nulabinc.zxcvbn.matchers.Match; import com.nulabinc.zxcvbn.matchers.OmnibusMatcher; - import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -11,27 +10,28 @@ public class Matching { - private final Context context; + private final Context context; - protected final Map> rankedDictionaries; + protected final Map> rankedDictionaries; - public Matching(Context context, List orderedList) { - this.context = context; + public Matching(Context context, List orderedList) { + this.context = context; - final Map dictionaryMap = new LinkedHashMap<>(context.getDictionaryMap()); - dictionaryMap.put("user_inputs", new Dictionary("user_inputs", orderedList)); - this.rankedDictionaries = buildRankedDictionaryMap(dictionaryMap); - } + final Map dictionaryMap = new LinkedHashMap<>(context.getDictionaryMap()); + dictionaryMap.put("user_inputs", new Dictionary("user_inputs", orderedList)); + this.rankedDictionaries = buildRankedDictionaryMap(dictionaryMap); + } - public List omnimatch(CharSequence password) { - return new OmnibusMatcher(context, rankedDictionaries).execute(password); - } + public List omnimatch(CharSequence password) { + return new OmnibusMatcher(context, rankedDictionaries).execute(password); + } - private static Map> buildRankedDictionaryMap(Map dictionaryMap) { - Map> rankedDictionaries = new HashMap<>(); - for (Dictionary dictionary : dictionaryMap.values()) { - rankedDictionaries.put(dictionary.getName(), dictionary.getRankedDictionary()); - } - return rankedDictionaries; + private static Map> buildRankedDictionaryMap( + Map dictionaryMap) { + Map> rankedDictionaries = new HashMap<>(); + for (Dictionary dictionary : dictionaryMap.values()) { + rankedDictionaries.put(dictionary.getName(), dictionary.getRankedDictionary()); } + return rankedDictionaries; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/Pattern.java b/src/main/java/com/nulabinc/zxcvbn/Pattern.java index bd493f3..bab81b5 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Pattern.java +++ b/src/main/java/com/nulabinc/zxcvbn/Pattern.java @@ -1,22 +1,21 @@ package com.nulabinc.zxcvbn; public enum Pattern { - Bruteforce("bruteforce"), - Dictionary("dictionary"), - Spatial("spatial"), - Repeat("repeat"), - Sequence("sequence"), - Regex("regex"), - Date("date"); + Bruteforce("bruteforce"), + Dictionary("dictionary"), + Spatial("spatial"), + Repeat("repeat"), + Sequence("sequence"), + Regex("regex"), + Date("date"); - private final String value; + private final String value; - private Pattern(final String value) { - this.value = value; - } - - public String value() { - return value; - } + private Pattern(final String value) { + this.value = value; + } + public String value() { + return value; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/Scoring.java b/src/main/java/com/nulabinc/zxcvbn/Scoring.java index 1efcec1..34e1f8b 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Scoring.java +++ b/src/main/java/com/nulabinc/zxcvbn/Scoring.java @@ -3,7 +3,6 @@ import com.nulabinc.zxcvbn.guesses.EstimateGuess; import com.nulabinc.zxcvbn.matchers.Match; import com.nulabinc.zxcvbn.matchers.MatchFactory; - import java.io.Serializable; import java.util.ArrayList; import java.util.Calendar; @@ -15,173 +14,174 @@ public class Scoring { - public static final int REFERENCE_YEAR = Calendar.getInstance().get(Calendar.YEAR); + public static final int REFERENCE_YEAR = Calendar.getInstance().get(Calendar.YEAR); - public static final int MIN_GUESSES_BEFORE_GROWING_SEQUENCE = 10000; + public static final int MIN_GUESSES_BEFORE_GROWING_SEQUENCE = 10000; - public static final long JS_NUMBER_MAX = 9007199254740991L; + public static final long JS_NUMBER_MAX = 9007199254740991L; - private final Context context; + private final Context context; - public static double log10(double n) { - return Math.log(n) / Math.log(10); - } + public static double log10(double n) { + return Math.log(n) / Math.log(10); + } - public Scoring(Context context) { - this.context = context; - } + public Scoring(Context context) { + this.context = context; + } - protected Context getContext() { - return context; - } + protected Context getContext() { + return context; + } - public Strength mostGuessableMatchSequence(CharSequence password, List matches) { - return mostGuessableMatchSequence(password, matches, false); - } + public Strength mostGuessableMatchSequence(CharSequence password, List matches) { + return mostGuessableMatchSequence(password, matches, false); + } - public Strength mostGuessableMatchSequence(CharSequence password, List matches, boolean excludeAdditive) { - final int n = password.length(); - final List> matchesByJ = new ArrayList<>(); - for (int i = 0; i < n; i++) { - matchesByJ.add(new ArrayList()); - } - for (Match m : matches) { - matchesByJ.get(m.j).add(m); - } - for(List lst : matchesByJ) { - Collections.sort(lst, new MatchComparator()); - } - final Optimal optimal = new Optimal(n); - for (int k = 0; k < n; k++) { - for(Match m :matchesByJ.get(k)) { - if (m.i > 0) { - for(Map.Entry entry : optimal.m.get(m.i - 1).entrySet()) { - int l = entry.getKey(); - update(password, m, l + 1, optimal, excludeAdditive); - } - } else { - update(password, m, 1, optimal, excludeAdditive); - } - } - bruteforceUpdate(password, k, optimal, excludeAdditive); - } - List optimalMatchSequence = unwind(n, optimal); - Integer optimalL = optimalMatchSequence.size(); - double guesses = password.length() == 0 ? 1 : optimal.g.get(n - 1).get(optimalL); - Strength strength = new Strength(); - strength.setPassword(password); - strength.setGuesses(guesses); - strength.setGuessesLog10(log10(guesses)); - strength.setSequence(optimalMatchSequence); - return strength; + public Strength mostGuessableMatchSequence( + CharSequence password, List matches, boolean excludeAdditive) { + final int n = password.length(); + final List> matchesByJ = new ArrayList<>(); + for (int i = 0; i < n; i++) { + matchesByJ.add(new ArrayList()); } - - private void update(CharSequence password, Match m, int l, Optimal optimal, boolean excludeAdditive) { - int k = m.j; - double pi = new EstimateGuess(this.context, password).exec(m); - if (l > 1) { - pi *= optimal.pi.get(m.i - 1).get(l - 1); - } - if (Double.isInfinite(pi)) { - pi = Double.MAX_VALUE; - } - double g = factorial(l) * pi; - if (Double.isInfinite(g)) { - g = Double.MAX_VALUE; - } - if (!excludeAdditive) { - g += Math.pow(MIN_GUESSES_BEFORE_GROWING_SEQUENCE, l - 1); - if (Double.isInfinite(g)) { - g = Double.MAX_VALUE; - } - } - for (Map.Entry competing : optimal.g.get(k).entrySet()) { - if (competing.getKey() > l) { - continue; - } - if (competing.getValue() <= g) { - return; - } - } - optimal.g.get(k).put(l, g); - optimal.m.get(k).put(l, m); - optimal.pi.get(k).put(l, pi); + for (Match m : matches) { + matchesByJ.get(m.j).add(m); } - - private void bruteforceUpdate(CharSequence password, int k, Optimal optimal, boolean excludeAdditive) { - Match m = makeBruteforceMatch(password, 0, k); - update(password, m, 1, optimal, excludeAdditive); - for (int i = 1; i <= k; i++) { - m = makeBruteforceMatch(password, i, k); - for (Map.Entry entry : optimal.m.get(i - 1).entrySet()) { - int l = entry.getKey(); - Match last_m = entry.getValue(); - if (last_m.pattern != Pattern.Bruteforce) { - update(password, m, l + 1, optimal, excludeAdditive); - } - } - } - + for (List lst : matchesByJ) { + Collections.sort(lst, new MatchComparator()); } - - private static List unwind(int n, Optimal optimal) { - List optimalMatchSequence = new ArrayList<>(); - int k = n - 1; - if (0 <= k) { - int l = 0; - Double g = Double.POSITIVE_INFINITY; - for (Map.Entry candidate : optimal.g.get(k).entrySet()) { - if (candidate.getValue() < g) { - l = candidate.getKey(); - g = candidate.getValue(); - } - } - while (k >= 0) { - Match m = optimal.m.get(k).get(l); - optimalMatchSequence.add(0, m); - k = m.i - 1; - l--; - } + final Optimal optimal = new Optimal(n); + for (int k = 0; k < n; k++) { + for (Match m : matchesByJ.get(k)) { + if (m.i > 0) { + for (Map.Entry entry : optimal.m.get(m.i - 1).entrySet()) { + int l = entry.getKey(); + update(password, m, l + 1, optimal, excludeAdditive); + } + } else { + update(password, m, 1, optimal, excludeAdditive); } - return optimalMatchSequence; + } + bruteforceUpdate(password, k, optimal, excludeAdditive); } - - private static Match makeBruteforceMatch(CharSequence password, int i, int j) { - return MatchFactory.createBruteforceMatch(i, j, password.subSequence(i, j + 1)); + List optimalMatchSequence = unwind(n, optimal); + Integer optimalL = optimalMatchSequence.size(); + double guesses = password.length() == 0 ? 1 : optimal.g.get(n - 1).get(optimalL); + Strength strength = new Strength(); + strength.setPassword(password); + strength.setGuesses(guesses); + strength.setGuessesLog10(log10(guesses)); + strength.setSequence(optimalMatchSequence); + return strength; + } + + private void update( + CharSequence password, Match m, int l, Optimal optimal, boolean excludeAdditive) { + int k = m.j; + double pi = new EstimateGuess(this.context, password).exec(m); + if (l > 1) { + pi *= optimal.pi.get(m.i - 1).get(l - 1); } - - private static long factorial(int n) { - if (n < 2) return 1; - if (n > 19) return JS_NUMBER_MAX; - long f = 1; - for (int i = 2; i <= n; i++) f *= i; - return f; + if (Double.isInfinite(pi)) { + pi = Double.MAX_VALUE; } - - private static class MatchComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1L; - - @Override - public int compare(Match m1, Match m2) { - return m1.i - m2.i; + double g = factorial(l) * pi; + if (Double.isInfinite(g)) { + g = Double.MAX_VALUE; + } + if (!excludeAdditive) { + g += Math.pow(MIN_GUESSES_BEFORE_GROWING_SEQUENCE, l - 1); + if (Double.isInfinite(g)) { + g = Double.MAX_VALUE; + } + } + for (Map.Entry competing : optimal.g.get(k).entrySet()) { + if (competing.getKey() > l) { + continue; + } + if (competing.getValue() <= g) { + return; + } + } + optimal.g.get(k).put(l, g); + optimal.m.get(k).put(l, m); + optimal.pi.get(k).put(l, pi); + } + + private void bruteforceUpdate( + CharSequence password, int k, Optimal optimal, boolean excludeAdditive) { + Match m = makeBruteforceMatch(password, 0, k); + update(password, m, 1, optimal, excludeAdditive); + for (int i = 1; i <= k; i++) { + m = makeBruteforceMatch(password, i, k); + for (Map.Entry entry : optimal.m.get(i - 1).entrySet()) { + int l = entry.getKey(); + Match last_m = entry.getValue(); + if (last_m.pattern != Pattern.Bruteforce) { + update(password, m, l + 1, optimal, excludeAdditive); + } + } + } + } + + private static List unwind(int n, Optimal optimal) { + List optimalMatchSequence = new ArrayList<>(); + int k = n - 1; + if (0 <= k) { + int l = 0; + Double g = Double.POSITIVE_INFINITY; + for (Map.Entry candidate : optimal.g.get(k).entrySet()) { + if (candidate.getValue() < g) { + l = candidate.getKey(); + g = candidate.getValue(); } + } + while (k >= 0) { + Match m = optimal.m.get(k).get(l); + optimalMatchSequence.add(0, m); + k = m.i - 1; + l--; + } } + return optimalMatchSequence; + } + + private static Match makeBruteforceMatch(CharSequence password, int i, int j) { + return MatchFactory.createBruteforceMatch(i, j, password.subSequence(i, j + 1)); + } + + private static long factorial(int n) { + if (n < 2) return 1; + if (n > 19) return JS_NUMBER_MAX; + long f = 1; + for (int i = 2; i <= n; i++) f *= i; + return f; + } + + private static class MatchComparator implements Comparator, Serializable { + private static final long serialVersionUID = 1L; + + @Override + public int compare(Match m1, Match m2) { + return m1.i - m2.i; + } + } - private static class Optimal { + private static class Optimal { - public final List> m = new ArrayList<>(); + public final List> m = new ArrayList<>(); - public final List> pi = new ArrayList<>(); + public final List> pi = new ArrayList<>(); - public final List> g = new ArrayList<>(); + public final List> g = new ArrayList<>(); - public Optimal(int n) { - for (int i = 0; i < n; i++) { - m.add(new HashMap()); - pi.add(new HashMap()); - g.add(new HashMap()); - } - } + public Optimal(int n) { + for (int i = 0; i < n; i++) { + m.add(new HashMap()); + pi.add(new HashMap()); + g.add(new HashMap()); + } } - + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/StandardContext.java b/src/main/java/com/nulabinc/zxcvbn/StandardContext.java index ce84094..d06d41a 100644 --- a/src/main/java/com/nulabinc/zxcvbn/StandardContext.java +++ b/src/main/java/com/nulabinc/zxcvbn/StandardContext.java @@ -2,25 +2,23 @@ import com.nulabinc.zxcvbn.matchers.Dictionary; import com.nulabinc.zxcvbn.matchers.Keyboard; - import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; class StandardContext { - static Context build() throws IOException { - Map dictionaryMap = new LinkedHashMap<>(); - for (Dictionary dictionary : StandardDictionaries.loadAllDictionaries()) { - dictionaryMap.put(dictionary.getName(), dictionary); - } - - Map keyboardMap = new LinkedHashMap<>(); - for (Keyboard keyboard : StandardKeyboards.loadAllKeyboards()) { - keyboardMap.put(keyboard.getName(), keyboard); - } + static Context build() throws IOException { + Map dictionaryMap = new LinkedHashMap<>(); + for (Dictionary dictionary : StandardDictionaries.loadAllDictionaries()) { + dictionaryMap.put(dictionary.getName(), dictionary); + } - return new Context(dictionaryMap, keyboardMap); + Map keyboardMap = new LinkedHashMap<>(); + for (Keyboard keyboard : StandardKeyboards.loadAllKeyboards()) { + keyboardMap.put(keyboard.getName(), keyboard); } + return new Context(dictionaryMap, keyboardMap); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/StandardDictionaries.java b/src/main/java/com/nulabinc/zxcvbn/StandardDictionaries.java index 54d2511..191ee7b 100644 --- a/src/main/java/com/nulabinc/zxcvbn/StandardDictionaries.java +++ b/src/main/java/com/nulabinc/zxcvbn/StandardDictionaries.java @@ -3,47 +3,59 @@ import com.nulabinc.zxcvbn.io.ClasspathResource; import com.nulabinc.zxcvbn.matchers.Dictionary; import com.nulabinc.zxcvbn.matchers.DictionaryLoader; - import java.io.IOException; import java.util.ArrayList; import java.util.List; public class StandardDictionaries { - private static final String BASE_PATH = "/com/nulabinc/zxcvbn/matchers/dictionaries/"; + private static final String BASE_PATH = "/com/nulabinc/zxcvbn/matchers/dictionaries/"; - public static final String US_TV_AND_FILM = "us_tv_and_film"; + public static final String US_TV_AND_FILM = "us_tv_and_film"; - public static final String ENGLISH_WIKIPEDIA = "english_wikipedia"; + public static final String ENGLISH_WIKIPEDIA = "english_wikipedia"; - public static final String PASSWORDS = "passwords"; + public static final String PASSWORDS = "passwords"; - public static final String SURNAMES = "surnames"; + public static final String SURNAMES = "surnames"; - public static final String MALE_NAMES = "male_names"; + public static final String MALE_NAMES = "male_names"; - public static final String FEMALE_NAMES = "female_names"; + public static final String FEMALE_NAMES = "female_names"; - public static final DictionaryLoader US_TV_AND_FILM_LOADER = new DictionaryLoader(US_TV_AND_FILM, new ClasspathResource(BASE_PATH + "us_tv_and_film.txt")); + public static final DictionaryLoader US_TV_AND_FILM_LOADER = + new DictionaryLoader(US_TV_AND_FILM, new ClasspathResource(BASE_PATH + "us_tv_and_film.txt")); - public static final DictionaryLoader ENGLISH_WIKIPEDIA_LOADER = new DictionaryLoader(ENGLISH_WIKIPEDIA, new ClasspathResource(BASE_PATH + "english_wikipedia.txt")); + public static final DictionaryLoader ENGLISH_WIKIPEDIA_LOADER = + new DictionaryLoader( + ENGLISH_WIKIPEDIA, new ClasspathResource(BASE_PATH + "english_wikipedia.txt")); - public static final DictionaryLoader PASSWORDS_LOADER = new DictionaryLoader(PASSWORDS, new ClasspathResource(BASE_PATH + "passwords.txt")); + public static final DictionaryLoader PASSWORDS_LOADER = + new DictionaryLoader(PASSWORDS, new ClasspathResource(BASE_PATH + "passwords.txt")); - public static final DictionaryLoader SURNAMES_LOADER = new DictionaryLoader(SURNAMES, new ClasspathResource(BASE_PATH + "surnames.txt")); + public static final DictionaryLoader SURNAMES_LOADER = + new DictionaryLoader(SURNAMES, new ClasspathResource(BASE_PATH + "surnames.txt")); - public static final DictionaryLoader MALE_NAMES_LOADER = new DictionaryLoader(MALE_NAMES, new ClasspathResource(BASE_PATH + "male_names.txt")); + public static final DictionaryLoader MALE_NAMES_LOADER = + new DictionaryLoader(MALE_NAMES, new ClasspathResource(BASE_PATH + "male_names.txt")); - public static final DictionaryLoader FEMALE_NAMES_LOADER = new DictionaryLoader(FEMALE_NAMES, new ClasspathResource(BASE_PATH + "female_names.txt")); + public static final DictionaryLoader FEMALE_NAMES_LOADER = + new DictionaryLoader(FEMALE_NAMES, new ClasspathResource(BASE_PATH + "female_names.txt")); - private static final DictionaryLoader[] ALL_LOADERS = {US_TV_AND_FILM_LOADER, ENGLISH_WIKIPEDIA_LOADER, PASSWORDS_LOADER, SURNAMES_LOADER, MALE_NAMES_LOADER, FEMALE_NAMES_LOADER}; + private static final DictionaryLoader[] ALL_LOADERS = { + US_TV_AND_FILM_LOADER, + ENGLISH_WIKIPEDIA_LOADER, + PASSWORDS_LOADER, + SURNAMES_LOADER, + MALE_NAMES_LOADER, + FEMALE_NAMES_LOADER + }; - public static List loadAllDictionaries() throws IOException { - List dictionaries = new ArrayList<>(); - for (DictionaryLoader dictionaryLoader : ALL_LOADERS) { - dictionaries.add(dictionaryLoader.load()); - } - return dictionaries; + public static List loadAllDictionaries() throws IOException { + List dictionaries = new ArrayList<>(); + for (DictionaryLoader dictionaryLoader : ALL_LOADERS) { + dictionaries.add(dictionaryLoader.load()); } - + return dictionaries; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/StandardKeyboards.java b/src/main/java/com/nulabinc/zxcvbn/StandardKeyboards.java index 4ce4605..cbaf1f0 100644 --- a/src/main/java/com/nulabinc/zxcvbn/StandardKeyboards.java +++ b/src/main/java/com/nulabinc/zxcvbn/StandardKeyboards.java @@ -5,42 +5,53 @@ import com.nulabinc.zxcvbn.matchers.Keyboard; import com.nulabinc.zxcvbn.matchers.KeyboardLoader; import com.nulabinc.zxcvbn.matchers.SlantedKeyboardLoader; - import java.io.IOException; import java.util.ArrayList; import java.util.List; public class StandardKeyboards { - private static final String RESOURCES_PACKAGE_PATH = "/com/nulabinc/zxcvbn/matchers/keyboards/"; + private static final String RESOURCES_PACKAGE_PATH = "/com/nulabinc/zxcvbn/matchers/keyboards/"; - public static final String QWERTY = "qwerty"; + public static final String QWERTY = "qwerty"; - public static final String DVORAK = "dvorak"; + public static final String DVORAK = "dvorak"; - public static final String JIS = "jis"; + public static final String JIS = "jis"; - public static final String KEYPAD = "keypad"; + public static final String KEYPAD = "keypad"; - public static final String MAC_KEYPAD = "mac_keypad"; + public static final String MAC_KEYPAD = "mac_keypad"; - public static final KeyboardLoader QWERTY_LOADER = new SlantedKeyboardLoader(QWERTY, new ClasspathResource(RESOURCES_PACKAGE_PATH + "qwerty.txt")); + public static final KeyboardLoader QWERTY_LOADER = + new SlantedKeyboardLoader( + QWERTY, new ClasspathResource(RESOURCES_PACKAGE_PATH + "qwerty.txt")); - public static final KeyboardLoader DVORAK_LOADER = new SlantedKeyboardLoader(DVORAK, new ClasspathResource(RESOURCES_PACKAGE_PATH + "dvorak.txt")); + public static final KeyboardLoader DVORAK_LOADER = + new SlantedKeyboardLoader( + DVORAK, new ClasspathResource(RESOURCES_PACKAGE_PATH + "dvorak.txt")); - public static final KeyboardLoader JIS_LOADER = new SlantedKeyboardLoader(JIS, new ClasspathResource(RESOURCES_PACKAGE_PATH + "jis.txt")); + public static final KeyboardLoader JIS_LOADER = + new SlantedKeyboardLoader(JIS, new ClasspathResource(RESOURCES_PACKAGE_PATH + "jis.txt")); - public static final KeyboardLoader KEYPAD_LOADER = new AlignedKeyboardLoader(KEYPAD, new ClasspathResource(RESOURCES_PACKAGE_PATH + "keypad.txt")); + public static final KeyboardLoader KEYPAD_LOADER = + new AlignedKeyboardLoader( + KEYPAD, new ClasspathResource(RESOURCES_PACKAGE_PATH + "keypad.txt")); - public static final KeyboardLoader MAC_KEYPAD_LOADER = new AlignedKeyboardLoader(MAC_KEYPAD, new ClasspathResource(RESOURCES_PACKAGE_PATH + "mac_keypad.txt")); + public static final KeyboardLoader MAC_KEYPAD_LOADER = + new AlignedKeyboardLoader( + MAC_KEYPAD, new ClasspathResource(RESOURCES_PACKAGE_PATH + "mac_keypad.txt")); - private static final KeyboardLoader[] ALL_LOADERS = new KeyboardLoader[]{QWERTY_LOADER, DVORAK_LOADER, JIS_LOADER, KEYPAD_LOADER, MAC_KEYPAD_LOADER}; + private static final KeyboardLoader[] ALL_LOADERS = + new KeyboardLoader[] { + QWERTY_LOADER, DVORAK_LOADER, JIS_LOADER, KEYPAD_LOADER, MAC_KEYPAD_LOADER + }; - public static List loadAllKeyboards() throws IOException { - List keyboards = new ArrayList<>(); - for (KeyboardLoader keyboardLoader : ALL_LOADERS) { - keyboards.add(keyboardLoader.load()); - } - return keyboards; + public static List loadAllKeyboards() throws IOException { + List keyboards = new ArrayList<>(); + for (KeyboardLoader keyboardLoader : ALL_LOADERS) { + keyboards.add(keyboardLoader.load()); } + return keyboards; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/Strength.java b/src/main/java/com/nulabinc/zxcvbn/Strength.java index 8464f6a..e197f76 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Strength.java +++ b/src/main/java/com/nulabinc/zxcvbn/Strength.java @@ -1,108 +1,104 @@ package com.nulabinc.zxcvbn; import com.nulabinc.zxcvbn.matchers.Match; - import java.util.ArrayList; import java.util.List; public class Strength { - private CharSequence password; - private double guesses; - private double guessesLog10; - private AttackTimes.CrackTimeSeconds crackTimeSeconds; - private AttackTimes.CrackTimesDisplay crackTimesDisplay; - private int score; - private Feedback feedback; - private List sequence; - private long calcTime; - - public Strength() { - this.sequence = new ArrayList<>(); - } - - public CharSequence getPassword() { - return password; - } - - public void setPassword(CharSequence password) { - this.password = password; - } - - public double getGuesses() { - return guesses; - } - - public void setGuesses(double guesses) { - this.guesses = guesses; - } - - public double getGuessesLog10() { - return guessesLog10; - } - - public void setGuessesLog10(double guessesLog10) { - this.guessesLog10 = guessesLog10; - } - - public AttackTimes.CrackTimeSeconds getCrackTimeSeconds() { - return crackTimeSeconds; - } - - public void setCrackTimeSeconds(AttackTimes.CrackTimeSeconds crackTimeSeconds) { - this.crackTimeSeconds = crackTimeSeconds; - } - - public AttackTimes.CrackTimesDisplay getCrackTimesDisplay() { - return crackTimesDisplay; - } - - public void setCrackTimesDisplay(AttackTimes.CrackTimesDisplay crackTimesDisplay) { - this.crackTimesDisplay = crackTimesDisplay; - } - - public int getScore() { - return score; - } - - public void setScore(int score) { - this.score = score; - } - - public Feedback getFeedback() { - return feedback; - } - - public void setFeedback(Feedback feedback) { - this.feedback = feedback; - } - - public List getSequence() { - return sequence; - } - - public void setSequence(List sequence) { - this.sequence = sequence; - } - - public long getCalcTime() { - return calcTime; - } - - public void setCalcTime(long calcTime) { - this.calcTime = calcTime; - } - - /** - * Attempts to wipe any sensitive content from the object. - */ - public void wipe() { - WipeableString.wipeIfPossible(password); - for (Match match : sequence) { - WipeableString.wipeIfPossible(match.token); - WipeableString.wipeIfPossible(match.baseToken); - WipeableString.wipeIfPossible(match.matchedWord); - } - } - + private CharSequence password; + private double guesses; + private double guessesLog10; + private AttackTimes.CrackTimeSeconds crackTimeSeconds; + private AttackTimes.CrackTimesDisplay crackTimesDisplay; + private int score; + private Feedback feedback; + private List sequence; + private long calcTime; + + public Strength() { + this.sequence = new ArrayList<>(); + } + + public CharSequence getPassword() { + return password; + } + + public void setPassword(CharSequence password) { + this.password = password; + } + + public double getGuesses() { + return guesses; + } + + public void setGuesses(double guesses) { + this.guesses = guesses; + } + + public double getGuessesLog10() { + return guessesLog10; + } + + public void setGuessesLog10(double guessesLog10) { + this.guessesLog10 = guessesLog10; + } + + public AttackTimes.CrackTimeSeconds getCrackTimeSeconds() { + return crackTimeSeconds; + } + + public void setCrackTimeSeconds(AttackTimes.CrackTimeSeconds crackTimeSeconds) { + this.crackTimeSeconds = crackTimeSeconds; + } + + public AttackTimes.CrackTimesDisplay getCrackTimesDisplay() { + return crackTimesDisplay; + } + + public void setCrackTimesDisplay(AttackTimes.CrackTimesDisplay crackTimesDisplay) { + this.crackTimesDisplay = crackTimesDisplay; + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } + + public Feedback getFeedback() { + return feedback; + } + + public void setFeedback(Feedback feedback) { + this.feedback = feedback; + } + + public List getSequence() { + return sequence; + } + + public void setSequence(List sequence) { + this.sequence = sequence; + } + + public long getCalcTime() { + return calcTime; + } + + public void setCalcTime(long calcTime) { + this.calcTime = calcTime; + } + + /** Attempts to wipe any sensitive content from the object. */ + public void wipe() { + WipeableString.wipeIfPossible(password); + for (Match match : sequence) { + WipeableString.wipeIfPossible(match.token); + WipeableString.wipeIfPossible(match.baseToken); + WipeableString.wipeIfPossible(match.matchedWord); + } + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/TimeEstimates.java b/src/main/java/com/nulabinc/zxcvbn/TimeEstimates.java index 73878bd..2a60d5a 100644 --- a/src/main/java/com/nulabinc/zxcvbn/TimeEstimates.java +++ b/src/main/java/com/nulabinc/zxcvbn/TimeEstimates.java @@ -4,58 +4,55 @@ public class TimeEstimates { - public static AttackTimes estimateAttackTimes(double guesses) { - AttackTimes.CrackTimeSeconds crackTimeSeconds = new AttackTimes.CrackTimeSeconds( - divide(guesses, 100.0 / 3600.0), - guesses / 10, - guesses / 1e4, - guesses / 1e10 - ); - AttackTimes.CrackTimesDisplay crackTimesDisplay = new AttackTimes.CrackTimesDisplay( - displayTime(crackTimeSeconds.getOnlineThrottling100perHour()), - displayTime(crackTimeSeconds.getOnlineNoThrottling10perSecond()), - displayTime(crackTimeSeconds.getOfflineSlowHashing1e4perSecond()), - displayTime(crackTimeSeconds.getOfflineFastHashing1e10PerSecond()) - ); - return new AttackTimes(crackTimeSeconds, crackTimesDisplay, guessesToScore(guesses)); - } + public static AttackTimes estimateAttackTimes(double guesses) { + AttackTimes.CrackTimeSeconds crackTimeSeconds = + new AttackTimes.CrackTimeSeconds( + divide(guesses, 100.0 / 3600.0), guesses / 10, guesses / 1e4, guesses / 1e10); + AttackTimes.CrackTimesDisplay crackTimesDisplay = + new AttackTimes.CrackTimesDisplay( + displayTime(crackTimeSeconds.getOnlineThrottling100perHour()), + displayTime(crackTimeSeconds.getOnlineNoThrottling10perSecond()), + displayTime(crackTimeSeconds.getOfflineSlowHashing1e4perSecond()), + displayTime(crackTimeSeconds.getOfflineFastHashing1e10PerSecond())); + return new AttackTimes(crackTimeSeconds, crackTimesDisplay, guessesToScore(guesses)); + } - public static int guessesToScore(double guesses) { - int DELTA = 5; - if (guesses < 1e3 + DELTA) return 0; - else if (guesses < 1e6 + DELTA) return 1; - else if (guesses < 1e8 + DELTA) return 2; - else if (guesses < 1e10 + DELTA) return 3; - else return 4; - } + public static int guessesToScore(double guesses) { + int DELTA = 5; + if (guesses < 1e3 + DELTA) return 0; + else if (guesses < 1e6 + DELTA) return 1; + else if (guesses < 1e8 + DELTA) return 2; + else if (guesses < 1e10 + DELTA) return 3; + else return 4; + } - public static String displayTime(final double seconds) { - final Double minute = 60.0; - final Double hour = minute * 60; - final Double day = hour * 24; - final Double month = day * 31; - final Double year = month * 12; - final Double century = year * 100; - if (seconds < 1) return format(null, "less than a second"); - else if (seconds < minute) return format(seconds, "%s second"); - else if (seconds < hour) return format(divide(seconds, minute), "%s minute"); - else if (seconds < day) return format(divide(seconds, hour), "%s hour"); - else if (seconds < month) return format(divide(seconds, day), "%s day"); - else if (seconds < year) return format(divide(seconds, month), "%s month"); - else if (seconds < century) return format(divide(seconds, year), "%s year"); - else return format(null, "centuries"); - } + public static String displayTime(final double seconds) { + final Double minute = 60.0; + final Double hour = minute * 60; + final Double day = hour * 24; + final Double month = day * 31; + final Double year = month * 12; + final Double century = year * 100; + if (seconds < 1) return format(null, "less than a second"); + else if (seconds < minute) return format(seconds, "%s second"); + else if (seconds < hour) return format(divide(seconds, minute), "%s minute"); + else if (seconds < day) return format(divide(seconds, hour), "%s hour"); + else if (seconds < month) return format(divide(seconds, day), "%s day"); + else if (seconds < year) return format(divide(seconds, month), "%s month"); + else if (seconds < century) return format(divide(seconds, year), "%s year"); + else return format(null, "centuries"); + } - private static String format(Double number, String text) { - if (number != null) { - text = String.format(text, Math.round(number)) + (number != 1 ? "s" : ""); - } - return text; + private static String format(Double number, String text) { + if (number != null) { + text = String.format(text, Math.round(number)) + (number != 1 ? "s" : ""); } + return text; + } - private static double divide(double dividend, double divisor) { - BigDecimal dividendDecimal = new BigDecimal(dividend); - BigDecimal divisorDecimal = new BigDecimal(divisor); - return dividendDecimal.divide(divisorDecimal, BigDecimal.ROUND_HALF_DOWN).doubleValue(); - } + private static double divide(double dividend, double divisor) { + BigDecimal dividendDecimal = new BigDecimal(dividend); + BigDecimal divisorDecimal = new BigDecimal(divisor); + return dividendDecimal.divide(divisorDecimal, BigDecimal.ROUND_HALF_DOWN).doubleValue(); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/WipeableString.java b/src/main/java/com/nulabinc/zxcvbn/WipeableString.java index d9c2b83..0eafd09 100644 --- a/src/main/java/com/nulabinc/zxcvbn/WipeableString.java +++ b/src/main/java/com/nulabinc/zxcvbn/WipeableString.java @@ -3,295 +3,265 @@ import java.nio.CharBuffer; import java.util.Arrays; -/** - * A character sequence with many attributes of Strings, but that can have its content wiped. - */ +/** A character sequence with many attributes of Strings, but that can have its content wiped. */ public class WipeableString implements CharSequence { - private char[] content; - private int hash = 0; - private boolean wiped = false; - - /** - * Creates a new wipeable string, copying the content from the specified source. - */ - public WipeableString(CharSequence source) { - this.content = new char[source.length()]; - for (int n = 0; n < content.length; n++) { - content[n] = source.charAt(n); - } - } + private char[] content; + private int hash = 0; + private boolean wiped = false; - /** - * Creates a new wipeable string, copying the content from the specified source. - */ - public WipeableString(char[] source) { - this.content = Arrays.copyOf(source,source.length); + /** Creates a new wipeable string, copying the content from the specified source. */ + public WipeableString(CharSequence source) { + this.content = new char[source.length()]; + for (int n = 0; n < content.length; n++) { + content[n] = source.charAt(n); } - - @Override - public int length() { - return content == null ? 0 : content.length; - } - - @Override - public char charAt(int index) { - return content[index]; + } + + /** Creates a new wipeable string, copying the content from the specified source. */ + public WipeableString(char[] source) { + this.content = Arrays.copyOf(source, source.length); + } + + @Override + public int length() { + return content == null ? 0 : content.length; + } + + @Override + public char charAt(int index) { + return content[index]; + } + + @Override + public WipeableString subSequence(int start, int end) { + return new WipeableString(Arrays.copyOfRange(content, start, end)); + } + + /** + * Wipe the content of the wipeable string. + * + *

Overwrites the content buffer with spaces, then replaces the buffer with an empty one. + */ + public void wipe() { + Arrays.fill(content, ' '); + hash = 0; + content = new char[0]; + wiped = true; + } + + /** Returns a new wipeable string with the specified content forced into lower case. */ + public static WipeableString lowerCase(CharSequence source) { + assert source != null; + char[] chars = new char[source.length()]; + for (int n = 0; n < source.length(); n++) { + chars[n] = Character.toLowerCase(source.charAt(n)); } - - @Override - public WipeableString subSequence(int start, int end) { - return new WipeableString(Arrays.copyOfRange(content,start,end)); + return new WipeableString(chars); + } + + /** + * Returns a new wipeable string with the specified content but with the order of the characters + * reversed. + */ + public static WipeableString reversed(CharSequence source) { + assert source != null; + int length = source.length(); + char[] chars = new char[length]; + for (int n = 0; n < source.length(); n++) { + chars[n] = source.charAt(length - n - 1); } - - /** - * Wipe the content of the wipeable string. - * - * Overwrites the content buffer with spaces, then replaces the buffer with an empty one. - */ - public void wipe() { - Arrays.fill(content,' '); - hash = 0; - content = new char[0]; - wiped = true; + return new WipeableString(chars); + } + + /** Returns a copy of a portion of a character sequence as a wipeable string. */ + public static WipeableString copy(CharSequence source, int start, int end) { + return new WipeableString(source.subSequence(start, end)); + } + + /** Returns the position of the first match of the specified character (indexed from 0). */ + public int indexOf(char character) { + for (int n = 0; n < content.length; n++) { + if (content[n] == character) { + return n; + } } - - /** - * Returns a new wipeable string with the specified content forced into lower case. - */ - public static WipeableString lowerCase(CharSequence source) { - assert source != null; - char[] chars = new char[source.length()]; - for (int n = 0; n < source.length(); n++) { - chars[n] = Character.toLowerCase(source.charAt(n)); - } - return new WipeableString(chars); + return -1; + } + + /** Returns the nth Unicode code point. */ + public int codePointAt(int index) { + // Copy the implementation from String + if ((index < 0) || (index >= content.length)) { + throw new StringIndexOutOfBoundsException(index); } - - /** - * Returns a new wipeable string with the specified content but with the order of the characters reversed. - */ - public static WipeableString reversed(CharSequence source) { - assert source != null; - int length = source.length(); - char[] chars = new char[length]; - for (int n = 0; n < source.length(); n++) { - chars[n] = source.charAt(length-n-1); - } - return new WipeableString(chars); + return Character.codePointAt(content, index, content.length); + } + + /** Returns true if the wipeable string has been wiped. */ + public boolean isWiped() { + return this.wiped; + } + + /** Returns a copy of the content as a char array. */ + public char[] charArray() { + return Arrays.copyOf(content, content.length); + } + + /** + * Trims whitespace from a CharSequence. + * + *

If there is no trailing whitespace then the original value is returned. If there is trailing + * whitespace then the content (without that trailing whitespace) is copied into a new + * WipeableString. + */ + static CharSequence trimTrailingWhitespace(CharSequence s) { + if (!Character.isWhitespace(s.charAt(s.length() - 1))) { + return s; } - /** - * Returns a copy of a portion of a character sequence as a wipeable string. - */ - public static WipeableString copy(CharSequence source, int start, int end) { - return new WipeableString(source.subSequence(start,end)); - } + int length = s.length(); - /** - * Returns the position of the first match of the specified character (indexed from 0). - */ - public int indexOf(char character) { - for (int n = 0; n < content.length; n++) { - if (content[n] == character) { - return n; - } - } - return -1; + while (length > 0 && Character.isWhitespace(s.charAt(length - 1))) { + length--; } - /** - * Returns the nth Unicode code point. - */ - public int codePointAt(int index) { - // Copy the implementation from String - if ((index < 0) || (index >= content.length)) { - throw new StringIndexOutOfBoundsException(index); - } - return Character.codePointAt(content, index, content.length); - } + return WipeableString.copy(s, 0, length); + } - /** - * Returns true if the wipeable string has been wiped. - */ - public boolean isWiped() { - return this.wiped; - } + /** A version of Integer.parse(String) that accepts CharSequence as parameter. */ + public static int parseInt(CharSequence s) throws NumberFormatException { + return parseInt(s, 10); + } - /** - * Returns a copy of the content as a char array. - */ - public char[] charArray() { - return Arrays.copyOf(content,content.length); + /** A version of Integer.parse(String) that accepts CharSequence as parameter. */ + public static int parseInt(CharSequence s, int radix) throws NumberFormatException { + if (s == null) { + throw new NumberFormatException("null"); } - /** - * Trims whitespace from a CharSequence. - * - * If there is no trailing whitespace then the original value is returned. - * If there is trailing whitespace then the content (without that trailing - * whitespace) is copied into a new WipeableString. - */ - static CharSequence trimTrailingWhitespace(CharSequence s) { - if (!Character.isWhitespace(s.charAt(s.length()-1))) { - return s; - } - - int length = s.length(); - - while (length > 0 && Character.isWhitespace(s.charAt(length-1))) { - length--; - } + s = trimTrailingWhitespace(s); - return WipeableString.copy(s,0,length); + if (radix < Character.MIN_RADIX) { + throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX"); } - - /** - * A version of Integer.parse(String) that accepts CharSequence as parameter. - */ - public static int parseInt(CharSequence s) throws NumberFormatException { - return parseInt(s,10); + if (radix > Character.MAX_RADIX) { + throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX"); } - /** - * A version of Integer.parse(String) that accepts CharSequence as parameter. - */ - public static int parseInt(CharSequence s, int radix) throws NumberFormatException { - if (s == null) { - throw new NumberFormatException("null"); + int result = 0; + boolean negative = false; + int i = 0, len = s.length(); + int limit = -Integer.MAX_VALUE; + int multmin; + int digit; + + if (len > 0) { + char firstChar = s.charAt(0); + if (firstChar < '0') { // Possible leading "+" or "-" + if (firstChar == '-') { + negative = true; + limit = Integer.MIN_VALUE; + } else if (firstChar != '+') + throw new NumberFormatException("For input string: \"" + s + "\""); + + if (len == 1) // Cannot have lone "+" or "-" + throw new NumberFormatException("For input string: \"" + s + "\""); + i++; + } + multmin = limit / radix; + while (i < len) { + // Accumulating negatively avoids surprises near MAX_VALUE + digit = Character.digit(s.charAt(i++), radix); + if (digit < 0) { + throw new NumberFormatException("For input string: \"" + s + "\""); } - - s = trimTrailingWhitespace(s); - - if (radix < Character.MIN_RADIX) { - throw new NumberFormatException("radix " + radix + - " less than Character.MIN_RADIX"); - } - - if (radix > Character.MAX_RADIX) { - throw new NumberFormatException("radix " + radix + - " greater than Character.MAX_RADIX"); + if (result < multmin) { + throw new NumberFormatException("For input string: \"" + s + "\""); } - - int result = 0; - boolean negative = false; - int i = 0, len = s.length(); - int limit = -Integer.MAX_VALUE; - int multmin; - int digit; - - if (len > 0) { - char firstChar = s.charAt(0); - if (firstChar < '0') { // Possible leading "+" or "-" - if (firstChar == '-') { - negative = true; - limit = Integer.MIN_VALUE; - } else if (firstChar != '+') - throw new NumberFormatException("For input string: \"" + s + "\""); - - if (len == 1) // Cannot have lone "+" or "-" - throw new NumberFormatException("For input string: \"" + s + "\""); - i++; - } - multmin = limit / radix; - while (i < len) { - // Accumulating negatively avoids surprises near MAX_VALUE - digit = Character.digit(s.charAt(i++),radix); - if (digit < 0) { - throw new NumberFormatException("For input string: \"" + s + "\""); - } - if (result < multmin) { - throw new NumberFormatException("For input string: \"" + s + "\""); - } - result *= radix; - if (result < limit + digit) { - throw new NumberFormatException("For input string: \"" + s + "\""); - } - result -= digit; - } - } else { - throw new NumberFormatException("For input string: \"" + s + "\""); + result *= radix; + if (result < limit + digit) { + throw new NumberFormatException("For input string: \"" + s + "\""); } - return negative ? result : -result; + result -= digit; + } + } else { + throw new NumberFormatException("For input string: \"" + s + "\""); } - - @Override - public String toString() { - return new String(content); + return negative ? result : -result; + } + + @Override + public String toString() { + return new String(content); + } + + @Override + public int hashCode() { + // Reproduce the same hash as String + int h = hash; + if (h == 0 && content.length > 0) { + char val[] = content; + + for (int i = 0; i < content.length; i++) { + h = 31 * h + val[i]; + } + hash = h; } - - @Override - public int hashCode() { - // Reproduce the same hash as String - int h = hash; - if (h == 0 && content.length > 0) { - char val[] = content; - - for (int i = 0; i < content.length; i++) { - h = 31 * h + val[i]; - } - hash = h; - } - return h; + return h; + } + + @Override + public boolean equals(Object obj) { + // Use an algorithm that matches any CharSequence (including Strings) with identical content. + if (obj == null) { + return false; } - - - @Override - public boolean equals(Object obj) { - // Use an algorithm that matches any CharSequence (including Strings) with identical content. - if (obj == null) { - return false; - } - if (obj == this) { - return true; - } - if (obj instanceof CharSequence) { - CharSequence other = (CharSequence)obj; - if (other.length() != length()) { - return false; - } - for (int n = 0; n < length(); n++) { - if (charAt(n) != other.charAt(n)) { - return false; - } - } - return true; - } + if (obj == this) { + return true; + } + if (obj instanceof CharSequence) { + CharSequence other = (CharSequence) obj; + if (other.length() != length()) { return false; + } + for (int n = 0; n < length(); n++) { + if (charAt(n) != other.charAt(n)) { + return false; + } + } + return true; } - - /** - * Wipes the content of the specified character sequence if possible. - * - * The following types can be wiped... - * WipeableString - * StringBuilder - * StringBuffer - * CharBuffer (if not readOnly) - */ - public static void wipeIfPossible(CharSequence text) { - if (text == null) return; - if (text instanceof WipeableString) { - ((WipeableString)text).wipe(); - } else if (text instanceof StringBuilder) { - for (int n = 0; n < text.length(); n++) { - ((StringBuilder) text).setCharAt(n, ' '); - } - ((StringBuilder) text).setLength(0); - } else if (text instanceof StringBuffer) { - for (int n = 0; n < text.length(); n++) { - ((StringBuffer) text).setCharAt(n, ' '); - } - ((StringBuffer) text).setLength(0); - } else if (text instanceof CharBuffer) { - if (!((CharBuffer)text).isReadOnly()) { - for (int n = 0; n < text.length(); n++) { - ((CharBuffer) text).put(n, ' '); - } - } + return false; + } + + /** + * Wipes the content of the specified character sequence if possible. + * + *

The following types can be wiped... WipeableString StringBuilder StringBuffer CharBuffer (if + * not readOnly) + */ + public static void wipeIfPossible(CharSequence text) { + if (text == null) return; + if (text instanceof WipeableString) { + ((WipeableString) text).wipe(); + } else if (text instanceof StringBuilder) { + for (int n = 0; n < text.length(); n++) { + ((StringBuilder) text).setCharAt(n, ' '); + } + ((StringBuilder) text).setLength(0); + } else if (text instanceof StringBuffer) { + for (int n = 0; n < text.length(); n++) { + ((StringBuffer) text).setCharAt(n, ' '); + } + ((StringBuffer) text).setLength(0); + } else if (text instanceof CharBuffer) { + if (!((CharBuffer) text).isReadOnly()) { + for (int n = 0; n < text.length(); n++) { + ((CharBuffer) text).put(n, ' '); } - + } } - + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/Zxcvbn.java b/src/main/java/com/nulabinc/zxcvbn/Zxcvbn.java index 426261f..e33f1c8 100644 --- a/src/main/java/com/nulabinc/zxcvbn/Zxcvbn.java +++ b/src/main/java/com/nulabinc/zxcvbn/Zxcvbn.java @@ -1,7 +1,6 @@ package com.nulabinc.zxcvbn; import com.nulabinc.zxcvbn.matchers.Match; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -10,57 +9,56 @@ public class Zxcvbn { - private final Context context; + private final Context context; - public Zxcvbn() { - try { - context = StandardContext.build(); - } catch (IOException e) { - throw new IllegalStateException(e); - } + public Zxcvbn() { + try { + context = StandardContext.build(); + } catch (IOException e) { + throw new IllegalStateException(e); } + } - Zxcvbn(Context context) { - this.context = context; - } + Zxcvbn(Context context) { + this.context = context; + } - public Strength measure(CharSequence password) { - return measure(password, null); - } + public Strength measure(CharSequence password) { + return measure(password, null); + } - public Strength measure(CharSequence password, List sanitizedInputs) { - if (password == null) { - throw new IllegalArgumentException("Password is null."); - } - List lowerSanitizedInputs; - if (sanitizedInputs != null && !sanitizedInputs.isEmpty()) { - lowerSanitizedInputs = new ArrayList<>(sanitizedInputs.size()); - for (String sanitizedInput : sanitizedInputs) { - lowerSanitizedInputs.add(sanitizedInput.toLowerCase(Locale.getDefault())); - } - } else { - lowerSanitizedInputs = Collections.emptyList(); - } - long start = time(); - Matching matching = createMatching(lowerSanitizedInputs); - List 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; + public Strength measure(CharSequence password, List sanitizedInputs) { + if (password == null) { + throw new IllegalArgumentException("Password is null."); } - - protected Matching createMatching(List lowerSanitizedInputs) { - return new Matching(this.context, lowerSanitizedInputs); + List lowerSanitizedInputs; + if (sanitizedInputs != null && !sanitizedInputs.isEmpty()) { + lowerSanitizedInputs = new ArrayList<>(sanitizedInputs.size()); + for (String sanitizedInput : sanitizedInputs) { + lowerSanitizedInputs.add(sanitizedInput.toLowerCase(Locale.getDefault())); + } + } else { + lowerSanitizedInputs = Collections.emptyList(); } + long start = time(); + Matching matching = createMatching(lowerSanitizedInputs); + List 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; + } - private long time() { - return System.nanoTime(); - } + protected Matching createMatching(List lowerSanitizedInputs) { + return new Matching(this.context, lowerSanitizedInputs); + } + private long time() { + return System.nanoTime(); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/ZxcvbnBuilder.java b/src/main/java/com/nulabinc/zxcvbn/ZxcvbnBuilder.java index e6970b4..540b406 100644 --- a/src/main/java/com/nulabinc/zxcvbn/ZxcvbnBuilder.java +++ b/src/main/java/com/nulabinc/zxcvbn/ZxcvbnBuilder.java @@ -2,43 +2,41 @@ import com.nulabinc.zxcvbn.matchers.Dictionary; import com.nulabinc.zxcvbn.matchers.Keyboard; - import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class ZxcvbnBuilder { - private final Map dictionaryMap = new LinkedHashMap<>(); + private final Map dictionaryMap = new LinkedHashMap<>(); - private final Map keyboardMap = new LinkedHashMap<>(); + private final Map keyboardMap = new LinkedHashMap<>(); - public Zxcvbn build() { - return new Zxcvbn(new Context(dictionaryMap, keyboardMap)); - } + public Zxcvbn build() { + return new Zxcvbn(new Context(dictionaryMap, keyboardMap)); + } - public ZxcvbnBuilder dictionary(final Dictionary dictionary) { - this.dictionaryMap.put(dictionary.getName(), dictionary); - return this; - } + public ZxcvbnBuilder dictionary(final Dictionary dictionary) { + this.dictionaryMap.put(dictionary.getName(), dictionary); + return this; + } - public ZxcvbnBuilder dictionaries(final List dictionaries) { - for (Dictionary dictionary : dictionaries) { - this.dictionary(dictionary); - } - return this; + public ZxcvbnBuilder dictionaries(final List dictionaries) { + for (Dictionary dictionary : dictionaries) { + this.dictionary(dictionary); } + return this; + } - public ZxcvbnBuilder keyboard(final Keyboard keyboard) { - this.keyboardMap.put(keyboard.getName(), keyboard); - return this; - } + public ZxcvbnBuilder keyboard(final Keyboard keyboard) { + this.keyboardMap.put(keyboard.getName(), keyboard); + return this; + } - public ZxcvbnBuilder keyboards(final List keyboards) { - for (Keyboard keyboard : keyboards) { - this.keyboard(keyboard); - } - return this; + public ZxcvbnBuilder keyboards(final List keyboards) { + for (Keyboard keyboard : keyboards) { + this.keyboard(keyboard); } - + return this; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/BaseGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/BaseGuess.java index 14f059f..712905c 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/BaseGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/BaseGuess.java @@ -5,27 +5,26 @@ public abstract class BaseGuess implements Guess { - private final Context context; + private final Context context; - protected BaseGuess(Context context) { - this.context = context; - } + protected BaseGuess(Context context) { + this.context = context; + } - protected Context getContext() { - return context; - } + protected Context getContext() { + return context; + } - protected static int nCk(int n, int k) { - // http://blog.plover.com/math/choose.html - if (k > n) return 0; - if (k == 0) return 1; - int r = 1; - for (int d = 1; d <= k; d++) { - r *= n; - r /= d; - n -= 1; - } - return r; + protected static int nCk(int n, int k) { + // http://blog.plover.com/math/choose.html + if (k > n) return 0; + if (k == 0) return 1; + int r = 1; + for (int d = 1; d <= k; d++) { + r *= n; + r /= d; + n -= 1; } - + return r; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/BruteforceGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/BruteforceGuess.java index af119c4..f29caf3 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/BruteforceGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/BruteforceGuess.java @@ -5,17 +5,20 @@ public class BruteforceGuess extends BaseGuess { - protected BruteforceGuess(final Context context) { - super(context); - } + protected BruteforceGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - double guesses = Math.pow(BRUTEFORCE_CARDINALITY, match.tokenLength()); - if (Double.isInfinite(guesses)) { - guesses = Double.MAX_VALUE; - } - double minGuesses = match.tokenLength() == 1 ? MIN_SUBMATCH_GUESSES_SINGLE_CHAR + 1 : MIN_SUBMATCH_GUESSES_MULTI_CHAR + 1; - return Math.max(guesses, minGuesses); + @Override + public double exec(Match match) { + double guesses = Math.pow(BRUTEFORCE_CARDINALITY, match.tokenLength()); + if (Double.isInfinite(guesses)) { + guesses = Double.MAX_VALUE; } + double minGuesses = + match.tokenLength() == 1 + ? MIN_SUBMATCH_GUESSES_SINGLE_CHAR + 1 + : MIN_SUBMATCH_GUESSES_MULTI_CHAR + 1; + return Math.max(guesses, minGuesses); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/DateGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/DateGuess.java index d39c913..70bb836 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/DateGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/DateGuess.java @@ -5,15 +5,15 @@ public class DateGuess extends BaseGuess { - public DateGuess(final Context context) { - super(context); - } + public DateGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - double yearSpace = Math.max(Math.abs(match.year - REFERENCE_YEAR), MIN_YEAR_SPACE); - double guesses = yearSpace * 365; - if (match.separator != null && !match.separator.isEmpty()) guesses *= 4; - return guesses; - } + @Override + public double exec(Match match) { + double yearSpace = Math.max(Math.abs(match.year - REFERENCE_YEAR), MIN_YEAR_SPACE); + double guesses = yearSpace * 365; + if (match.separator != null && !match.separator.isEmpty()) guesses *= 4; + return guesses; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/DictionaryGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/DictionaryGuess.java index 194508f..d1c15ed 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/DictionaryGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/DictionaryGuess.java @@ -3,70 +3,69 @@ import com.nulabinc.zxcvbn.Context; import com.nulabinc.zxcvbn.WipeableString; import com.nulabinc.zxcvbn.matchers.Match; -import java.util.regex.Pattern; - import java.util.Map; +import java.util.regex.Pattern; public class DictionaryGuess extends BaseGuess { - public final static Pattern START_UPPER = Pattern.compile("^[A-Z][^A-Z]+$"); - private final static Pattern END_UPPER = Pattern.compile("^[^A-Z]+[A-Z]$"); - public final static Pattern ALL_UPPER = Pattern.compile("^[^a-z]+$"); - private final static Pattern ALL_LOWER = Pattern.compile("^[^A-Z]+$"); + public static final Pattern START_UPPER = Pattern.compile("^[A-Z][^A-Z]+$"); + private static final Pattern END_UPPER = Pattern.compile("^[^A-Z]+[A-Z]$"); + public static final Pattern ALL_UPPER = Pattern.compile("^[^a-z]+$"); + private static final Pattern ALL_LOWER = Pattern.compile("^[^A-Z]+$"); - public DictionaryGuess(final Context context) { - super(context); - } + public DictionaryGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - match.baseGuesses = (double) match.rank; - int uppercaseVariations = uppercaseVariations(match); - int l33tVariations = l33tVariations(match); - int reversedVariations = match.reversed ? 2 : 1; - return match.rank * uppercaseVariations * l33tVariations * reversedVariations; - } + @Override + public double exec(Match match) { + match.baseGuesses = (double) match.rank; + int uppercaseVariations = uppercaseVariations(match); + int l33tVariations = l33tVariations(match); + int reversedVariations = match.reversed ? 2 : 1; + return match.rank * uppercaseVariations * l33tVariations * reversedVariations; + } - public int uppercaseVariations(Match match) { - CharSequence word = match.token; - WipeableString lowercaseWord = WipeableString.lowerCase(word); - if (ALL_LOWER.matcher(word).find(0) || lowercaseWord.equals(word)) return 1; - for(Pattern pattern: new Pattern[] { START_UPPER, END_UPPER, ALL_UPPER }) - if (pattern.matcher(word).find()) return 2; - int u = 0; - int l = 0; - for (int n = 0; n < word.length(); n++) { - l += Character.isLowerCase(word.charAt(n)) ? 1 : 0; - u += Character.isUpperCase(word.charAt(n)) ? 1 : 0; - } - int variations = 0; - for (int i = 1; i <= Math.min(u, l); i++) variations += nCk(u + l, i); - lowercaseWord.wipe(); - return variations; + public int uppercaseVariations(Match match) { + CharSequence word = match.token; + WipeableString lowercaseWord = WipeableString.lowerCase(word); + if (ALL_LOWER.matcher(word).find(0) || lowercaseWord.equals(word)) return 1; + for (Pattern pattern : new Pattern[] {START_UPPER, END_UPPER, ALL_UPPER}) + if (pattern.matcher(word).find()) return 2; + int u = 0; + int l = 0; + for (int n = 0; n < word.length(); n++) { + l += Character.isLowerCase(word.charAt(n)) ? 1 : 0; + u += Character.isUpperCase(word.charAt(n)) ? 1 : 0; } + int variations = 0; + for (int i = 1; i <= Math.min(u, l); i++) variations += nCk(u + l, i); + lowercaseWord.wipe(); + return variations; + } - public int l33tVariations(Match match) { - if (!match.l33t) return 1; - int variations = 1; - for (Map.Entry subRef : match.sub.entrySet()) { - Character subbed = subRef.getKey(); - Character unsubbed = subRef.getValue(); - int s = 0; - int u = 0; - WipeableString lower = WipeableString.lowerCase(match.token); - for (char chr: lower.charArray()) { - if (chr == subbed) s++; - if (chr == unsubbed) u++; - } - if (s == 0 || u == 0) { - variations *= 2; - } else { - int p = Math.min(u, s); - int possibilities = 0; - for (int i = 1; i <= p; i++) possibilities += nCk(u + s, i); - variations *= possibilities; - } - } - return variations; + public int l33tVariations(Match match) { + if (!match.l33t) return 1; + int variations = 1; + for (Map.Entry subRef : match.sub.entrySet()) { + Character subbed = subRef.getKey(); + Character unsubbed = subRef.getValue(); + int s = 0; + int u = 0; + WipeableString lower = WipeableString.lowerCase(match.token); + for (char chr : lower.charArray()) { + if (chr == subbed) s++; + if (chr == unsubbed) u++; + } + if (s == 0 || u == 0) { + variations *= 2; + } else { + int p = Math.min(u, s); + int possibilities = 0; + for (int i = 1; i <= p; i++) possibilities += nCk(u + s, i); + variations *= possibilities; + } } + return variations; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/EstimateGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/EstimateGuess.java index eeeb695..87549ef 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/EstimateGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/EstimateGuess.java @@ -7,34 +7,53 @@ public class EstimateGuess extends BaseGuess { - private final CharSequence password; + private final CharSequence password; - public EstimateGuess(Context context, CharSequence password) { - super(context); - this.password = password; - } + public EstimateGuess(Context context, CharSequence password) { + super(context); + this.password = password; + } - @Override - public double exec(Match match) { - if (match.guesses != null) return match.guesses; - int minGuesses = 1; - if (match.tokenLength() < password.length()) { - minGuesses = match.tokenLength() == 1 ? MIN_SUBMATCH_GUESSES_SINGLE_CHAR : MIN_SUBMATCH_GUESSES_MULTI_CHAR; - } - final Guess guess; - switch (match.pattern) { - case Bruteforce: guess = new BruteforceGuess(this.getContext()); break; - case Dictionary: guess = new DictionaryGuess(this.getContext()); break; - case Spatial: guess = new SpatialGuess(this.getContext()); break; - case Repeat: guess = new RepeatGuess(this.getContext()); break; - case Sequence: guess = new SequenceGuess(this.getContext()); break; - case Regex: guess = new RegexGuess(this.getContext()); break; - case Date: guess = new DateGuess(this.getContext()); break; - default: guess = null; break; - } - double guesses = guess != null ? guess.exec(match) : 0; - match.guesses = Math.max(guesses, minGuesses); - match.guessesLog10 = Scoring.log10(match.guesses); - return match.guesses; + @Override + public double exec(Match match) { + if (match.guesses != null) return match.guesses; + int minGuesses = 1; + if (match.tokenLength() < password.length()) { + minGuesses = + match.tokenLength() == 1 + ? MIN_SUBMATCH_GUESSES_SINGLE_CHAR + : MIN_SUBMATCH_GUESSES_MULTI_CHAR; + } + final Guess guess; + switch (match.pattern) { + case Bruteforce: + guess = new BruteforceGuess(this.getContext()); + break; + case Dictionary: + guess = new DictionaryGuess(this.getContext()); + break; + case Spatial: + guess = new SpatialGuess(this.getContext()); + break; + case Repeat: + guess = new RepeatGuess(this.getContext()); + break; + case Sequence: + guess = new SequenceGuess(this.getContext()); + break; + case Regex: + guess = new RegexGuess(this.getContext()); + break; + case Date: + guess = new DateGuess(this.getContext()); + break; + default: + guess = null; + break; } + double guesses = guess != null ? guess.exec(match) : 0; + match.guesses = Math.max(guesses, minGuesses); + match.guessesLog10 = Scoring.log10(match.guesses); + return match.guesses; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/RegexGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/RegexGuess.java index e26014d..c8c1d05 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/RegexGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/RegexGuess.java @@ -3,45 +3,45 @@ import com.nulabinc.zxcvbn.Context; import com.nulabinc.zxcvbn.WipeableString; import com.nulabinc.zxcvbn.matchers.Match; - import java.util.HashMap; import java.util.Map; public class RegexGuess extends BaseGuess { - private static final Map CHAR_CLASS_BASES = new HashMap<>(); - static { - CHAR_CLASS_BASES.put("alpha_lower", 26); - CHAR_CLASS_BASES.put("alpha_upper", 26); - CHAR_CLASS_BASES.put("alpha", 52); - CHAR_CLASS_BASES.put("alphanumeric", 62); - CHAR_CLASS_BASES.put("digits", 10); - CHAR_CLASS_BASES.put("symbols", 33); - } + private static final Map CHAR_CLASS_BASES = new HashMap<>(); - protected RegexGuess(final Context context) { - super(context); - } + static { + CHAR_CLASS_BASES.put("alpha_lower", 26); + CHAR_CLASS_BASES.put("alpha_upper", 26); + CHAR_CLASS_BASES.put("alpha", 52); + CHAR_CLASS_BASES.put("alphanumeric", 62); + CHAR_CLASS_BASES.put("digits", 10); + CHAR_CLASS_BASES.put("symbols", 33); + } + + protected RegexGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - if (CHAR_CLASS_BASES.containsKey(match.regexName)) { - return Math.pow(CHAR_CLASS_BASES.get(match.regexName), match.tokenLength()); - } else if ("recent_year".equals(match.regexName)) { - double yearSpace = Math.abs(parseInt(match.token) - REFERENCE_YEAR); - yearSpace = Math.max(yearSpace, MIN_YEAR_SPACE); - return yearSpace; - } - return 0; + @Override + public double exec(Match match) { + if (CHAR_CLASS_BASES.containsKey(match.regexName)) { + return Math.pow(CHAR_CLASS_BASES.get(match.regexName), match.tokenLength()); + } else if ("recent_year".equals(match.regexName)) { + double yearSpace = Math.abs(parseInt(match.token) - REFERENCE_YEAR); + yearSpace = Math.max(yearSpace, MIN_YEAR_SPACE); + return yearSpace; } + return 0; + } - private static int parseInt(CharSequence s) { - int result = 0; - try { - result = WipeableString.parseInt(s); - } catch (NumberFormatException e) { - // ignore - } - return result; + private static int parseInt(CharSequence s) { + int result = 0; + try { + result = WipeableString.parseInt(s); + } catch (NumberFormatException e) { + // ignore } + return result; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/RepeatGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/RepeatGuess.java index 9317d77..2ef8a10 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/RepeatGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/RepeatGuess.java @@ -5,12 +5,12 @@ public class RepeatGuess extends BaseGuess { - public RepeatGuess(final Context context) { - super(context); - } + public RepeatGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - return match.baseGuesses * match.repeatCount; - } + @Override + public double exec(Match match) { + return match.baseGuesses * match.repeatCount; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/SequenceGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/SequenceGuess.java index 14e143d..1fba057 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/SequenceGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/SequenceGuess.java @@ -2,33 +2,33 @@ import com.nulabinc.zxcvbn.Context; import com.nulabinc.zxcvbn.matchers.Match; - import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; public class SequenceGuess extends BaseGuess { - private final static List START_POINTS = Arrays.asList('a', 'A', 'z', 'Z', '0', '1', '9'); + private static final List START_POINTS = + Arrays.asList('a', 'A', 'z', 'Z', '0', '1', '9'); - public SequenceGuess(final Context context) { - super(context); - } + public SequenceGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - final char firstChr = match.token.charAt(0); - double baseGuesses; - if (START_POINTS.contains(firstChr)) { - baseGuesses = 4; - } else { - if (Pattern.compile("\\d").matcher(String.valueOf(firstChr)).find()) { - baseGuesses = 10; - } else { - baseGuesses = 26; - } - } - if (!match.ascending) baseGuesses *= 2; - return baseGuesses * match.tokenLength(); + @Override + public double exec(Match match) { + final char firstChr = match.token.charAt(0); + double baseGuesses; + if (START_POINTS.contains(firstChr)) { + baseGuesses = 4; + } else { + if (Pattern.compile("\\d").matcher(String.valueOf(firstChr)).find()) { + baseGuesses = 10; + } else { + baseGuesses = 26; + } } + if (!match.ascending) baseGuesses *= 2; + return baseGuesses * match.tokenLength(); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/guesses/SpatialGuess.java b/src/main/java/com/nulabinc/zxcvbn/guesses/SpatialGuess.java index 09ec413..6e7c000 100644 --- a/src/main/java/com/nulabinc/zxcvbn/guesses/SpatialGuess.java +++ b/src/main/java/com/nulabinc/zxcvbn/guesses/SpatialGuess.java @@ -6,37 +6,37 @@ public class SpatialGuess extends BaseGuess { - public SpatialGuess(final Context context) { - super(context); - } + public SpatialGuess(final Context context) { + super(context); + } - @Override - public double exec(Match match) { - Keyboard keyboard = this.getContext().getKeyboardMap().get(match.graph); - int s = keyboard.getStartingPositions(); - double d = keyboard.getAverageDegree(); - double guesses = 0; - int l = match.tokenLength(); - int t = match.turns; - for (int i = 2; i <= l; i++) { - int possibleTurns = Math.min(t, i - 1); - for (int j = 1; j <= possibleTurns; j++) { - guesses += nCk(i - 1, j - 1) * s * Math.pow(d, j); - } - } - if (match.shiftedCount > 0) { - int shiftedCount = match.shiftedCount; - int unshiftedCount = match.tokenLength() - match.shiftedCount; - if (shiftedCount == 0 || unshiftedCount == 0) { - guesses *= 2; - } else { - int shiftedVariations = 0; - for (int i = 1; i <= Math.min(shiftedCount, unshiftedCount); i++) { - shiftedVariations += nCk(shiftedCount + unshiftedCount, i); - } - guesses *= shiftedVariations; - } + @Override + public double exec(Match match) { + Keyboard keyboard = this.getContext().getKeyboardMap().get(match.graph); + int s = keyboard.getStartingPositions(); + double d = keyboard.getAverageDegree(); + double guesses = 0; + int l = match.tokenLength(); + int t = match.turns; + for (int i = 2; i <= l; i++) { + int possibleTurns = Math.min(t, i - 1); + for (int j = 1; j <= possibleTurns; j++) { + guesses += nCk(i - 1, j - 1) * s * Math.pow(d, j); + } + } + if (match.shiftedCount > 0) { + int shiftedCount = match.shiftedCount; + int unshiftedCount = match.tokenLength() - match.shiftedCount; + if (shiftedCount == 0 || unshiftedCount == 0) { + guesses *= 2; + } else { + int shiftedVariations = 0; + for (int i = 1; i <= Math.min(shiftedCount, unshiftedCount); i++) { + shiftedVariations += nCk(shiftedCount + unshiftedCount, i); } - return guesses; + guesses *= shiftedVariations; + } } + return guesses; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/io/ClasspathResource.java b/src/main/java/com/nulabinc/zxcvbn/io/ClasspathResource.java index de40f98..363a9fd 100644 --- a/src/main/java/com/nulabinc/zxcvbn/io/ClasspathResource.java +++ b/src/main/java/com/nulabinc/zxcvbn/io/ClasspathResource.java @@ -6,85 +6,85 @@ public final class ClasspathResource implements Resource { - private final String path; + private final String path; - public ClasspathResource(final String path) { - this.path = path; - } + public ClasspathResource(final String path) { + this.path = path; + } - @Override - public InputStream getInputStream() throws IOException { - InputStream in = this.getResourceAsStreamWithFallback(path); - if (in == null) { - throw new FileNotFoundException("Could not get resource as stream"); - } - return in; + @Override + public InputStream getInputStream() throws IOException { + InputStream in = this.getResourceAsStreamWithFallback(path); + if (in == null) { + throw new FileNotFoundException("Could not get resource as stream"); } + return in; + } - /** - * This code base is spring-framework's ClassUtils#getDefaultClassLoader(). - * https://github.com/spring-projects/spring-framework/blob/dfb7ca733ad309b35040e0027fb7a2f10f3a196a/spring-core/src/main/java/org/springframework/util/ClassUtils.java#L173-L210 - *

- * First, return the InputStream to use: typically the thread context ClassLoader, if available; - * Next, the ClassLoader that loaded the ResourceLoader class will be used as fallback. - * Finally, if even the system ClassLoader could not access resource as stream, return null. - */ - private InputStream getResourceAsStreamWithFallback(String path) { - // 0. try loading the resource from the same artifact as this class - { - InputStream in = getClass().getResourceAsStream(path); - if (in != null) { - return in; - } - } // no exceptions thrown - - // 1. try to get resource with thread context ClassLoader - try { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - InputStream in = this.getResourceAsStream(cl, path); - if (in != null) { - return in; - } - } catch (Throwable ex) { - // Cannot access thread context ClassLoader - falling back... - } + /** + * This code base is spring-framework's ClassUtils#getDefaultClassLoader(). + * https://github.com/spring-projects/spring-framework/blob/dfb7ca733ad309b35040e0027fb7a2f10f3a196a/spring-core/src/main/java/org/springframework/util/ClassUtils.java#L173-L210 + * + *

First, return the InputStream to use: typically the thread context ClassLoader, if + * available; Next, the ClassLoader that loaded the ResourceLoader class will be used as fallback. + * Finally, if even the system ClassLoader could not access resource as stream, return null. + */ + private InputStream getResourceAsStreamWithFallback(String path) { + // 0. try loading the resource from the same artifact as this class + { + InputStream in = getClass().getResourceAsStream(path); + if (in != null) { + return in; + } + } // no exceptions thrown - // 2. try to get resource with this class context ClassLoader - try { - ClassLoader cl = this.getClass().getClassLoader(); - InputStream in = this.getResourceAsStream(cl, path); - if (in != null) { - return in; - } - } catch (Throwable ex) { - // Cannot access this class context ClassLoader - falling back... - } + // 1. try to get resource with thread context ClassLoader + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + InputStream in = this.getResourceAsStream(cl, path); + if (in != null) { + return in; + } + } catch (Throwable ex) { + // Cannot access thread context ClassLoader - falling back... + } - // 3. try to get resource with this class context ClassLoader - try { - ClassLoader cl = ClassLoader.getSystemClassLoader(); - InputStream in = this.getResourceAsStream(cl, path); - if (in != null) { - return in; - } - } catch (Throwable ex) { - // Cannot access system ClassLoader - oh well, maybe the caller can live with null... - } + // 2. try to get resource with this class context ClassLoader + try { + ClassLoader cl = this.getClass().getClassLoader(); + InputStream in = this.getResourceAsStream(cl, path); + if (in != null) { + return in; + } + } catch (Throwable ex) { + // Cannot access this class context ClassLoader - falling back... + } - return null; + // 3. try to get resource with this class context ClassLoader + try { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + InputStream in = this.getResourceAsStream(cl, path); + if (in != null) { + return in; + } + } catch (Throwable ex) { + // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } - private InputStream getResourceAsStream(ClassLoader cl, String path) { - try { - if (cl != null) { - InputStream in = cl.getResourceAsStream(path); - if (in != null) { - return in; - } - } - } catch (Throwable ex) { - // Cannot access resource as stream + return null; + } + + private InputStream getResourceAsStream(ClassLoader cl, String path) { + try { + if (cl != null) { + InputStream in = cl.getResourceAsStream(path); + if (in != null) { + return in; } - return null; + } + } catch (Throwable ex) { + // Cannot access resource as stream } + return null; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/io/Resource.java b/src/main/java/com/nulabinc/zxcvbn/io/Resource.java index e04b38f..50f35bb 100644 --- a/src/main/java/com/nulabinc/zxcvbn/io/Resource.java +++ b/src/main/java/com/nulabinc/zxcvbn/io/Resource.java @@ -5,6 +5,5 @@ public interface Resource { - InputStream getInputStream() throws IOException; - + InputStream getInputStream() throws IOException; } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedAdjacentGraphBuilder.java b/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedAdjacentGraphBuilder.java index 4c258f5..d02e8b5 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedAdjacentGraphBuilder.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedAdjacentGraphBuilder.java @@ -5,33 +5,33 @@ public class AlignedAdjacentGraphBuilder extends Keyboard.AdjacentGraphBuilder { - public AlignedAdjacentGraphBuilder(final String layout) { - super(layout); - } + public AlignedAdjacentGraphBuilder(final String layout) { + super(layout); + } - @Override - public boolean isSlanted() { - return false; - } + @Override + public boolean isSlanted() { + return false; + } - @Override - protected int calcSlant(int y) { - return 0; - } + @Override + protected int calcSlant(int y) { + return 0; + } - /** - * returns the nine clockwise adjacent coordinates on a keypad, where each row is vert aligned. - */ - @Override - protected List getAdjacentCoords(final Position position) { - return Arrays.asList( - Position.of(position.getX() - 1, position.getY()), - Position.of(position.getX() - 1, position.getY() - 1), - Position.of(position.getX(), position.getY() - 1), - Position.of(position.getX() + 1, position.getY() - 1), - Position.of(position.getX() + 1, position.getY()), - Position.of(position.getX() + 1, position.getY() + 1), - Position.of(position.getX(), position.getY() + 1), - Position.of(position.getX() - 1, position.getY() + 1)); - } + /** + * returns the nine clockwise adjacent coordinates on a keypad, where each row is vert aligned. + */ + @Override + protected List getAdjacentCoords(final Position position) { + return Arrays.asList( + Position.of(position.getX() - 1, position.getY()), + Position.of(position.getX() - 1, position.getY() - 1), + Position.of(position.getX(), position.getY() - 1), + Position.of(position.getX() + 1, position.getY() - 1), + Position.of(position.getX() + 1, position.getY()), + Position.of(position.getX() + 1, position.getY() + 1), + Position.of(position.getX(), position.getY() + 1), + Position.of(position.getX() - 1, position.getY() + 1)); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedKeyboardLoader.java b/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedKeyboardLoader.java index 3c0c36a..c093dea 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedKeyboardLoader.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/AlignedKeyboardLoader.java @@ -4,13 +4,12 @@ public class AlignedKeyboardLoader extends KeyboardLoader { - public AlignedKeyboardLoader(final String name, final Resource inputStreamSource) { - super(name, inputStreamSource); - } - - @Override - protected Keyboard.AdjacentGraphBuilder buildAdjacentGraphBuilder(final String layout) { - return new AlignedAdjacentGraphBuilder(layout); - } - + public AlignedKeyboardLoader(final String name, final Resource inputStreamSource) { + super(name, inputStreamSource); + } + + @Override + protected Keyboard.AdjacentGraphBuilder buildAdjacentGraphBuilder(final String layout) { + return new AlignedAdjacentGraphBuilder(layout); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/BaseMatcher.java b/src/main/java/com/nulabinc/zxcvbn/matchers/BaseMatcher.java index e1d8e3f..e55fc04 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/BaseMatcher.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/BaseMatcher.java @@ -2,40 +2,37 @@ import com.nulabinc.zxcvbn.Context; import com.nulabinc.zxcvbn.Matcher; -import com.nulabinc.zxcvbn.WipeableString; - import java.io.Serializable; import java.util.*; public abstract class BaseMatcher implements Matcher { - private final Context context; + private final Context context; - protected BaseMatcher(Context context) { - this.context = context; - } + protected BaseMatcher(Context context) { + this.context = context; + } - protected Context getContext() { - return context; - } + protected Context getContext() { + return context; + } - protected List sorted(List matches) { - Collections.sort(matches, new MatchComparator()); - return matches; - } + protected List sorted(List matches) { + Collections.sort(matches, new MatchComparator()); + return matches; + } - private static class MatchComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1L; - - @Override - public int compare(Match o1, Match o2) { - int c = o1.i - o2.i; - if (c != 0) { - return c; - } else { - return (o1.j - o2.j); - } - } - } + private static class MatchComparator implements Comparator, Serializable { + private static final long serialVersionUID = 1L; -} \ No newline at end of file + @Override + public int compare(Match o1, Match o2) { + int c = o1.i - o2.i; + if (c != 0) { + return c; + } else { + return (o1.j - o2.j); + } + } + } +} diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/DateMatcher.java b/src/main/java/com/nulabinc/zxcvbn/matchers/DateMatcher.java index 17046de..aa01eb2 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/DateMatcher.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/DateMatcher.java @@ -33,7 +33,8 @@ public class DateMatcher extends BaseMatcher { private static final int[][][] DATE_SPLITS = new int[9][][]; private static final Pattern MAYBE_DATE_NO_SEPARATOR = Pattern.compile("^\\d{4,8}$"); private static final Pattern MAYBE_DATE_WITH_SEPARATOR = - Pattern.compile("^" + Pattern.compile( + "^" + // day, month, year "(\\d{1,4})" diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/Dictionary.java b/src/main/java/com/nulabinc/zxcvbn/matchers/Dictionary.java index 9335d3f..7d07f91 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/Dictionary.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/Dictionary.java @@ -6,37 +6,37 @@ public class Dictionary { - private final String name; + private final String name; - private final List frequencies; + private final List frequencies; - private final Map rankedDictionary; + private final Map rankedDictionary; - public Dictionary(String name, List frequencies) { - this.name = name; - this.frequencies = frequencies; - this.rankedDictionary = toRankedDictionary(frequencies); - } + public Dictionary(String name, List frequencies) { + this.name = name; + this.frequencies = frequencies; + this.rankedDictionary = toRankedDictionary(frequencies); + } - private Map toRankedDictionary(final List frequencies) { - Map result = new HashMap<>(); - int i = 1; // rank starts at 1, not 0 - for (String word : frequencies) { - result.put(word, i); - i++; - } - return result; + private Map toRankedDictionary(final List frequencies) { + Map result = new HashMap<>(); + int i = 1; // rank starts at 1, not 0 + for (String word : frequencies) { + result.put(word, i); + i++; } + return result; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public List getFrequencies() { - return frequencies; - } + public List getFrequencies() { + return frequencies; + } - public Map getRankedDictionary() { - return rankedDictionary; - } + public Map getRankedDictionary() { + return rankedDictionary; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/DictionaryLoader.java b/src/main/java/com/nulabinc/zxcvbn/matchers/DictionaryLoader.java index e09172c..0f836ea 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/DictionaryLoader.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/DictionaryLoader.java @@ -1,7 +1,6 @@ package com.nulabinc.zxcvbn.matchers; import com.nulabinc.zxcvbn.io.Resource; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -11,29 +10,29 @@ public class DictionaryLoader { - private final String name; + private final String name; - private final Resource resource; + private final Resource resource; - public DictionaryLoader(final String name, final Resource resource) { - this.name = name; - this.resource = resource; - } + public DictionaryLoader(final String name, final Resource resource) { + this.name = name; + this.resource = resource; + } - public Dictionary load() throws IOException { - List words = new ArrayList<>(); - // Reasons for not using StandardCharsets - // refs: https://github.com/nulab/zxcvbn4j/issues/62 - try (final InputStream inputStream = resource.getInputStream(); - final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); - final BufferedReader br = new BufferedReader(inputStreamReader)) { - String line; - while ((line = br.readLine()) != null) { - words.add(line); - } - } catch (IOException e) { - throw new RuntimeException("Error while reading " + name); - } - return new Dictionary(name, words); + public Dictionary load() throws IOException { + List words = new ArrayList<>(); + // Reasons for not using StandardCharsets + // refs: https://github.com/nulab/zxcvbn4j/issues/62 + try (final InputStream inputStream = resource.getInputStream(); + final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); + final BufferedReader br = new BufferedReader(inputStreamReader)) { + String line; + while ((line = br.readLine()) != null) { + words.add(line); + } + } catch (IOException e) { + throw new RuntimeException("Error while reading " + name); } + return new Dictionary(name, words); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/Keyboard.java b/src/main/java/com/nulabinc/zxcvbn/matchers/Keyboard.java index 84e192c..4a85f59 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/Keyboard.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/Keyboard.java @@ -7,217 +7,219 @@ public class Keyboard { - private final String name; + private final String name; - private final Map> adjacencyGraph; + private final Map> adjacencyGraph; - private final boolean slanted; + private final boolean slanted; - private final int startingPositions; + private final int startingPositions; - private final double averageDegree; + private final double averageDegree; - Keyboard(final String name, final AdjacentGraphBuilder adjacentGraphBuilder) { - this.name = name; - this.adjacencyGraph = adjacentGraphBuilder.build(); - this.slanted = adjacentGraphBuilder.isSlanted(); - this.startingPositions = adjacencyGraph.size(); - this.averageDegree = calcAverageDegree(adjacencyGraph); - } + Keyboard(final String name, final AdjacentGraphBuilder adjacentGraphBuilder) { + this.name = name; + this.adjacencyGraph = adjacentGraphBuilder.build(); + this.slanted = adjacentGraphBuilder.isSlanted(); + this.startingPositions = adjacencyGraph.size(); + this.averageDegree = calcAverageDegree(adjacencyGraph); + } - private static double calcAverageDegree(final Map> adjacencyGraph) { - double average = 0; - for (Map.Entry> graphRef : adjacencyGraph.entrySet()) { - List neighbors = graphRef.getValue(); - List results = new ArrayList<>(); - for (String neighbor : neighbors) { - if (neighbor != null) { - results.add(neighbor); - } - } - average += results.size(); - } - List keys = new ArrayList<>(); - for (Map.Entry> graphRef : adjacencyGraph.entrySet()) { - keys.add(graphRef.getKey()); + private static double calcAverageDegree(final Map> adjacencyGraph) { + double average = 0; + for (Map.Entry> graphRef : adjacencyGraph.entrySet()) { + List neighbors = graphRef.getValue(); + List results = new ArrayList<>(); + for (String neighbor : neighbors) { + if (neighbor != null) { + results.add(neighbor); } - average /= keys.size(); - return average; - } - - public String getName() { - return name; - } - - public Map> getAdjacencyGraph() { - return adjacencyGraph; + } + average += results.size(); } - - public boolean isSlanted() { - return slanted; - } - - public int getStartingPositions() { - return startingPositions; + List keys = new ArrayList<>(); + for (Map.Entry> graphRef : adjacencyGraph.entrySet()) { + keys.add(graphRef.getKey()); } - - public double getAverageDegree() { - return averageDegree; - } - - public static abstract class AdjacentGraphBuilder { - - private static final SplitMatcher WHITESPACE_SPLIT_MATCHER = new SplitMatcher() { - @Override - public boolean match(final char c) { - return Character.isWhitespace(c); - } + average /= keys.size(); + return average; + } + + public String getName() { + return name; + } + + public Map> getAdjacencyGraph() { + return adjacencyGraph; + } + + public boolean isSlanted() { + return slanted; + } + + public int getStartingPositions() { + return startingPositions; + } + + public double getAverageDegree() { + return averageDegree; + } + + public abstract static class AdjacentGraphBuilder { + + private static final SplitMatcher WHITESPACE_SPLIT_MATCHER = + new SplitMatcher() { + @Override + public boolean match(final char c) { + return Character.isWhitespace(c); + } }; - private static final SplitMatcher NEW_LINE_SPLIT_MATCHER = new SplitMatcher() { - @Override - public boolean match(final char c) { - return c == '\n'; - } + private static final SplitMatcher NEW_LINE_SPLIT_MATCHER = + new SplitMatcher() { + @Override + public boolean match(final char c) { + return c == '\n'; + } }; - private final String layout; + private final String layout; - public AdjacentGraphBuilder(final String layout) { - this.layout = layout; - } + public AdjacentGraphBuilder(final String layout) { + this.layout = layout; + } - /** - * builds an adjacency graph as a dictionary: {character: [adjacent_characters]}. - * adjacent characters occur in a clockwise order. - * for example: - * on qwerty layout, 'g' maps to ['fF', 'tT', 'yY', 'hH', 'bB', 'vV'] - * on keypad layout, '7' maps to [None, None, None, '=', '8', '5', '4', None] * - */ - public Map> build() { - final Map positionTable = buildPositionTable(layout); - - final Map> adjacencyGraph = new HashMap<>(); - for (Map.Entry entry : positionTable.entrySet()) { - for (final char key : entry.getValue().toCharArray()) { - final List adjacencies = new ArrayList<>(); - final Position position = entry.getKey(); - for (final Position coord : getAdjacentCoords(position)) { - adjacencies.add(positionTable.get(coord)); - } - adjacencyGraph.put(key, adjacencies); - } - } - - return adjacencyGraph; + /** + * builds an adjacency graph as a dictionary: {character: [adjacent_characters]}. adjacent + * characters occur in a clockwise order. for example: on qwerty layout, 'g' maps to ['fF', + * 'tT', 'yY', 'hH', 'bB', 'vV'] on keypad layout, '7' maps to [None, None, None, '=', '8', '5', + * '4', None] * + */ + public Map> build() { + final Map positionTable = buildPositionTable(layout); + + final Map> adjacencyGraph = new HashMap<>(); + for (Map.Entry entry : positionTable.entrySet()) { + for (final char key : entry.getValue().toCharArray()) { + final List adjacencies = new ArrayList<>(); + final Position position = entry.getKey(); + for (final Position coord : getAdjacentCoords(position)) { + adjacencies.add(positionTable.get(coord)); + } + adjacencyGraph.put(key, adjacencies); } + } - private Map buildPositionTable(final String layout) { - final Map positionTable = new HashMap<>(); - - final List tokens = split(layout, WHITESPACE_SPLIT_MATCHER); - final int tokenSize = tokens.get(0).length(); - final int xUnit = tokenSize + 1; - - for (String token : tokens) { - assert token.length() == tokenSize : String.format("token [%s] length mismatch:%n%s", token, layout); - } - - int y = 1; - for (final String line : split(layout, NEW_LINE_SPLIT_MATCHER)) { - // the way I illustrated keys above, each qwerty row is indented one space in from the last - int slant = calcSlant(y); - for (final String token : split(line, WHITESPACE_SPLIT_MATCHER)) { - int index = line.indexOf(token) - slant; - int x = index / xUnit; - final int remainder = index % xUnit; - assert remainder == 0 : String.format("unexpected x offset [%d] for %s in:%n%s", x, token, layout); - positionTable.put(Position.of(x, y), token); - } - - y++; - } - return positionTable; + return adjacencyGraph; + } + + private Map buildPositionTable(final String layout) { + final Map positionTable = new HashMap<>(); + + final List tokens = split(layout, WHITESPACE_SPLIT_MATCHER); + final int tokenSize = tokens.get(0).length(); + final int xUnit = tokenSize + 1; + + for (String token : tokens) { + assert token.length() == tokenSize + : String.format("token [%s] length mismatch:%n%s", token, layout); + } + + int y = 1; + for (final String line : split(layout, NEW_LINE_SPLIT_MATCHER)) { + // the way I illustrated keys above, each qwerty row is indented one space in from the last + int slant = calcSlant(y); + for (final String token : split(line, WHITESPACE_SPLIT_MATCHER)) { + int index = line.indexOf(token) - slant; + int x = index / xUnit; + final int remainder = index % xUnit; + assert remainder == 0 + : String.format("unexpected x offset [%d] for %s in:%n%s", x, token, layout); + positionTable.put(Position.of(x, y), token); } - protected abstract List getAdjacentCoords(final Position position); - - private static List split(final String str, final SplitMatcher splitMatcher) { - final int len = str.length(); - final List list = new ArrayList<>(); - int i = 0, start = 0; - boolean match = false; - while (i < len) { - if (splitMatcher.match(str.charAt(i))) { - if (match) { - list.add(str.substring(start, i)); - match = false; - } - start = ++i; - continue; - } - match = true; - i++; - } - if (match) { - list.add(str.substring(start, i)); - } - return list; + y++; + } + return positionTable; + } + + protected abstract List getAdjacentCoords(final Position position); + + private static List split(final String str, final SplitMatcher splitMatcher) { + final int len = str.length(); + final List list = new ArrayList<>(); + int i = 0, start = 0; + boolean match = false; + while (i < len) { + if (splitMatcher.match(str.charAt(i))) { + if (match) { + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; } + match = true; + i++; + } + if (match) { + list.add(str.substring(start, i)); + } + return list; + } - protected abstract int calcSlant(int y); + protected abstract int calcSlant(int y); - public abstract boolean isSlanted(); + public abstract boolean isSlanted(); - private interface SplitMatcher { - boolean match(char c); - } + private interface SplitMatcher { + boolean match(char c); + } - static class Position { + static class Position { - private final int x; + private final int x; - private final int y; + private final int y; - private Position(int x, int y) { - this.x = x; - this.y = y; - } + private Position(int x, int y) { + this.x = x; + this.y = y; + } - public static Position of(int x, int y) { - return new Position(x, y); - } + public static Position of(int x, int y) { + return new Position(x, y); + } - public int getX() { - return x; - } + public int getX() { + return x; + } - public int getY() { - return y; - } + public int getY() { + return y; + } - @Override - public int hashCode() { - int result = x; - result = 31 * result + y; - return result; - } + @Override + public int hashCode() { + int result = x; + result = 31 * result + y; + return result; + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Position)) return false; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Position)) return false; - final Position position = (Position) o; + final Position position = (Position) o; - return x == position.x && y == position.y; - } + return x == position.x && y == position.y; + } - @Override - public String toString() { - return "[" + x + "," + y + ']'; - } - } + @Override + public String toString() { + return "[" + x + "," + y + ']'; + } } - + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/KeyboardLoader.java b/src/main/java/com/nulabinc/zxcvbn/matchers/KeyboardLoader.java index d238a62..17cea1f 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/KeyboardLoader.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/KeyboardLoader.java @@ -1,7 +1,6 @@ package com.nulabinc.zxcvbn.matchers; import com.nulabinc.zxcvbn.io.Resource; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -9,35 +8,34 @@ public abstract class KeyboardLoader { - private final String name; - - private final Resource resource; - - public KeyboardLoader(final String name, final Resource resource) { - this.name = name; - this.resource = resource; - } - - public Keyboard load() throws IOException { - InputStream inputStream = resource.getInputStream(); - String layout = loadAsString(inputStream); - return new Keyboard(name, buildAdjacentGraphBuilder(layout)); - } - - protected abstract Keyboard.AdjacentGraphBuilder buildAdjacentGraphBuilder(final String layout); - - private static String loadAsString(final InputStream input) { - try (final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"))) { - final StringBuilder sb = new StringBuilder(1024 * 4); - String str; - while ((str = reader.readLine()) != null) { - sb.append(str); - sb.append('\n'); - } - return sb.toString(); - } catch (final IOException e) { - throw new IllegalArgumentException(e); - } + private final String name; + + private final Resource resource; + + public KeyboardLoader(final String name, final Resource resource) { + this.name = name; + this.resource = resource; + } + + public Keyboard load() throws IOException { + InputStream inputStream = resource.getInputStream(); + String layout = loadAsString(inputStream); + return new Keyboard(name, buildAdjacentGraphBuilder(layout)); + } + + protected abstract Keyboard.AdjacentGraphBuilder buildAdjacentGraphBuilder(final String layout); + + private static String loadAsString(final InputStream input) { + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"))) { + final StringBuilder sb = new StringBuilder(1024 * 4); + String str; + while ((str = reader.readLine()) != null) { + sb.append(str); + sb.append('\n'); + } + return sb.toString(); + } catch (final IOException e) { + throw new IllegalArgumentException(e); } - + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/L33tSubDict.java b/src/main/java/com/nulabinc/zxcvbn/matchers/L33tSubDict.java index b58f3aa..4a87c8a 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/L33tSubDict.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/L33tSubDict.java @@ -10,62 +10,66 @@ public class L33tSubDict implements Iterable> { - private final List> subDicts; + private final List> subDicts; - L33tSubDict(Map> table) { - this.subDicts = buildSubDicts(table); - } + L33tSubDict(Map> table) { + this.subDicts = buildSubDicts(table); + } - private List> buildSubDicts(final Map> table) { - final Set> initialSubs = new LinkedHashSet<>(); - initialSubs.add(new ArrayList()); - final Set> subs = helper(table, table.keySet().iterator(), initialSubs); + private List> buildSubDicts( + final Map> table) { + final Set> initialSubs = new LinkedHashSet<>(); + initialSubs.add(new ArrayList()); + final Set> subs = helper(table, table.keySet().iterator(), initialSubs); - List> subDicts = new ArrayList<>(); - for (List sub : subs) { - Map subDict = new HashMap<>(); - for (CharSequence ref : sub) { - subDict.put(ref.charAt(0), ref.charAt(1)); - } - subDicts.add(subDict); - } - return subDicts; + List> subDicts = new ArrayList<>(); + for (List sub : subs) { + Map subDict = new HashMap<>(); + for (CharSequence ref : sub) { + subDict.put(ref.charAt(0), ref.charAt(1)); + } + subDicts.add(subDict); } + return subDicts; + } - private Set> helper(final Map> table, final Iterator keysIterator, final Set> subs) { - if (!keysIterator.hasNext()) { - return subs; - } + private Set> helper( + final Map> table, + final Iterator keysIterator, + final Set> subs) { + if (!keysIterator.hasNext()) { + return subs; + } - Character key = keysIterator.next(); - Set> nextSubs = new LinkedHashSet<>(); - for (Character l33tChr : table.get(key)) { - for (List sub : subs) { - boolean found = false; - for (int i = 0; i < sub.size(); i++) { - if (sub.get(i).charAt(0) == l33tChr) { - List subAlternative = new ArrayList<>(sub); - subAlternative.remove(i); - subAlternative.add(String.valueOf(new char[]{l33tChr, key})); - nextSubs.add(sub); - nextSubs.add(subAlternative); - found = true; - break; - } - } - if (!found) { - List subExtension = new ArrayList<>(sub); - subExtension.add(String.valueOf(new char[]{l33tChr, key})); - nextSubs.add(subExtension); - } - } + Character key = keysIterator.next(); + Set> nextSubs = new LinkedHashSet<>(); + for (Character l33tChr : table.get(key)) { + for (List sub : subs) { + boolean found = false; + for (int i = 0; i < sub.size(); i++) { + if (sub.get(i).charAt(0) == l33tChr) { + List subAlternative = new ArrayList<>(sub); + subAlternative.remove(i); + subAlternative.add(String.valueOf(new char[] {l33tChr, key})); + nextSubs.add(sub); + nextSubs.add(subAlternative); + found = true; + break; + } } - - return helper(table, keysIterator, nextSubs); + if (!found) { + List subExtension = new ArrayList<>(sub); + subExtension.add(String.valueOf(new char[] {l33tChr, key})); + nextSubs.add(subExtension); + } + } } - @Override - public Iterator> iterator() { - return subDicts.iterator(); - } + return helper(table, keysIterator, nextSubs); + } + + @Override + public Iterator> iterator() { + return subDicts.iterator(); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/MatchFactory.java b/src/main/java/com/nulabinc/zxcvbn/matchers/MatchFactory.java index 7d8c280..4f84472 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/MatchFactory.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/MatchFactory.java @@ -1,90 +1,109 @@ package com.nulabinc.zxcvbn.matchers; import com.nulabinc.zxcvbn.Pattern; - import java.util.List; import java.util.Map; public class MatchFactory { - private MatchFactory() { } - - public static Match createBruteforceMatch(int i, int j, CharSequence token) { - return new Match.Builder(Pattern.Bruteforce, i, j, token).build(); - } - + private MatchFactory() {} - public static Match createDictionaryMatch(int i, int j, CharSequence token, CharSequence matchedWord, int rank, String dictionaryName) { - return new Match.Builder(Pattern.Dictionary, i, j, token) - .matchedWord(matchedWord) - .rank(rank) - .dictionaryName(dictionaryName) - .reversed(false) - .l33t(false) - .build(); - } + public static Match createBruteforceMatch(int i, int j, CharSequence token) { + return new Match.Builder(Pattern.Bruteforce, i, j, token).build(); + } - public static Match createReversedDictionaryMatch(int i, int j, CharSequence token, CharSequence matchedWord, int rank, String dictionaryName) { - return new Match.Builder(Pattern.Dictionary, i, j, token) - .matchedWord(matchedWord) - .rank(rank) - .dictionaryName(dictionaryName) - .reversed(true) - .l33t(false) - .build(); - } + public static Match createDictionaryMatch( + int i, int j, CharSequence token, CharSequence matchedWord, int rank, String dictionaryName) { + return new Match.Builder(Pattern.Dictionary, i, j, token) + .matchedWord(matchedWord) + .rank(rank) + .dictionaryName(dictionaryName) + .reversed(false) + .l33t(false) + .build(); + } - public static Match createDictionaryL33tMatch(int i, int j, CharSequence token, CharSequence matchedWord, int rank, String dictionaryName, boolean reversed, Map sub, String subDisplay) { - return new Match.Builder(Pattern.Dictionary, i, j, token) - .matchedWord(matchedWord) - .rank(rank) - .dictionaryName(dictionaryName) - .reversed(reversed) - .sub(sub) - .subDisplay(subDisplay) - .l33t(true) - .build(); - } + public static Match createReversedDictionaryMatch( + int i, int j, CharSequence token, CharSequence matchedWord, int rank, String dictionaryName) { + return new Match.Builder(Pattern.Dictionary, i, j, token) + .matchedWord(matchedWord) + .rank(rank) + .dictionaryName(dictionaryName) + .reversed(true) + .l33t(false) + .build(); + } - public static Match createSpatialMatch(int i, int j, CharSequence token, String graph, int turns, int shiftedCount) { - return new Match.Builder(Pattern.Spatial, i, j, token) - .graph(graph) - .turns(turns) - .shiftedCount(shiftedCount) - .build(); - } + public static Match createDictionaryL33tMatch( + int i, + int j, + CharSequence token, + CharSequence matchedWord, + int rank, + String dictionaryName, + boolean reversed, + Map sub, + String subDisplay) { + return new Match.Builder(Pattern.Dictionary, i, j, token) + .matchedWord(matchedWord) + .rank(rank) + .dictionaryName(dictionaryName) + .reversed(reversed) + .sub(sub) + .subDisplay(subDisplay) + .l33t(true) + .build(); + } - public static Match createRepeatMatch(int i, int j, CharSequence token, CharSequence baseToken, double baseGuesses, List baseMatches, int repeatCount) { - return new Match.Builder(Pattern.Repeat, i, j, token) - .baseToken(baseToken) - .baseGuesses(baseGuesses) - .baseMatches(baseMatches) - .repeatCount(repeatCount) - .build(); - } + public static Match createSpatialMatch( + int i, int j, CharSequence token, String graph, int turns, int shiftedCount) { + return new Match.Builder(Pattern.Spatial, i, j, token) + .graph(graph) + .turns(turns) + .shiftedCount(shiftedCount) + .build(); + } - public static Match createSequenceMatch(int i, int j, CharSequence token, String sequenceName, int sequenceSpace, boolean ascending) { - return new Match.Builder(Pattern.Sequence, i, j, token) - .sequenceName(sequenceName) - .sequenceSpace(sequenceSpace) - .ascending(ascending) - .build(); - } + public static Match createRepeatMatch( + int i, + int j, + CharSequence token, + CharSequence baseToken, + double baseGuesses, + List baseMatches, + int repeatCount) { + return new Match.Builder(Pattern.Repeat, i, j, token) + .baseToken(baseToken) + .baseGuesses(baseGuesses) + .baseMatches(baseMatches) + .repeatCount(repeatCount) + .build(); + } - public static Match createRegexMatch(int i, int j, CharSequence token, String regexName, java.util.regex.Matcher regexMatch) { - return new Match.Builder(Pattern.Regex, i, j, token) - .regexName(regexName) - .regexMatch(regexMatch) - .build(); - } + public static Match createSequenceMatch( + int i, int j, CharSequence token, String sequenceName, int sequenceSpace, boolean ascending) { + return new Match.Builder(Pattern.Sequence, i, j, token) + .sequenceName(sequenceName) + .sequenceSpace(sequenceSpace) + .ascending(ascending) + .build(); + } - public static Match createDateMatch(int i, int j, CharSequence token, String separator, int year, int month, int day) { - return new Match.Builder(Pattern.Date, i, j, token) - .separator(separator) - .year(year) - .month(month) - .day(day) - .build(); - } + public static Match createRegexMatch( + int i, int j, CharSequence token, String regexName, java.util.regex.Matcher regexMatch) { + return new Match.Builder(Pattern.Regex, i, j, token) + .regexName(regexName) + .regexMatch(regexMatch) + .build(); + } + public static Match createDateMatch( + int i, int j, CharSequence token, String separator, int year, int month, int day) { + return new Match.Builder(Pattern.Date, i, j, token) + .separator(separator) + .year(year) + .month(month) + .day(day) + .build(); + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedAdjacentGraphBuilder.java b/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedAdjacentGraphBuilder.java index 95ce715..ba24d47 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedAdjacentGraphBuilder.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedAdjacentGraphBuilder.java @@ -5,34 +5,34 @@ public class SlantedAdjacentGraphBuilder extends Keyboard.AdjacentGraphBuilder { - public SlantedAdjacentGraphBuilder(final String layout) { - super(layout); - } + public SlantedAdjacentGraphBuilder(final String layout) { + super(layout); + } - /** - * returns the six adjacent coordinates on a standard keyboard, where each row is slanted to the - * right from the last. adjacencies are clockwise, starting with key to the left, then two keys - * above, then right key, then two keys below. (that is, only near-diagonal keys are adjacent, - * so g's coordinate is adjacent to those of t,y,b,v, but not those of r,u,n,c.) - */ - @Override - protected List getAdjacentCoords(final Position position) { - return Arrays.asList( - Position.of(position.getX() - 1, position.getY()), - Position.of(position.getX(), position.getY() - 1), - Position.of(position.getX() + 1, position.getY() - 1), - Position.of(position.getX() + 1, position.getY()), - Position.of(position.getX(), position.getY() + 1), - Position.of(position.getX() - 1, position.getY() + 1)); - } + /** + * returns the six adjacent coordinates on a standard keyboard, where each row is slanted to the + * right from the last. adjacencies are clockwise, starting with key to the left, then two keys + * above, then right key, then two keys below. (that is, only near-diagonal keys are adjacent, so + * g's coordinate is adjacent to those of t,y,b,v, but not those of r,u,n,c.) + */ + @Override + protected List getAdjacentCoords(final Position position) { + return Arrays.asList( + Position.of(position.getX() - 1, position.getY()), + Position.of(position.getX(), position.getY() - 1), + Position.of(position.getX() + 1, position.getY() - 1), + Position.of(position.getX() + 1, position.getY()), + Position.of(position.getX(), position.getY() + 1), + Position.of(position.getX() - 1, position.getY() + 1)); + } - @Override - public boolean isSlanted() { - return true; - } + @Override + public boolean isSlanted() { + return true; + } - @Override - protected int calcSlant(int y) { - return y - 1; - } + @Override + protected int calcSlant(int y) { + return y - 1; + } } diff --git a/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedKeyboardLoader.java b/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedKeyboardLoader.java index 64efc21..08bef4a 100644 --- a/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedKeyboardLoader.java +++ b/src/main/java/com/nulabinc/zxcvbn/matchers/SlantedKeyboardLoader.java @@ -4,13 +4,12 @@ public class SlantedKeyboardLoader extends KeyboardLoader { - public SlantedKeyboardLoader(final String name, final Resource inputStreamSource) { - super(name, inputStreamSource); - } - - @Override - protected Keyboard.AdjacentGraphBuilder buildAdjacentGraphBuilder(final String layout) { - return new SlantedAdjacentGraphBuilder(layout); - } - + public SlantedKeyboardLoader(final String name, final Resource inputStreamSource) { + super(name, inputStreamSource); + } + + @Override + protected Keyboard.AdjacentGraphBuilder buildAdjacentGraphBuilder(final String layout) { + return new SlantedAdjacentGraphBuilder(layout); + } } diff --git a/src/main/java9/module-info.java b/src/main/java9/module-info.java index 474dcbd..d815613 100644 --- a/src/main/java9/module-info.java +++ b/src/main/java9/module-info.java @@ -1,4 +1,4 @@ module com.nulabinc.zxcvbn { - exports com.nulabinc.zxcvbn; - exports com.nulabinc.zxcvbn.matchers; -} \ No newline at end of file + exports com.nulabinc.zxcvbn; + exports com.nulabinc.zxcvbn.matchers; +} diff --git a/src/test/java/com/nulabinc/zxcvbn/ApproachComparisonTest.java b/src/test/java/com/nulabinc/zxcvbn/ApproachComparisonTest.java index c81029e..f96a86b 100644 --- a/src/test/java/com/nulabinc/zxcvbn/ApproachComparisonTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/ApproachComparisonTest.java @@ -1,11 +1,10 @@ package com.nulabinc.zxcvbn; -import org.junit.*; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import static java.nio.CharBuffer.wrap; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.fail; -import javax.script.ScriptEngine; -import javax.script.ScriptException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -14,201 +13,177 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; - -import static java.nio.CharBuffer.wrap; -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertNotNull; -import static junit.framework.TestCase.fail; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import org.junit.*; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; /** * These tests compare the output from different approaches for calculating password strength. * - * The approaches include measuring String passwords with Java, measuring CharSequence passwords + *

The approaches include measuring String passwords with Java, measuring CharSequence passwords * in Java, and using the JavaScript version. The measure method with an empty list as the second - * parameter is also compared. - * All versions should produce the same results. + * parameter is also compared. All versions should produce the same results. * - * The list of password to do the comparisons with is loaded from passwords.txt in the test/resources - * folder. + *

The list of password to do the comparisons with is loaded from passwords.txt in the + * test/resources folder. */ @RunWith(Parameterized.class) public class ApproachComparisonTest { - private static ScriptEngine engine; - - private final CharSequence password; - - private Strength charSequenceStrength; - private Strength stringStrength; - private Strength stringInputsStrength; - private JavaScriptStrength jsStrength; - - public ApproachComparisonTest(CharSequence password) { - this.password = wrap(password); - - Zxcvbn zxcvbn = new Zxcvbn(); - - calculateAndRecordStrengthUsingAllMethods(password, zxcvbn); - } - - private void calculateAndRecordStrengthUsingAllMethods(CharSequence password, Zxcvbn zxcvbn) { - charSequenceStrength = zxcvbn.measure(new WipeableString(password)); - - stringStrength = zxcvbn.measure(password.toString()); - - stringInputsStrength = zxcvbn.measure(password, Collections.emptyList()); - - jsStrength = invokeJsVersion(password); - } - - //=================================================================================// - - @Test - public void keyValuesAreNotNull() { - assertNotNull(password); - assertNotNull(stringStrength); - assertNotNull(charSequenceStrength); - assertNotNull(stringInputsStrength); - assertNotNull(jsStrength); + private static ScriptEngine engine; + + private final CharSequence password; + + private Strength charSequenceStrength; + private Strength stringStrength; + private Strength stringInputsStrength; + private JavaScriptStrength jsStrength; + + public ApproachComparisonTest(CharSequence password) { + this.password = wrap(password); + + Zxcvbn zxcvbn = new Zxcvbn(); + + calculateAndRecordStrengthUsingAllMethods(password, zxcvbn); + } + + private void calculateAndRecordStrengthUsingAllMethods(CharSequence password, Zxcvbn zxcvbn) { + charSequenceStrength = zxcvbn.measure(new WipeableString(password)); + + stringStrength = zxcvbn.measure(password.toString()); + + stringInputsStrength = zxcvbn.measure(password, Collections.emptyList()); + + jsStrength = invokeJsVersion(password); + } + + // =================================================================================// + + @Test + public void keyValuesAreNotNull() { + assertNotNull(password); + assertNotNull(stringStrength); + assertNotNull(charSequenceStrength); + assertNotNull(stringInputsStrength); + assertNotNull(jsStrength); + } + + @Test + public void passwordStrengthMatchesStringStrength() { + assertEquals(stringStrength.getScore(), charSequenceStrength.getScore()); + } + + @Test + public void passwordStrengthMatchesStringInputsStrength() { + assertEquals(stringStrength.getScore(), stringInputsStrength.getScore()); + } + + @Test + public void charsequenceAttackTimeMatchesStringAttackTime() { + assertEquals( + stringStrength.getCrackTimesDisplay().getOfflineFastHashing1e10PerSecond(), + charSequenceStrength.getCrackTimesDisplay().getOfflineFastHashing1e10PerSecond()); + } + + @Test + public void charsequenceStrengthPasswordMatchesStringStrengthPassword() { + assertEquals( + stringStrength.getPassword().toString(), charSequenceStrength.getPassword().toString()); + } + + @Test + public void strengthPasswordMatchesInput() { + assertEquals(password.toString(), charSequenceStrength.getPassword().toString()); + } + + @Test + public void strengthScoreMatchesJavascript() { + assertEquals(jsStrength.getScore(), charSequenceStrength.getScore()); + } + + @Test + public void strengthPasswordMatchesJavascript() { + assertEquals(jsStrength.getPassword(), charSequenceStrength.getPassword().toString()); + } + + @Test + public void charsequenceSuggestionsMatchStringSuggestions() { + assertStringListsAreEqual( + stringStrength.getFeedback().getSuggestions(), + charSequenceStrength.getFeedback().getSuggestions()); + } + + @Test + public void charsequenceGuessesMatchesStringGuesses() { + assertEquals(stringStrength.getGuessesLog10(), charSequenceStrength.getGuessesLog10(), 0.1); + } + + // =================================================================================// + + @Parameterized.Parameters(name = "{0}") + public static Iterable data() throws IOException { + List passwords = new LinkedList<>(); + passwords.add(new Object[] {""}); + try (InputStream data = ApproachComparisonTest.class.getResourceAsStream("/passwords.txt")) { + BufferedReader in = new BufferedReader(new InputStreamReader(data)); + String line; + while ((line = in.readLine()) != null) { + if (line.trim().length() > 0) { + passwords.add(new Object[] {line}); + } + } } + return passwords; + } - @Test - public void passwordStrengthMatchesStringStrength() { - assertEquals( - stringStrength.getScore(), - charSequenceStrength.getScore() - ); - } + public void assertStringListsAreEqual(List expectedStrings, List actualStrings) { + Collections.sort(expectedStrings); + Collections.sort(actualStrings); - @Test - public void passwordStrengthMatchesStringInputsStrength() { - assertEquals( - stringStrength.getScore(), - stringInputsStrength.getScore() - ); - } + assertEquals(expectedStrings.size(), actualStrings.size()); - @Test - public void charsequenceAttackTimeMatchesStringAttackTime() { - assertEquals( - stringStrength.getCrackTimesDisplay().getOfflineFastHashing1e10PerSecond(), - charSequenceStrength.getCrackTimesDisplay().getOfflineFastHashing1e10PerSecond() - ); + for (int n = 0; n < expectedStrings.size(); n++) { + assertEquals(expectedStrings.get(n), actualStrings.get(n)); } + } - @Test - public void charsequenceStrengthPasswordMatchesStringStrengthPassword() { - assertEquals( - stringStrength.getPassword().toString(), - charSequenceStrength.getPassword().toString() - ); - } + static class JavaScriptStrength { + private final Map values; - @Test - public void strengthPasswordMatchesInput() { - assertEquals( - password.toString(), - charSequenceStrength.getPassword().toString() - ); + public JavaScriptStrength(Map values) { + this.values = values; } - @Test - public void strengthScoreMatchesJavascript() { - assertEquals( - jsStrength.getScore(), - charSequenceStrength.getScore() - ); + public int getScore() { + Object score = values.get("score"); + // nashorn returns int, rhino returns double + if (score instanceof Double) { + return ((Double) score).intValue(); + } else { + return (int) score; + } } - @Test - public void strengthPasswordMatchesJavascript() { - assertEquals( - jsStrength.getPassword(), - charSequenceStrength.getPassword().toString() - ); + public String getPassword() { + return (String) values.get("password"); } + } - @Test - public void charsequenceSuggestionsMatchStringSuggestions() { - assertStringListsAreEqual( - stringStrength.getFeedback().getSuggestions(), - charSequenceStrength.getFeedback().getSuggestions() - ); + @SuppressWarnings("unchecked") + public JavaScriptStrength invokeJsVersion(CharSequence password) { + engine.put("pwd", password.toString()); + try { + return new JavaScriptStrength((Map) engine.eval("zxcvbn(pwd);")); + } catch (ScriptException e) { + fail("Error invoking JavaScript version for password " + password); + return null; } + } - @Test - public void charsequenceGuessesMatchesStringGuesses() { - assertEquals( - stringStrength.getGuessesLog10(), - charSequenceStrength.getGuessesLog10(), - 0.1 - ); - } - - //=================================================================================// - - @Parameterized.Parameters(name = "{0}") - public static Iterable data() throws IOException { - List passwords = new LinkedList<>(); - passwords.add(new Object[]{""}); - try (InputStream data = ApproachComparisonTest.class.getResourceAsStream("/passwords.txt")) { - BufferedReader in = new BufferedReader(new InputStreamReader(data)); - String line; - while ((line = in.readLine()) != null) { - if (line.trim().length() > 0) { - passwords.add(new Object[]{line}); - } - } - } - return passwords; - } - - public void assertStringListsAreEqual(List expectedStrings, List actualStrings) { - Collections.sort(expectedStrings); - Collections.sort(actualStrings); - - assertEquals(expectedStrings.size(), actualStrings.size()); - - for (int n = 0; n < expectedStrings.size(); n++) { - assertEquals(expectedStrings.get(n), actualStrings.get(n)); - } - } - - static class JavaScriptStrength { - private final Map values; - - public JavaScriptStrength(Map values) { - this.values = values; - } - - public int getScore() { - Object score = values.get("score"); - // nashorn returns int, rhino returns double - if (score instanceof Double) { - return ((Double) score).intValue(); - } else { - return (int) score; - } - } - - public String getPassword() { - return (String)values.get("password"); - } - } - - @SuppressWarnings("unchecked") - public JavaScriptStrength invokeJsVersion(CharSequence password) { - engine.put("pwd",password.toString()); - try { - return new JavaScriptStrength((Map) engine.eval("zxcvbn(pwd);")); - } catch (ScriptException e) { - fail("Error invoking JavaScript version for password " + password); - return null; - } - } - - @BeforeClass - public static void beforeClass() { - engine = new JSScriptEngineBuilder().build(); - } + @BeforeClass + public static void beforeClass() { + engine = new JSScriptEngineBuilder().build(); + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/EdgeCaseTest.java b/src/test/java/com/nulabinc/zxcvbn/EdgeCaseTest.java index 2dc79fb..f59780c 100644 --- a/src/test/java/com/nulabinc/zxcvbn/EdgeCaseTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/EdgeCaseTest.java @@ -4,39 +4,37 @@ public class EdgeCaseTest { - /** - * Reproduce Issue #49 from GitHub. - * - * This should fail if the trimTrailingWhitespace call is removed from WipeableString.parseInt(s,radix). - */ - @Test - public void testWindowsNewlineInDate() { - StringBuilder buf = new StringBuilder("PW2001"); - buf.append((char)10); - buf.append((char)13); - buf.append("0101"); - new Zxcvbn().measure(buf.toString()); - } + /** + * Reproduce Issue #49 from GitHub. + * + *

This should fail if the trimTrailingWhitespace call is removed from + * WipeableString.parseInt(s,radix). + */ + @Test + public void testWindowsNewlineInDate() { + StringBuilder buf = new StringBuilder("PW2001"); + buf.append((char) 10); + buf.append((char) 13); + buf.append("0101"); + new Zxcvbn().measure(buf.toString()); + } - @Test - public void testUnixNewlineInDate() { - StringBuilder buf = new StringBuilder("PW2001"); - buf.append((char)13); - buf.append("0101"); - new Zxcvbn().measure(buf.toString()); - } + @Test + public void testUnixNewlineInDate() { + StringBuilder buf = new StringBuilder("PW2001"); + buf.append((char) 13); + buf.append("0101"); + new Zxcvbn().measure(buf.toString()); + } - @Test - public void testSpaceAfterDate() { - new Zxcvbn().measure("PW2009 "); - } - - /** - * Try to reproduce GitHub issue #34 - */ - @Test - public void testJustFourDigitNumber() { - new Zxcvbn().measure("8604 "); - } + @Test + public void testSpaceAfterDate() { + new Zxcvbn().measure("PW2009 "); + } + /** Try to reproduce GitHub issue #34 */ + @Test + public void testJustFourDigitNumber() { + new Zxcvbn().measure("8604 "); + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/FeedbackTest.java b/src/test/java/com/nulabinc/zxcvbn/FeedbackTest.java index f091444..f071f87 100644 --- a/src/test/java/com/nulabinc/zxcvbn/FeedbackTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/FeedbackTest.java @@ -1,202 +1,277 @@ package com.nulabinc.zxcvbn; import com.nulabinc.zxcvbn.matchers.Match; +import java.text.SimpleDateFormat; +import java.util.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; -import java.text.SimpleDateFormat; -import java.util.*; - @RunWith(Parameterized.class) public class FeedbackTest { - private String password; - private String expectedWarning; - private String[] expectedSuggestions; - - public FeedbackTest(String password, String expectedWarning, String[] expectedSuggestions) { - this.password = password; - this.expectedWarning = expectedWarning; - this.expectedSuggestions = expectedSuggestions; - } + private String password; + private String expectedWarning; + private String[] expectedSuggestions; - @Test - public void testWarning() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback(); - ResourceBundle resourceBundle = ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.ROOT); + public FeedbackTest(String password, String expectedWarning, String[] expectedSuggestions) { + this.password = password; + this.expectedWarning = expectedWarning; + this.expectedSuggestions = expectedSuggestions; + } - String expectedWarningL10n = expectedWarning.length() > 0 ? resourceBundle.getString(expectedWarning) : ""; - Assert.assertEquals("Unexpected warning", expectedWarningL10n, feedback.getWarning(Locale.ENGLISH)); - } + @Test + public void testWarning() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback(); + ResourceBundle resourceBundle = + ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.ROOT); - @Test - public void testJapaneseWarning() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback(); - ResourceBundle resourceBundle = ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.JAPANESE); + String expectedWarningL10n = + expectedWarning.length() > 0 ? resourceBundle.getString(expectedWarning) : ""; + Assert.assertEquals( + "Unexpected warning", expectedWarningL10n, feedback.getWarning(Locale.ENGLISH)); + } - String expectedWarningL10n = expectedWarning.length() > 0 ? resourceBundle.getString(expectedWarning) : ""; - Assert.assertEquals("Unexpected warning", expectedWarningL10n, feedback.getWarning(Locale.JAPANESE)); - } + @Test + public void testJapaneseWarning() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback(); + ResourceBundle resourceBundle = + ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.JAPANESE); - @Test - public void testReplaceMessage() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback(); + String expectedWarningL10n = + expectedWarning.length() > 0 ? resourceBundle.getString(expectedWarning) : ""; + Assert.assertEquals( + "Unexpected warning", expectedWarningL10n, feedback.getWarning(Locale.JAPANESE)); + } - Map messages = new HashMap<>(); - ResourceBundle resourceBundle = ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.JAPANESE); - messages.put(Locale.ITALIAN, resourceBundle); - Feedback replacedFeedback = feedback.replaceResourceBundle(messages); + @Test + public void testReplaceMessage() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback(); - String expectedWarningL10n = expectedWarning.length() > 0 ? resourceBundle.getString(expectedWarning) : ""; - Assert.assertEquals("Unexpected warning", expectedWarningL10n, replacedFeedback.getWarning(Locale.ITALIAN)); - } + Map messages = new HashMap<>(); + ResourceBundle resourceBundle = + ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.JAPANESE); + messages.put(Locale.ITALIAN, resourceBundle); + Feedback replacedFeedback = feedback.replaceResourceBundle(messages); - @Test - public void testSuggestions() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback(); - ResourceBundle resourceBundle = ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.ROOT); - - String[] expectedSuggestionsL10n = new String[expectedSuggestions.length]; - for (int i = 0; i < expectedSuggestions.length; i++) { - String expectedSuggestion = expectedSuggestions[i]; - expectedSuggestionsL10n[i] = resourceBundle.getString(expectedSuggestion); - } - Assert.assertArrayEquals("Unexpected suggestions", expectedSuggestionsL10n, feedback.getSuggestions(Locale.ENGLISH).toArray()); - } + String expectedWarningL10n = + expectedWarning.length() > 0 ? resourceBundle.getString(expectedWarning) : ""; + Assert.assertEquals( + "Unexpected warning", expectedWarningL10n, replacedFeedback.getWarning(Locale.ITALIAN)); + } + + @Test + public void testSuggestions() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback(); + ResourceBundle resourceBundle = + ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.ROOT); - @Test - public void testJapaneseSuggestions() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback(); - ResourceBundle resourceBundle = ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.JAPANESE); - - String[] expectedSuggestionsL10n = new String[expectedSuggestions.length]; - for (int i = 0; i < expectedSuggestions.length; i++) { - String expectedSuggestion = expectedSuggestions[i]; - expectedSuggestionsL10n[i] = resourceBundle.getString(expectedSuggestion); - } - Assert.assertArrayEquals("Unexpected suggestions", expectedSuggestionsL10n, feedback.getSuggestions(Locale.JAPANESE).toArray()); + String[] expectedSuggestionsL10n = new String[expectedSuggestions.length]; + for (int i = 0; i < expectedSuggestions.length; i++) { + String expectedSuggestion = expectedSuggestions[i]; + expectedSuggestionsL10n[i] = resourceBundle.getString(expectedSuggestion); } + Assert.assertArrayEquals( + "Unexpected suggestions", + expectedSuggestionsL10n, + feedback.getSuggestions(Locale.ENGLISH).toArray()); + } - @Test - public void testUnknownWarning() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback().withResourceBundle(null); + @Test + public void testJapaneseSuggestions() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback(); + ResourceBundle resourceBundle = + ResourceBundle.getBundle("com/nulabinc/zxcvbn/messages", Locale.JAPANESE); - Assert.assertEquals("Unexpected warning", expectedWarning, feedback.getWarning()); + String[] expectedSuggestionsL10n = new String[expectedSuggestions.length]; + for (int i = 0; i < expectedSuggestions.length; i++) { + String expectedSuggestion = expectedSuggestions[i]; + expectedSuggestionsL10n[i] = resourceBundle.getString(expectedSuggestion); } + Assert.assertArrayEquals( + "Unexpected suggestions", + expectedSuggestionsL10n, + feedback.getSuggestions(Locale.JAPANESE).toArray()); + } - @Test - public void testUnknownSuggestions() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - Feedback feedback = strength.getFeedback().withResourceBundle(null); + @Test + public void testUnknownWarning() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback().withResourceBundle(null); - Assert.assertArrayEquals("Unexpected suggestions", expectedSuggestions, feedback.getSuggestions().toArray()); - } + Assert.assertEquals("Unexpected warning", expectedWarning, feedback.getWarning()); + } - @Test - public void testPrepareGetFeedback() { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - List sequence = strength.getSequence(); - if (sequence.size() == 0) { - return; - } - if (sequence.size() > 1) { - List sublistSequence = sequence.subList(1, sequence.size()); - Assert.assertEquals(sequence.size(), sublistSequence.size() + 1); - } - } + @Test + public void testUnknownSuggestions() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + Feedback feedback = strength.getFeedback().withResourceBundle(null); - @Parameterized.Parameters(name = "{0}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {"bbb", Feedback.REPEAT_WARNING_LIKE_AAA, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS - }}, - {"testtesttest", Feedback.REPEAT_WARNING_LIKE_ABCABCABC, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS - }}, - {"zxcvbnm,./", Feedback.SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN - }}, - {"lkjhgfdsa", Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DICTIONARY_SUGGESTIONS_REVERSED - }}, - {"justshort", "", new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"", "", new String[]{ - Feedback.DEFAULT_SUGGESTIONS_USE_FEW_WORDS, - Feedback.DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS, - }}, - {"efghijk", Feedback.SEQUENCE_WARNING_LIKE_ABCOR6543, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES - }}, - {new SimpleDateFormat("yyyy").format(new Date()), Feedback.REGEX_WARNING_RECENT_YEARS, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.REGEX_SUGGESTIONS_AVOID_RECENT_YEARS - }}, - {new SimpleDateFormat("dd-MM-yyyy").format(new Date()), Feedback.DATE_WARNING_DATES, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DATE_SUGGESTIONS_AVOID_DATES - }}, - {"password", Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"access", Feedback.DICTIONARY_WARNING_PASSWORDS_TOP100, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"psychnaut1", Feedback.DICTIONARY_WARNING_PASSWORDS_VERY_COMMON, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"distinguished", Feedback.DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"password8", Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"mitchell", Feedback.DICTIONARY_WARNING_ETC_NAMES_THEMSELVES, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD - }}, - {"Mitchell1", Feedback.DICTIONARY_WARNING_ETC_NAMES_COMMON, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DICTIONARY_SUGGESTIONS_CAPITALIZATION, - }}, - {"Password", Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DICTIONARY_SUGGESTIONS_CAPITALIZATION - }}, - {"PASSWORD", Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DICTIONARY_SUGGESTIONS_ALL_UPPERCASE - }}, - {"remmurd", Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DICTIONARY_SUGGESTIONS_REVERSED - }}, - {"p@ssword", Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, new String[]{ - Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, - Feedback.DICTIONARY_SUGGESTIONS_L33T - }}, - {"correcthorsebatterystaple", "", new String[0]} - }); + Assert.assertArrayEquals( + "Unexpected suggestions", expectedSuggestions, feedback.getSuggestions().toArray()); + } + + @Test + public void testPrepareGetFeedback() { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + List sequence = strength.getSequence(); + if (sequence.size() == 0) { + return; } + if (sequence.size() > 1) { + List sublistSequence = sequence.subList(1, sequence.size()); + Assert.assertEquals(sequence.size(), sublistSequence.size() + 1); + } + } + @Parameterized.Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { + { + "bbb", + Feedback.REPEAT_WARNING_LIKE_AAA, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS + } + }, + { + "testtesttest", + Feedback.REPEAT_WARNING_LIKE_ABCABCABC, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.REPEAT_SUGGESTIONS_AVOID_REPEATED_WORDS + } + }, + { + "zxcvbnm,./", + Feedback.SPATIAL_WARNING_STRAIGHT_ROWS_OF_KEYS, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.SPATIAL_SUGGESTIONS_USE_LONGER_KEYBOARD_PATTERN + } + }, + { + "lkjhgfdsa", + Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, Feedback.DICTIONARY_SUGGESTIONS_REVERSED + } + }, + {"justshort", "", new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD}}, + { + "", + "", + new String[] { + Feedback.DEFAULT_SUGGESTIONS_USE_FEW_WORDS, + Feedback.DEFAULT_SUGGESTIONS_NO_NEED_SYMBOLS, + } + }, + { + "efghijk", + Feedback.SEQUENCE_WARNING_LIKE_ABCOR6543, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.SEQUENCE_SUGGESTIONS_AVOID_SEQUENCES + } + }, + { + new SimpleDateFormat("yyyy").format(new Date()), + Feedback.REGEX_WARNING_RECENT_YEARS, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.REGEX_SUGGESTIONS_AVOID_RECENT_YEARS + } + }, + { + new SimpleDateFormat("dd-MM-yyyy").format(new Date()), + Feedback.DATE_WARNING_DATES, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, Feedback.DATE_SUGGESTIONS_AVOID_DATES + } + }, + { + "password", + Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10, + new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD} + }, + { + "access", + Feedback.DICTIONARY_WARNING_PASSWORDS_TOP100, + new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD} + }, + { + "psychnaut1", + Feedback.DICTIONARY_WARNING_PASSWORDS_VERY_COMMON, + new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD} + }, + { + "distinguished", + Feedback.DICTIONARY_WARNING_ENGLISH_WIKIPEDIA_ITSELF, + new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD} + }, + { + "password8", + Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, + new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD} + }, + { + "mitchell", + Feedback.DICTIONARY_WARNING_ETC_NAMES_THEMSELVES, + new String[] {Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD} + }, + { + "Mitchell1", + Feedback.DICTIONARY_WARNING_ETC_NAMES_COMMON, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.DICTIONARY_SUGGESTIONS_CAPITALIZATION, + } + }, + { + "Password", + Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.DICTIONARY_SUGGESTIONS_CAPITALIZATION + } + }, + { + "PASSWORD", + Feedback.DICTIONARY_WARNING_PASSWORDS_TOP10, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, + Feedback.DICTIONARY_SUGGESTIONS_ALL_UPPERCASE + } + }, + { + "remmurd", + Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, Feedback.DICTIONARY_SUGGESTIONS_REVERSED + } + }, + { + "p@ssword", + Feedback.DICTIONARY_WARNING_PASSWORDS_SIMILAR, + new String[] { + Feedback.EXTRA_SUGGESTIONS_ADD_ANOTHER_WORD, Feedback.DICTIONARY_SUGGESTIONS_L33T + } + }, + {"correcthorsebatterystaple", "", new String[0]} + }); + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/JSScriptEngineBuilder.java b/src/test/java/com/nulabinc/zxcvbn/JSScriptEngineBuilder.java index ed7465f..f6d9678 100644 --- a/src/test/java/com/nulabinc/zxcvbn/JSScriptEngineBuilder.java +++ b/src/test/java/com/nulabinc/zxcvbn/JSScriptEngineBuilder.java @@ -1,36 +1,34 @@ package com.nulabinc.zxcvbn; import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine; -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.Engine; - -import javax.script.ScriptEngine; -import javax.script.ScriptException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.net.URISyntaxException; import java.net.URL; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; public class JSScriptEngineBuilder { - public ScriptEngine build() { - final GraalJSScriptEngine engine = GraalJSScriptEngine.create( - Engine.newBuilder() - .option("engine.WarnInterpreterOnly", "false") - .build(), - Context.newBuilder("js")); - loadZxcvbnJs(engine); - return engine; - } + public ScriptEngine build() { + final GraalJSScriptEngine engine = + GraalJSScriptEngine.create( + Engine.newBuilder().option("engine.WarnInterpreterOnly", "false").build(), + Context.newBuilder("js")); + loadZxcvbnJs(engine); + return engine; + } - private void loadZxcvbnJs(ScriptEngine engine) { - try { - //using the 4.4.1 release - URL script = JSScriptEngineBuilder.class.getClassLoader().getResource("zxcvbn.js"); - engine.eval(new FileReader(new File(script.toURI()))); - } catch (URISyntaxException | FileNotFoundException | ScriptException e) { - throw new RuntimeException("Cannot instantiate Javascript Engine", e); - } + private void loadZxcvbnJs(ScriptEngine engine) { + try { + // using the 4.4.1 release + URL script = JSScriptEngineBuilder.class.getClassLoader().getResource("zxcvbn.js"); + engine.eval(new FileReader(new File(script.toURI()))); + } catch (URISyntaxException | FileNotFoundException | ScriptException e) { + throw new RuntimeException("Cannot instantiate Javascript Engine", e); } + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/JavaPortTest.java b/src/test/java/com/nulabinc/zxcvbn/JavaPortTest.java index d7aec91..0cdc3d6 100644 --- a/src/test/java/com/nulabinc/zxcvbn/JavaPortTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/JavaPortTest.java @@ -1,98 +1,101 @@ package com.nulabinc.zxcvbn; +import java.util.Arrays; +import java.util.Map; +import javax.script.ScriptEngine; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import javax.script.ScriptEngine; -import java.util.Arrays; -import java.util.Map; - @RunWith(Parameterized.class) public class JavaPortTest { - private static ScriptEngine engine; - private final String password; - private final Zxcvbn zxcvbn; + private static ScriptEngine engine; + private final String password; + private final Zxcvbn zxcvbn; - public JavaPortTest(String password) { - this.password = password; - this.zxcvbn = new Zxcvbn(); - } + public JavaPortTest(String password) { + this.password = password; + this.zxcvbn = new Zxcvbn(); + } - @BeforeClass - public static void initEngine() { - engine = new JSScriptEngineBuilder().build(); - } + @BeforeClass + public static void initEngine() { + engine = new JSScriptEngineBuilder().build(); + } - @Test - public void testMeasure() throws Exception { - // add password to the engine scope - engine.put("pwd", password); - @SuppressWarnings("unchecked") - Map result = (Map) engine.eval("zxcvbn(pwd);"); - Object score = result.get("score"); - int jsScore; - // nashorn returns int, rhino returns double - if (score instanceof Double) { - jsScore = ((Double) score).intValue(); - } else { - jsScore = (int) score; - } - Strength strength = zxcvbn.measure(password); - int javaScore = strength.getScore(); - Assert.assertEquals("Password score difference for " + password, jsScore, javaScore); + @Test + public void testMeasure() throws Exception { + // add password to the engine scope + engine.put("pwd", password); + @SuppressWarnings("unchecked") + Map result = (Map) engine.eval("zxcvbn(pwd);"); + Object score = result.get("score"); + int jsScore; + // nashorn returns int, rhino returns double + if (score instanceof Double) { + jsScore = ((Double) score).intValue(); + } else { + jsScore = (int) score; } + Strength strength = zxcvbn.measure(password); + int javaScore = strength.getScore(); + Assert.assertEquals("Password score difference for " + password, jsScore, javaScore); + } - @Parameterized.Parameters(name = "{0}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {"qwER43@!"}, - {"Tr0ub4dour&3"}, - {"correcthorsebatterystaple"}, - {"password"}, - {"drowssap"}, - {"passwordp"}, - {"passwordadmin"}, - {"p@$$word@dmin"}, - {"19700101"}, - {"20300101"}, - {"aaaaaaaaa"}, - {"123456789"}, - {"abcdefghijklmnopqrstuvwxyz"}, - {"qwertyuiop@["}, - {"zxcvbnm,./_"}, - {"asdfghjkl;:]"}, - {"pandapandapandapandapandapandapandapandapandaa"}, - {"appleappleappleappleappleappleappleappleapplea"}, - {"dncrbliehbvkehr734yf;ewhihwfph@houaegfueqpg30^r0urfvhej¥]e;l,ckvniwbgoidnci@oewhfoobojabouhqwou12482386fhoiwehe@o"}, - {"apple orenge aabb "}, - {"eTq($%u-44c_j9NJB45a#2#JP7sH"}, - {"IB7~EOw!51gug+7s#+%A9P1O/w8f"}, - {"1v_f%7JvS8w!_t398+ON-CObI#v0"}, - {"8lFmfc0!w)&iU9DM6~4_w)D)Y44J"}, - {"&BZ09gjG!iKG&#M09s_1Gr41&o%i"}, - {"T9Y-!ciS%XW9U5l/~aw9+4!5u8Ti"}, - {"QMji&0uze5O#%+%2e_Y08E(R6L8p"}, - {"6EG4y1nJASd!1~!//#6+Yhb1vW3d"}, - {"8$q_5f2U3s6~W(S7iv)_8N%lJkOE"}, - {"%nbd~$)2y/6hV6)2R9vYPpA49A~C"}, - {"xsw234rfvb"}, - {"yaq123edc"}, - {"cde345tgbn"}, - {"yaqwedcvb"}, - {"5621127"}, - {"61526611441"}, - {"0078690420729"}, - {"zhang198822"}, - {"Sigma@123"}, - {"password@123"}, - {"lkjhgfdsa"}, - {"hGFd"}, - //the following password fails in version 4.4.1 - //https://github.com/dropbox/zxcvbn/issues/174 -// {"Rh&pW%EXT=/Z1lzouG.wU_+2MT+FG4sm+&jqN?L25jDtjW3EQuppfvD_30Vo3K=SX4=z3-U2gVf7A0oSM5oWegRa_sV$-GLI3LzCo&@!h@$v#OkoN#@-eS8Y&W$pGmmVXc#XHAv?n$M+_wQx1FAB_*iaZE1_9ZV.cwn-d@+90B8z0bVOKc63lV9QntW0kryN7Y#rjv@0+Bd8hc-3WW_Yn%z5/DE?R*UeiKgR#$/F8kA9I!Ib*GDa.x0T7UWCCxDV&ithebyz$=7vW6TdmlmL%WZxmA7K%*Rg1035UO%WOTIgiMs4AjpmL1"} + @Parameterized.Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { + {"qwER43@!"}, + {"Tr0ub4dour&3"}, + {"correcthorsebatterystaple"}, + {"password"}, + {"drowssap"}, + {"passwordp"}, + {"passwordadmin"}, + {"p@$$word@dmin"}, + {"19700101"}, + {"20300101"}, + {"aaaaaaaaa"}, + {"123456789"}, + {"abcdefghijklmnopqrstuvwxyz"}, + {"qwertyuiop@["}, + {"zxcvbnm,./_"}, + {"asdfghjkl;:]"}, + {"pandapandapandapandapandapandapandapandapandaa"}, + {"appleappleappleappleappleappleappleappleapplea"}, + { + "dncrbliehbvkehr734yf;ewhihwfph@houaegfueqpg30^r0urfvhej¥]e;l,ckvniwbgoidnci@oewhfoobojabouhqwou12482386fhoiwehe@o" + }, + {"apple orenge aabb "}, + {"eTq($%u-44c_j9NJB45a#2#JP7sH"}, + {"IB7~EOw!51gug+7s#+%A9P1O/w8f"}, + {"1v_f%7JvS8w!_t398+ON-CObI#v0"}, + {"8lFmfc0!w)&iU9DM6~4_w)D)Y44J"}, + {"&BZ09gjG!iKG&#M09s_1Gr41&o%i"}, + {"T9Y-!ciS%XW9U5l/~aw9+4!5u8Ti"}, + {"QMji&0uze5O#%+%2e_Y08E(R6L8p"}, + {"6EG4y1nJASd!1~!//#6+Yhb1vW3d"}, + {"8$q_5f2U3s6~W(S7iv)_8N%lJkOE"}, + {"%nbd~$)2y/6hV6)2R9vYPpA49A~C"}, + {"xsw234rfvb"}, + {"yaq123edc"}, + {"cde345tgbn"}, + {"yaqwedcvb"}, + {"5621127"}, + {"61526611441"}, + {"0078690420729"}, + {"zhang198822"}, + {"Sigma@123"}, + {"password@123"}, + {"lkjhgfdsa"}, + {"hGFd"}, + // the following password fails in version 4.4.1 + // https://github.com/dropbox/zxcvbn/issues/174 + // + // {"Rh&pW%EXT=/Z1lzouG.wU_+2MT+FG4sm+&jqN?L25jDtjW3EQuppfvD_30Vo3K=SX4=z3-U2gVf7A0oSM5oWegRa_sV$-GLI3LzCo&@!h@$v#OkoN#@-eS8Y&W$pGmmVXc#XHAv?n$M+_wQx1FAB_*iaZE1_9ZV.cwn-d@+90B8z0bVOKc63lV9QntW0kryN7Y#rjv@0+Bd8hc-3WW_Yn%z5/DE?R*UeiKgR#$/F8kA9I!Ib*GDa.x0T7UWCCxDV&ithebyz$=7vW6TdmlmL%WZxmA7K%*Rg1035UO%WOTIgiMs4AjpmL1"} }); - } + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/MatchingTest.java b/src/test/java/com/nulabinc/zxcvbn/MatchingTest.java index 876cb70..491d720 100644 --- a/src/test/java/com/nulabinc/zxcvbn/MatchingTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/MatchingTest.java @@ -1,5 +1,8 @@ package com.nulabinc.zxcvbn; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.nulabinc.zxcvbn.matchers.DateMatcher; import com.nulabinc.zxcvbn.matchers.DictionaryMatcher; import com.nulabinc.zxcvbn.matchers.Keyboard; @@ -10,12 +13,6 @@ import com.nulabinc.zxcvbn.matchers.ReverseDictionaryMatcher; import com.nulabinc.zxcvbn.matchers.SequenceMatcher; import com.nulabinc.zxcvbn.matchers.SpatialMatcher; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.runners.Enclosed; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -24,567 +21,627 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Enclosed.class) public class MatchingTest { - private static void assertMatches(String prefix, Pattern expectedPattern, ExpectedMatch[] expectedMatches, List actualMatches) { - String msg = String.format("%s: matches.length == %s", prefix, expectedMatches.length); - assertEquals(msg, expectedMatches.length, actualMatches.size()); - for (int k = 0; k < expectedMatches.length; k++) { - ExpectedMatch expectedMatch = expectedMatches[k]; - Match actualMatch = actualMatches.get(k); - - msg = String.format("%s: matches[%s].pattern == '%s'", prefix, k, expectedPattern); - assertEquals(msg, expectedPattern, actualMatch.pattern); - - msg = String.format("%s: matches[%s] should start at %s", prefix, k, expectedMatch.start); - assertEquals(msg, expectedMatch.start, actualMatch.i); - - msg = String.format("%s: matches[%s] should end at %s", prefix, k, expectedMatch.end); - assertEquals(msg, expectedMatch.end, actualMatch.j); - - msg = String.format("%s: matches[%s].token == '%s'", prefix, k, expectedMatch.token); - assertEquals(msg, expectedMatch.token, actualMatch.token.toString()); - - for (String fieldName : expectedMatch.fields.keySet()) { - Object expectedValue = expectedMatch.fields.get(fieldName); - Object actualValue; - try { - actualValue = Match.class.getField(fieldName).get(actualMatch); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException(e); - } - msg = String.format("%s: matches[%s].%s == '%s'", prefix, k, fieldName, expectedValue); - if ((expectedValue instanceof String) && (actualValue instanceof CharSequence)) { - // If comparing a CharSequence to a String, convert the CharSequence to a String first - // because String.equals doesn't consider it equal if it isn't a String itself. - assertEquals(msg, expectedValue, actualValue.toString()); - } else { - assertEquals(msg, expectedValue, actualValue); - } - } - } + private static void assertMatches( + String prefix, + Pattern expectedPattern, + ExpectedMatch[] expectedMatches, + List actualMatches) { + String msg = String.format("%s: matches.length == %s", prefix, expectedMatches.length); + assertEquals(msg, expectedMatches.length, actualMatches.size()); + for (int k = 0; k < expectedMatches.length; k++) { + ExpectedMatch expectedMatch = expectedMatches[k]; + Match actualMatch = actualMatches.get(k); + + msg = String.format("%s: matches[%s].pattern == '%s'", prefix, k, expectedPattern); + assertEquals(msg, expectedPattern, actualMatch.pattern); + + msg = String.format("%s: matches[%s] should start at %s", prefix, k, expectedMatch.start); + assertEquals(msg, expectedMatch.start, actualMatch.i); + + msg = String.format("%s: matches[%s] should end at %s", prefix, k, expectedMatch.end); + assertEquals(msg, expectedMatch.end, actualMatch.j); + + msg = String.format("%s: matches[%s].token == '%s'", prefix, k, expectedMatch.token); + assertEquals(msg, expectedMatch.token, actualMatch.token.toString()); + + for (String fieldName : expectedMatch.fields.keySet()) { + Object expectedValue = expectedMatch.fields.get(fieldName); + Object actualValue; + try { + actualValue = Match.class.getField(fieldName).get(actualMatch); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + msg = String.format("%s: matches[%s].%s == '%s'", prefix, k, fieldName, expectedValue); + if ((expectedValue instanceof String) && (actualValue instanceof CharSequence)) { + // If comparing a CharSequence to a String, convert the CharSequence to a String first + // because String.equals doesn't consider it equal if it isn't a String itself. + assertEquals(msg, expectedValue, actualValue.toString()); + } else { + assertEquals(msg, expectedValue, actualValue); + } + } } + } - private static Map dictionary(String... words) { - Map dictionary = new HashMap<>(); - for (int i = 0; i < words.length; i++) { - String word = words[i]; - dictionary.put(word, i + 1); - } - return dictionary; + private static Map dictionary(String... words) { + Map dictionary = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + String word = words[i]; + dictionary.put(word, i + 1); + } + return dictionary; + } + + @RunWith(Parameterized.class) + public static class DictionaryMatchingTest { + private final Map> dictionaries = new HashMap<>(); + private final String password; + private final String message; + private final ExpectedMatch[] expectedMatches; + + public DictionaryMatchingTest( + String password, String message, ExpectedMatch[] expectedMatches) { + this.password = password; + this.message = message; + this.expectedMatches = expectedMatches; } - @RunWith(Parameterized.class) - public static class DictionaryMatchingTest { - private final Map> dictionaries = new HashMap<>(); - private final String password; - private final String message; - private final ExpectedMatch[] expectedMatches; - - public DictionaryMatchingTest(String password, String message, ExpectedMatch[] expectedMatches) { - this.password = password; - this.message = message; - this.expectedMatches = expectedMatches; - } - - @Before - public void setUp() throws Exception { - dictionaries.put("d1", dictionary( - "motherboard", - "mother", - "board", - "abcd", - "cdef" - )); - dictionaries.put("d2", dictionary( - "z", - "8", - "99", - "$", - "asdf1234&*" - )); - } + @Before + public void setUp() throws Exception { + dictionaries.put("d1", dictionary("motherboard", "mother", "board", "abcd", "cdef")); + dictionaries.put("d2", dictionary("z", "8", "99", "$", "asdf1234&*")); + } - @Test - public void testDictionaryMatching() throws Exception { - Context context = StandardContext.build(); - List actualMatches = new DictionaryMatcher(context, dictionaries).execute(password); - assertMatches(message, Pattern.Dictionary, expectedMatches, actualMatches); - } + @Test + public void testDictionaryMatching() throws Exception { + Context context = StandardContext.build(); + List actualMatches = new DictionaryMatcher(context, dictionaries).execute(password); + assertMatches(message, Pattern.Dictionary, expectedMatches, actualMatches); + } - @Parameterized.Parameters(name = "\"{0}\": {1}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {"motherboard", "matches words that contain other words", new ExpectedMatch[]{ - new ExpectedMatch("mother", 0, 5).matchedWord("mother").dictionaryName("d1").rank(2), - new ExpectedMatch("motherboard", 0, 10).matchedWord("motherboard").dictionaryName("d1").rank(1), - new ExpectedMatch("board", 6, 10).matchedWord("board").dictionaryName("d1").rank(3) - }}, - {"abcdef", "matches multiple words when they overlap", new ExpectedMatch[]{ - new ExpectedMatch("abcd", 0, 3).matchedWord("abcd").dictionaryName("d1").rank(4), - new ExpectedMatch("cdef", 2, 5).matchedWord("cdef").dictionaryName("d1").rank(5) - }}, - {"BoaRdZ", "ignores uppercasing", new ExpectedMatch[]{ - new ExpectedMatch("BoaRd", 0, 4).matchedWord("board").dictionaryName("d1").rank(3), - new ExpectedMatch("Z", 5, 5).matchedWord("z").dictionaryName("d2").rank(1) - }} - }); - } + @Parameterized.Parameters(name = "\"{0}\": {1}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + { + "motherboard", + "matches words that contain other words", + new ExpectedMatch[] { + new ExpectedMatch("mother", 0, 5) + .matchedWord("mother") + .dictionaryName("d1") + .rank(2), + new ExpectedMatch("motherboard", 0, 10) + .matchedWord("motherboard") + .dictionaryName("d1") + .rank(1), + new ExpectedMatch("board", 6, 10).matchedWord("board").dictionaryName("d1").rank(3) + } + }, + { + "abcdef", + "matches multiple words when they overlap", + new ExpectedMatch[] { + new ExpectedMatch("abcd", 0, 3).matchedWord("abcd").dictionaryName("d1").rank(4), + new ExpectedMatch("cdef", 2, 5).matchedWord("cdef").dictionaryName("d1").rank(5) + } + }, + { + "BoaRdZ", + "ignores uppercasing", + new ExpectedMatch[] { + new ExpectedMatch("BoaRd", 0, 4).matchedWord("board").dictionaryName("d1").rank(3), + new ExpectedMatch("Z", 5, 5).matchedWord("z").dictionaryName("d2").rank(1) + } + } + }); } + } - @RunWith(Parameterized.class) - public static class L33tMatchingTest { - private final Map> testTable = new HashMap>() {{ + @RunWith(Parameterized.class) + public static class L33tMatchingTest { + private final Map> testTable = + new HashMap>() { + { put('a', Arrays.asList('4', '@')); put('c', Arrays.asList('(', '{', '[', '<')); put('g', Arrays.asList('6', '9')); put('o', Arrays.asList('0')); - }}; + } + }; - private final String password; - private final Map expected; + private final String password; + private final Map expected; - public L33tMatchingTest(String password, Map expected) { - this.password = password; - this.expected = expected; - } - - @Test - public void testL33tMatching() throws Exception { - Context context = StandardContext.build(); - String msg = "reduces l33t table to only the substitutions that a password might be employing"; - assertEquals(msg, expected.size(), new L33tMatcher(context, Collections.>emptyMap()).relevantL33tSubTable(password, testTable).size()); - } - - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {"", new HashMap()}, - {"abcdefgo123578!#$&*)]}>", new HashMap()}, - {"a", new HashMap()}, - {"4", new HashMap() {{ - put('a', new Character[]{'4'}); - }}}, - {"4@", new HashMap() {{ - put('a', new Character[]{'4', '@'}); - }}}, - {"4({60", new HashMap() {{ - put('a', new Character[]{'4'}); - put('c', new Character[]{'(', '{'}); - put('g', new Character[]{'6'}); - put('o', new Character[]{'0'}); - }}} - }); - } + public L33tMatchingTest(String password, Map expected) { + this.password = password; + this.expected = expected; } - @RunWith(Parameterized.class) - public static class SpatialMatchingSimpleTest { - final String password; - - public SpatialMatchingSimpleTest(String password) { - this.password = password; - } - - @Test - public void testSpatialMatching() throws Exception { - Context context = StandardContext.build(); - String msg = "doesn't match 1- and 2-character spatial patterns"; - assertEquals(msg, 0, new SpatialMatcher(context).execute(password).size()); - } - - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {""}, - {"/"}, - {"qw"}, - {"*/"} - }); - } + @Test + public void testL33tMatching() throws Exception { + Context context = StandardContext.build(); + String msg = + "reduces l33t table to only the substitutions that a password might be employing"; + assertEquals( + msg, + expected.size(), + new L33tMatcher(context, Collections.>emptyMap()) + .relevantL33tSubTable(password, testTable) + .size()); } - @RunWith(Parameterized.class) - public static class SpatialMatchingTest { - final String token; - final Keyboard keyboard; - final int turns; - final int shifts; - - public SpatialMatchingTest(String token, Keyboard keyboard, int turns, int shifts) { - this.token = token; - this.keyboard = keyboard; - this.turns = turns; - this.shifts = shifts; - } + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + {"", new HashMap()}, + {"abcdefgo123578!#$&*)]}>", new HashMap()}, + {"a", new HashMap()}, + { + "4", + new HashMap() { + { + put('a', new Character[] {'4'}); + } + } + }, + { + "4@", + new HashMap() { + { + put('a', new Character[] {'4', '@'}); + } + } + }, + { + "4({60", + new HashMap() { + { + put('a', new Character[] {'4'}); + put('c', new Character[] {'(', '{'}); + put('g', new Character[] {'6'}); + put('o', new Character[] {'0'}); + } + } + } + }); + } + } - @Test - public void testSpatialMatching() throws Exception { - Context context = StandardContext.build(); - List actualMatches = new SpatialMatcher(context, Collections.singletonMap(keyboard.getName(), keyboard)).execute(token); - String msg = String.format("matches %s as a %s token", token, keyboard); - assertMatches(msg, Pattern.Spatial, new ExpectedMatch[]{ - new ExpectedMatch(token).graph(keyboard.getName()).turns(turns).shiftedCount(shifts)}, - actualMatches); - } + @RunWith(Parameterized.class) + public static class SpatialMatchingSimpleTest { + final String password; - @Parameterized.Parameters(name = "{0}") - public static Collection data() throws IOException { - final Keyboard qwerty = StandardKeyboards.QWERTY_LOADER.load(); - final Keyboard keypad = StandardKeyboards.KEYPAD_LOADER.load(); - final Keyboard macKeypad = StandardKeyboards.MAC_KEYPAD_LOADER.load(); - final Keyboard dvorak = StandardKeyboards.DVORAK_LOADER.load(); - return Arrays.asList(new Object[][]{ - {"12345", qwerty, 1, 0}, - {"@WSX", qwerty, 1, 4}, - {"6tfGHJ", qwerty, 2, 3}, - {"hGFd", qwerty, 1, 2}, - {"/;p09876yhn", qwerty, 3, 0}, - {"Xdr%", qwerty, 1, 2}, - {"159-", keypad, 1, 0}, - {"*84", keypad, 1, 0}, - {"/8520", keypad, 1, 0}, - {"369", keypad, 1, 0}, - {"/963.", macKeypad, 1, 0}, - {"*-632.0214", macKeypad, 9, 0}, - {"aoEP%yIxkjq:", dvorak, 4, 5}, - {";qoaOQ:Aoq;a", dvorak, 11, 4} - }); - } + public SpatialMatchingSimpleTest(String password) { + this.password = password; } - @RunWith(Parameterized.class) - public static class SequenceMatchingSimpleTest { - private final String password; - - public SequenceMatchingSimpleTest(String password) { - this.password = password; - } + @Test + public void testSpatialMatching() throws Exception { + Context context = StandardContext.build(); + String msg = "doesn't match 1- and 2-character spatial patterns"; + assertEquals(msg, 0, new SpatialMatcher(context).execute(password).size()); + } - @Test - public void testSequenceMatching() throws Exception { - Context context = StandardContext.build(); - String msg = String.format("doesn't match length-%s sequences", password.length()); - assertEquals(msg, new SequenceMatcher(context).execute(password).size(), 0); - } + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList(new Object[][] {{""}, {"/"}, {"qw"}, {"*/"}}); + } + } + + @RunWith(Parameterized.class) + public static class SpatialMatchingTest { + final String token; + final Keyboard keyboard; + final int turns; + final int shifts; + + public SpatialMatchingTest(String token, Keyboard keyboard, int turns, int shifts) { + this.token = token; + this.keyboard = keyboard; + this.turns = turns; + this.shifts = shifts; + } - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {""}, - {"a"}, - {"1"} - }); - } + @Test + public void testSpatialMatching() throws Exception { + Context context = StandardContext.build(); + List actualMatches = + new SpatialMatcher(context, Collections.singletonMap(keyboard.getName(), keyboard)) + .execute(token); + String msg = String.format("matches %s as a %s token", token, keyboard); + assertMatches( + msg, + Pattern.Spatial, + new ExpectedMatch[] { + new ExpectedMatch(token).graph(keyboard.getName()).turns(turns).shiftedCount(shifts) + }, + actualMatches); } - @RunWith(Parameterized.class) - public static class RepeatMatchingSimpleTest { - private final String password; - private final int size; + @Parameterized.Parameters(name = "{0}") + public static Collection data() throws IOException { + final Keyboard qwerty = StandardKeyboards.QWERTY_LOADER.load(); + final Keyboard keypad = StandardKeyboards.KEYPAD_LOADER.load(); + final Keyboard macKeypad = StandardKeyboards.MAC_KEYPAD_LOADER.load(); + final Keyboard dvorak = StandardKeyboards.DVORAK_LOADER.load(); + return Arrays.asList( + new Object[][] { + {"12345", qwerty, 1, 0}, + {"@WSX", qwerty, 1, 4}, + {"6tfGHJ", qwerty, 2, 3}, + {"hGFd", qwerty, 1, 2}, + {"/;p09876yhn", qwerty, 3, 0}, + {"Xdr%", qwerty, 1, 2}, + {"159-", keypad, 1, 0}, + {"*84", keypad, 1, 0}, + {"/8520", keypad, 1, 0}, + {"369", keypad, 1, 0}, + {"/963.", macKeypad, 1, 0}, + {"*-632.0214", macKeypad, 9, 0}, + {"aoEP%yIxkjq:", dvorak, 4, 5}, + {";qoaOQ:Aoq;a", dvorak, 11, 4} + }); + } + } - public RepeatMatchingSimpleTest(String password, int size) { - this.password = password; - this.size = size; - } + @RunWith(Parameterized.class) + public static class SequenceMatchingSimpleTest { + private final String password; - @Test - public void testRepeatMatchingSimple() throws Exception { - Context context = StandardContext.build(); - String msg = String.format("doesn't match length-%s repeat patterns", password.length()); - assertEquals(msg, new RepeatMatcher(context).execute(password).size(), size); - } + public SequenceMatchingSimpleTest(String password) { + this.password = password; + } - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {"", 0}, - {"#", 0}, - {"abababababbbbbbbbbbbbb", 2}, - {"abababababbbbbbbbbbbb", 2}, - {"abababababbbbbbbbbbb", 2} + @Test + public void testSequenceMatching() throws Exception { + Context context = StandardContext.build(); + String msg = String.format("doesn't match length-%s sequences", password.length()); + assertEquals(msg, new SequenceMatcher(context).execute(password).size(), 0); + } - }); - } + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList(new Object[][] {{""}, {"a"}, {"1"}}); } + } - @RunWith(Parameterized.class) - public static class RepeatMatchingTest { - private final String password; - private final ExpectedMatch expectedMatch; + @RunWith(Parameterized.class) + public static class RepeatMatchingSimpleTest { + private final String password; + private final int size; - public RepeatMatchingTest(String password, ExpectedMatch expectedMatch) { - this.password = password; - this.expectedMatch = expectedMatch; - } + public RepeatMatchingSimpleTest(String password, int size) { + this.password = password; + this.size = size; + } - @Test - public void testRepeatMatching() throws Exception { - Context context = StandardContext.build(); - List actualMatches = new RepeatMatcher(context).execute(password); - assertMatches("matches embedded repeat patterns", Pattern.Repeat, new ExpectedMatch[]{expectedMatch}, actualMatches); - } + @Test + public void testRepeatMatchingSimple() throws Exception { + Context context = StandardContext.build(); + String msg = String.format("doesn't match length-%s repeat patterns", password.length()); + assertEquals(msg, new RepeatMatcher(context).execute(password).size(), size); + } - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - final String pattern = "&&&&&"; - - List prefixes = Arrays.asList("@", "y4@"); - List suffices = Arrays.asList("u", "u%7"); - List result = new ArrayList<>(); - for (String prefix : prefixes) { - for (String suffix : suffices) { - int i = prefix.length(); - int j = prefix.length() + pattern.length() - 1; - String password = prefix + pattern + suffix; - result.add(new Object[]{password, new ExpectedMatch(pattern, i, j).baseToken("&")}); - } - } - return result; - } + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + {"", 0}, + {"#", 0}, + {"abababababbbbbbbbbbbbb", 2}, + {"abababababbbbbbbbbbbb", 2}, + {"abababababbbbbbbbbbb", 2} + }); } + } - @RunWith(Parameterized.class) - public static class DateMatchingTest { - private final String password; - private final String message; - private final ExpectedMatch expectedMatch; + @RunWith(Parameterized.class) + public static class RepeatMatchingTest { + private final String password; + private final ExpectedMatch expectedMatch; - public DateMatchingTest(String password, String message, ExpectedMatch expectedMatch) { - this.password = password; - this.message = message; - this.expectedMatch = expectedMatch; - } + public RepeatMatchingTest(String password, ExpectedMatch expectedMatch) { + this.password = password; + this.expectedMatch = expectedMatch; + } - @Test - public void testDateMatching() throws Exception { - Context context = StandardContext.build(); - List actualMatches = new DateMatcher(context).execute(password); - assertMatches(message, Pattern.Date, new ExpectedMatch[]{expectedMatch}, actualMatches); - } + @Test + public void testRepeatMatching() throws Exception { + Context context = StandardContext.build(); + List actualMatches = new RepeatMatcher(context).execute(password); + assertMatches( + "matches embedded repeat patterns", + Pattern.Repeat, + new ExpectedMatch[] {expectedMatch}, + actualMatches); + } - @Parameterized.Parameters(name = "{1}") - public static Collection data() { - List data = new ArrayList<>(); - - for (final String separator : new String[]{"", " ", "-", "/", "\\", "_", "."}) { - final String password = String.format("13%s2%s1921", separator, separator); - data.add(new Object[]{ - password, - String.format("matches dates that use '%s' as a separator", separator), - new ExpectedMatch(password).separator(separator).year(1921).month(2).day(13) - }); - } + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + final String pattern = "&&&&&"; + + List prefixes = Arrays.asList("@", "y4@"); + List suffices = Arrays.asList("u", "u%7"); + List result = new ArrayList<>(); + for (String prefix : prefixes) { + for (String suffix : suffices) { + int i = prefix.length(); + int j = prefix.length() + pattern.length() - 1; + String password = prefix + pattern + suffix; + result.add(new Object[] {password, new ExpectedMatch(pattern, i, j).baseToken("&")}); + } + } + return result; + } + } + + @RunWith(Parameterized.class) + public static class DateMatchingTest { + private final String password; + private final String message; + private final ExpectedMatch expectedMatch; + + public DateMatchingTest(String password, String message, ExpectedMatch expectedMatch) { + this.password = password; + this.message = message; + this.expectedMatch = expectedMatch; + } - for (final String order : new String[]{"mdy", "dmy", "ymd", "ydm"}) { - final String password = order - .replace("y", "88") - .replace("m", "8") - .replace("d", "8"); - data.add(new Object[]{ - password, - String.format("matches dates with '%s' format", order), - new ExpectedMatch(password).separator("").year(1988).month(8).day(8) - }); - } + @Test + public void testDateMatching() throws Exception { + Context context = StandardContext.build(); + List actualMatches = new DateMatcher(context).execute(password); + assertMatches(message, Pattern.Date, new ExpectedMatch[] {expectedMatch}, actualMatches); + } - data.add(new Object[]{ - "111504", - "matches the date with year closest to REFERENCE_YEAR when ambiguous", - new ExpectedMatch("111504").separator("").year(2004).month(11).day(15) + @Parameterized.Parameters(name = "{1}") + public static Collection data() { + List data = new ArrayList<>(); + + for (final String separator : new String[] {"", " ", "-", "/", "\\", "_", "."}) { + final String password = String.format("13%s2%s1921", separator, separator); + data.add( + new Object[] { + password, + String.format("matches dates that use '%s' as a separator", separator), + new ExpectedMatch(password).separator(separator).year(1921).month(2).day(13) + }); + } + + for (final String order : new String[] {"mdy", "dmy", "ymd", "ydm"}) { + final String password = order.replace("y", "88").replace("m", "8").replace("d", "8"); + data.add( + new Object[] { + password, + String.format("matches dates with '%s' format", order), + new ExpectedMatch(password).separator("").year(1988).month(8).day(8) }); + } - return data; - } - } + data.add( + new Object[] { + "111504", + "matches the date with year closest to REFERENCE_YEAR when ambiguous", + new ExpectedMatch("111504").separator("").year(2004).month(11).day(15) + }); - public static class RestMatchingTest { - - @Test - public void testReverseDictionaryMatching() throws Exception { - Context context = StandardContext.build(); - ReverseDictionaryMatcher reverseDictionaryMatcher = new ReverseDictionaryMatcher(context, new HashMap>() {{ - put("d1", dictionary( - "123", - "321", - "456", - "654" - )); - }}); - - String password = "0123456789"; - List actualMatches = reverseDictionaryMatcher.execute(password); - - ExpectedMatch[] expectedMatches = new ExpectedMatch[]{ - new ExpectedMatch("123", 1, 3).matchedWord("321").reversed(true).dictionaryName("d1").rank(2), - new ExpectedMatch("456", 4, 6).matchedWord("654").reversed(true).dictionaryName("d1").rank(4) - }; - assertMatches("matches against reversed words", Pattern.Dictionary, expectedMatches, actualMatches); - } + return data; + } + } + + public static class RestMatchingTest { + + @Test + public void testReverseDictionaryMatching() throws Exception { + Context context = StandardContext.build(); + ReverseDictionaryMatcher reverseDictionaryMatcher = + new ReverseDictionaryMatcher( + context, + new HashMap>() { + { + put("d1", dictionary("123", "321", "456", "654")); + } + }); + + String password = "0123456789"; + List actualMatches = reverseDictionaryMatcher.execute(password); + + ExpectedMatch[] expectedMatches = + new ExpectedMatch[] { + new ExpectedMatch("123", 1, 3) + .matchedWord("321") + .reversed(true) + .dictionaryName("d1") + .rank(2), + new ExpectedMatch("456", 4, 6) + .matchedWord("654") + .reversed(true) + .dictionaryName("d1") + .rank(4) + }; + assertMatches( + "matches against reversed words", Pattern.Dictionary, expectedMatches, actualMatches); + } - @Test - public void testSpatialMatching() throws Exception { - Context context = StandardContext.build(); - final Keyboard keyboard = StandardKeyboards.QWERTY_LOADER.load(); - final String token = "6tfGHJ"; - List actualMatches = new SpatialMatcher(context, Collections.singletonMap(keyboard.getName(), keyboard)) - .execute("rz!" + token + "%z"); - String msg = "matches against spatial patterns surrounded by non-spatial patterns"; - ExpectedMatch[] expectedMatches = new ExpectedMatch[]{ - new ExpectedMatch(token, 3, 3 + token.length() - 1).graph(keyboard.getName()).turns(2).shiftedCount(3) - }; - assertMatches(msg, Pattern.Spatial, expectedMatches, actualMatches); - } + @Test + public void testSpatialMatching() throws Exception { + Context context = StandardContext.build(); + final Keyboard keyboard = StandardKeyboards.QWERTY_LOADER.load(); + final String token = "6tfGHJ"; + List actualMatches = + new SpatialMatcher(context, Collections.singletonMap(keyboard.getName(), keyboard)) + .execute("rz!" + token + "%z"); + String msg = "matches against spatial patterns surrounded by non-spatial patterns"; + ExpectedMatch[] expectedMatches = + new ExpectedMatch[] { + new ExpectedMatch(token, 3, 3 + token.length() - 1) + .graph(keyboard.getName()) + .turns(2) + .shiftedCount(3) + }; + assertMatches(msg, Pattern.Spatial, expectedMatches, actualMatches); + } - @Test - public void testSequenceMatching() throws Exception { - Context context = StandardContext.build(); - List actualMatches = new SequenceMatcher(context).execute("abcbabc"); - ExpectedMatch[] expectedMatches = new ExpectedMatch[]{ - new ExpectedMatch("abc", 0, 2).ascending(true), - new ExpectedMatch("cba", 2, 4).ascending(false), - new ExpectedMatch("abc", 4, 6).ascending(true) - }; - assertMatches("matches overlapping patterns", Pattern.Sequence, expectedMatches, actualMatches); - } + @Test + public void testSequenceMatching() throws Exception { + Context context = StandardContext.build(); + List actualMatches = new SequenceMatcher(context).execute("abcbabc"); + ExpectedMatch[] expectedMatches = + new ExpectedMatch[] { + new ExpectedMatch("abc", 0, 2).ascending(true), + new ExpectedMatch("cba", 2, 4).ascending(false), + new ExpectedMatch("abc", 4, 6).ascending(true) + }; + assertMatches( + "matches overlapping patterns", Pattern.Sequence, expectedMatches, actualMatches); + } - @Test - public void testRegexMatchingPastYear() throws Exception { - testRegexMatching("1922"); - } + @Test + public void testRegexMatchingPastYear() throws Exception { + testRegexMatching("1922"); + } - @Test - public void testRegexMatchingFutureYear() throws Exception { - testRegexMatching("2017"); - } + @Test + public void testRegexMatchingFutureYear() throws Exception { + testRegexMatching("2017"); + } - private void testRegexMatching(String year) throws Exception { - Context context = StandardContext.build(); - List actualMatches = new RegexMatcher(context).execute(year); - assertMatches( - "matches " + year + " as a recent_year token", - Pattern.Regex, - new ExpectedMatch[]{new ExpectedMatch(year).regexName("recent_year")}, - actualMatches); - } + private void testRegexMatching(String year) throws Exception { + Context context = StandardContext.build(); + List actualMatches = new RegexMatcher(context).execute(year); + assertMatches( + "matches " + year + " as a recent_year token", + Pattern.Regex, + new ExpectedMatch[] {new ExpectedMatch(year).regexName("recent_year")}, + actualMatches); + } - @Test - public void testOmnimatch() throws Exception { - Context context = StandardContext.build(); - assertEquals(0, new Matching(context, new ArrayList()).omnimatch("").size()); - String password = "r0sebudmaelstrom11/20/91aaaa"; - List matches = new Matching(context, new ArrayList()).omnimatch(password); - Map testMatches = new HashMap<>(); - //testMatches.put(Pattern.Dictionary, new Integer[]{0, 6}); - testMatches.put(Pattern.Dictionary, new Integer[]{7, 15}); - testMatches.put(Pattern.Date, new Integer[]{16, 23}); - testMatches.put(Pattern.Repeat, new Integer[]{24, 27}); - for (Map.Entry testMatch : testMatches.entrySet()) { - Pattern patternName = testMatch.getKey(); - int i = testMatch.getValue()[0]; - int j = testMatch.getValue()[1]; - boolean included = false; - for (Match match : matches) { - if (match.i == i && match.j == j && match.pattern == patternName) { - included = true; - break; - } - } - String msg = String.format("for %s, matches a %s token at [%s, %s]", password, patternName.value(), i, j); - assertTrue(msg, included); - } - } + @Test + public void testOmnimatch() throws Exception { + Context context = StandardContext.build(); + assertEquals(0, new Matching(context, new ArrayList()).omnimatch("").size()); + String password = "r0sebudmaelstrom11/20/91aaaa"; + List matches = new Matching(context, new ArrayList()).omnimatch(password); + Map testMatches = new HashMap<>(); + // testMatches.put(Pattern.Dictionary, new Integer[]{0, 6}); + testMatches.put(Pattern.Dictionary, new Integer[] {7, 15}); + testMatches.put(Pattern.Date, new Integer[] {16, 23}); + testMatches.put(Pattern.Repeat, new Integer[] {24, 27}); + for (Map.Entry testMatch : testMatches.entrySet()) { + Pattern patternName = testMatch.getKey(); + int i = testMatch.getValue()[0]; + int j = testMatch.getValue()[1]; + boolean included = false; + for (Match match : matches) { + if (match.i == i && match.j == j && match.pattern == patternName) { + included = true; + break; + } + } + String msg = + String.format( + "for %s, matches a %s token at [%s, %s]", password, patternName.value(), i, j); + assertTrue(msg, included); + } } + } - private static class ExpectedMatch { - String token; - int start; - int end; - Map fields = new HashMap<>(); + private static class ExpectedMatch { + String token; + int start; + int end; + Map fields = new HashMap<>(); - public ExpectedMatch(String token) { - this(token, 0, token.length() - 1); - } + public ExpectedMatch(String token) { + this(token, 0, token.length() - 1); + } - public ExpectedMatch(String token, int start, int end) { - this.token = token; - this.start = start; - this.end = end; - } + public ExpectedMatch(String token, int start, int end) { + this.token = token; + this.start = start; + this.end = end; + } - public ExpectedMatch matchedWord(String matchedWord) { - fields.put("matchedWord", matchedWord); - return this; - } + public ExpectedMatch matchedWord(String matchedWord) { + fields.put("matchedWord", matchedWord); + return this; + } - public ExpectedMatch dictionaryName(String dictionaryName) { - fields.put("dictionaryName", dictionaryName); - return this; - } + public ExpectedMatch dictionaryName(String dictionaryName) { + fields.put("dictionaryName", dictionaryName); + return this; + } - public ExpectedMatch rank(int rank) { - fields.put("rank", rank); - return this; - } + public ExpectedMatch rank(int rank) { + fields.put("rank", rank); + return this; + } - public ExpectedMatch reversed(boolean reversed) { - fields.put("reversed", reversed); - return this; - } + public ExpectedMatch reversed(boolean reversed) { + fields.put("reversed", reversed); + return this; + } - public ExpectedMatch graph(String graph) { - fields.put("graph", graph); - return this; - } + public ExpectedMatch graph(String graph) { + fields.put("graph", graph); + return this; + } - public ExpectedMatch turns(int turns) { - fields.put("turns", turns); - return this; - } + public ExpectedMatch turns(int turns) { + fields.put("turns", turns); + return this; + } - public ExpectedMatch shiftedCount(int shiftedCount) { - fields.put("shiftedCount", shiftedCount); - return this; - } + public ExpectedMatch shiftedCount(int shiftedCount) { + fields.put("shiftedCount", shiftedCount); + return this; + } - public ExpectedMatch ascending(boolean ascending) { - fields.put("ascending", ascending); - return this; - } + public ExpectedMatch ascending(boolean ascending) { + fields.put("ascending", ascending); + return this; + } - public ExpectedMatch baseToken(String baseToken) { - fields.put("baseToken", baseToken); - return this; - } + public ExpectedMatch baseToken(String baseToken) { + fields.put("baseToken", baseToken); + return this; + } - public ExpectedMatch regexName(String regexName) { - fields.put("regexName", regexName); - return this; - } + public ExpectedMatch regexName(String regexName) { + fields.put("regexName", regexName); + return this; + } - public ExpectedMatch separator(String separator) { - fields.put("separator", separator); - return this; - } + public ExpectedMatch separator(String separator) { + fields.put("separator", separator); + return this; + } - public ExpectedMatch year(int year) { - fields.put("year", year); - return this; - } + public ExpectedMatch year(int year) { + fields.put("year", year); + return this; + } - public ExpectedMatch month(int month) { - fields.put("month", month); - return this; - } + public ExpectedMatch month(int month) { + fields.put("month", month); + return this; + } - public ExpectedMatch day(int day) { - fields.put("day", day); - return this; - } + public ExpectedMatch day(int day) { + fields.put("day", day); + return this; } + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/MeasureTest.java b/src/test/java/com/nulabinc/zxcvbn/MeasureTest.java index 0e46105..69c2f58 100644 --- a/src/test/java/com/nulabinc/zxcvbn/MeasureTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/MeasureTest.java @@ -1,62 +1,67 @@ package com.nulabinc.zxcvbn; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.util.Arrays; - -import static org.junit.Assert.assertEquals; - @RunWith(Parameterized.class) public class MeasureTest { - private String password; + private String password; - public MeasureTest(String password) { - this.password = password; - } + public MeasureTest(String password) { + this.password = password; + } - @Test - public void testMeasure() throws Exception { - Zxcvbn zxcvbn = new Zxcvbn(); - Strength strength = zxcvbn.measure(password); - assertEquals("Unexpected error. Password is " + password, password, strength.getPassword().toString()); - } + @Test + public void testMeasure() throws Exception { + Zxcvbn zxcvbn = new Zxcvbn(); + Strength strength = zxcvbn.measure(password); + assertEquals( + "Unexpected error. Password is " + password, password, strength.getPassword().toString()); + } - @Parameterized.Parameters(name = "{0}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {"qwER43@!"}, - {"Tr0ub4dour&3"}, - {"correcthorsebatterystaple"}, - {"password"}, - {"drowssap"}, - {"passwordp"}, - {"passwordadmin"}, - {"p@$$word@dmin"}, - {"19700101"}, - {"20300101"}, - {"aaaaaaaaa"}, - {"123456789"}, - {"abcdefghijklmnopqrstuvwxyz"}, - {"qwertyuiop@["}, - {"zxcvbnm,./_"}, - {"asdfghjkl;:]"}, - {"pandapandapandapandapandapandapandapandapandaa"}, - {"appleappleappleappleappleappleappleappleapplea"}, - {"dncrbliehbvkehr734yf;ewhihwfph@houaegfueqpg30^r0urfvhej¥]e;l,ckvniwbgoidnci@oewhfoobojabouhqwou12482386fhoiwehe@o"}, - {"apple orenge aabb "}, - {"eTq($%u-44c_j9NJB45a#2#JP7sH"}, - {"IB7~EOw!51gug+7s#+%A9P1O/w8f"}, - {"1v_f%7JvS8w!_t398+ON-CObI#v0"}, - {"8lFmfc0!w)&iU9DM6~4_w)D)Y44J"}, - {"&BZ09gjG!iKG&#M09s_1Gr41&o%i"}, - {"T9Y-!ciS%XW9U5l/~aw9+4!5u8Ti"}, - {"QMji&0uze5O#%+%2e_Y08E(R6L8p"}, - {"6EG4y1nJASd!1~!//#6+Yhb1vW3d"}, - {"8$q_5f2U3s6~W(S7iv)_8N%lJkOE"}, - {"%nbd~$)2y/6hV6)2R9vYPpA49A~C"}, - {"Rh&pW%EXT=/Z1lzouG.wU_+2MT+FG4sm+&jqN?L25jDtjW3EQuppfvD_30Vo3K=SX4=z3-U2gVf7A0oSM5oWegRa_sV$-GLI3LzCo&@!h@$v#OkoN#@-eS8Y&W$pGmmVXc#XHAv?n$M+_wQx1FAB_*iaZE1_9ZV.cwn-d@+90B8z0bVOKc63lV9QntW0kryN7Y#rjv@0+Bd8hc-3WW_Yn%z5/DE?R*UeiKgR#$/F8kA9I!Ib*GDa.x0T7UWCCxDV&ithebyz$=7vW6TdmlmL%WZxmA7K%*Rg1035UO%WOTIgiMs4AjpmL1"} + @Parameterized.Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { + {"qwER43@!"}, + {"Tr0ub4dour&3"}, + {"correcthorsebatterystaple"}, + {"password"}, + {"drowssap"}, + {"passwordp"}, + {"passwordadmin"}, + {"p@$$word@dmin"}, + {"19700101"}, + {"20300101"}, + {"aaaaaaaaa"}, + {"123456789"}, + {"abcdefghijklmnopqrstuvwxyz"}, + {"qwertyuiop@["}, + {"zxcvbnm,./_"}, + {"asdfghjkl;:]"}, + {"pandapandapandapandapandapandapandapandapandaa"}, + {"appleappleappleappleappleappleappleappleapplea"}, + { + "dncrbliehbvkehr734yf;ewhihwfph@houaegfueqpg30^r0urfvhej¥]e;l,ckvniwbgoidnci@oewhfoobojabouhqwou12482386fhoiwehe@o" + }, + {"apple orenge aabb "}, + {"eTq($%u-44c_j9NJB45a#2#JP7sH"}, + {"IB7~EOw!51gug+7s#+%A9P1O/w8f"}, + {"1v_f%7JvS8w!_t398+ON-CObI#v0"}, + {"8lFmfc0!w)&iU9DM6~4_w)D)Y44J"}, + {"&BZ09gjG!iKG&#M09s_1Gr41&o%i"}, + {"T9Y-!ciS%XW9U5l/~aw9+4!5u8Ti"}, + {"QMji&0uze5O#%+%2e_Y08E(R6L8p"}, + {"6EG4y1nJASd!1~!//#6+Yhb1vW3d"}, + {"8$q_5f2U3s6~W(S7iv)_8N%lJkOE"}, + {"%nbd~$)2y/6hV6)2R9vYPpA49A~C"}, + { + "Rh&pW%EXT=/Z1lzouG.wU_+2MT+FG4sm+&jqN?L25jDtjW3EQuppfvD_30Vo3K=SX4=z3-U2gVf7A0oSM5oWegRa_sV$-GLI3LzCo&@!h@$v#OkoN#@-eS8Y&W$pGmmVXc#XHAv?n$M+_wQx1FAB_*iaZE1_9ZV.cwn-d@+90B8z0bVOKc63lV9QntW0kryN7Y#rjv@0+Bd8hc-3WW_Yn%z5/DE?R*UeiKgR#$/F8kA9I!Ib*GDa.x0T7UWCCxDV&ithebyz$=7vW6TdmlmL%WZxmA7K%*Rg1035UO%WOTIgiMs4AjpmL1" + } }); - } + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/ScoringTest.java b/src/test/java/com/nulabinc/zxcvbn/ScoringTest.java index 178130b..5d5bf98 100644 --- a/src/test/java/com/nulabinc/zxcvbn/ScoringTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/ScoringTest.java @@ -1,5 +1,7 @@ package com.nulabinc.zxcvbn; +import static org.junit.Assert.assertEquals; + import com.nulabinc.zxcvbn.guesses.BaseGuess; import com.nulabinc.zxcvbn.guesses.DateGuess; import com.nulabinc.zxcvbn.guesses.DictionaryGuess; @@ -8,316 +10,392 @@ import com.nulabinc.zxcvbn.guesses.SequenceGuess; import com.nulabinc.zxcvbn.matchers.Match; import com.nulabinc.zxcvbn.matchers.MatchFactory; -import org.junit.*; -import org.junit.experimental.runners.Enclosed; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; - -import static org.junit.Assert.assertEquals; +import org.junit.*; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; @RunWith(Enclosed.class) public class ScoringTest { - @RunWith(Parameterized.class) - public static class NckTest { - private final int n; - private final int k; - private final int expected; - - public NckTest(int n, int k, int expected) { - this.n = n; - this.k = k; - this.expected = expected; - } - - @Test - public void testNck() throws Exception { - Context context = StandardContext.build(); - BaseGuess obj = new BaseGuess(context) { - @Override - public double exec(Match match) { - return 0; - } - }; - Method method = BaseGuess.class.getDeclaredMethod("nCk", int.class, int.class); - method.setAccessible(true); - - String msg = String.format("nCk(%s, %s) == %s", n, k, expected); - assertEquals(msg, expected, method.invoke(obj, n, k)); - } - - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new Object[][]{ - {0, 0, 1}, - {1, 0, 1}, - {5, 0, 1}, - {0, 1, 0}, - {0, 5, 0}, - {2, 1, 2}, - {4, 2, 6}, - {33, 7, 4272048} - }); - } + @RunWith(Parameterized.class) + public static class NckTest { + private final int n; + private final int k; + private final int expected; + + public NckTest(int n, int k, int expected) { + this.n = n; + this.k = k; + this.expected = expected; } - @RunWith(Parameterized.class) - public static class RepeatGuessesTest { - private final String token; - private final String baseToken; - private final int repeatCount; - - public RepeatGuessesTest(String token, String baseToken, int repeatCount) { - this.token = token; - this.baseToken = baseToken; - this.repeatCount = repeatCount; - } - - @Test - public void testRepeatGuesses() throws Exception { - Context context = StandardContext.build(); - Scoring scoring = new Scoring(context); - double baseGuesses = scoring.mostGuessableMatchSequence( - baseToken, new Matching(context, Collections.emptyList()).omnimatch(baseToken)).getGuesses(); - Match match = new Match.Builder(Pattern.Repeat, 0, 0, token) - .baseToken(baseToken) - .baseGuesses(baseGuesses) - .repeatCount(repeatCount) - .build(); - double expectedGuesses = baseGuesses * repeatCount; - String msg = String.format("the repeat pattern '%s' has guesses of %s", token, expectedGuesses); - assertEquals(msg, expectedGuesses, new RepeatGuess(context).exec(match), 0.0); - } - - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {"aa", "a", 2}, - {"999", "9", 3}, - {"$$$$", "$", 4}, - {"abab", "ab", 2}, - {"batterystaplebatterystaplebatterystaple", "batterystaple", 3} - }); - } + @Test + public void testNck() throws Exception { + Context context = StandardContext.build(); + BaseGuess obj = + new BaseGuess(context) { + @Override + public double exec(Match match) { + return 0; + } + }; + Method method = BaseGuess.class.getDeclaredMethod("nCk", int.class, int.class); + method.setAccessible(true); + + String msg = String.format("nCk(%s, %s) == %s", n, k, expected); + assertEquals(msg, expected, method.invoke(obj, n, k)); } - @RunWith(Parameterized.class) - public static class SequenceGuessesTest { - private final String token; - private final boolean ascending; - private final int expectedGuesses; - - public SequenceGuessesTest(String token, boolean ascending, int expectedGuesses) { - this.token = token; - this.ascending = ascending; - this.expectedGuesses = expectedGuesses; - } - - @Test - public void testSequenceGuesses() throws Exception { - Context context = StandardContext.build(); - Match match = new Match.Builder(Pattern.Sequence, 0, 0, token).ascending(ascending).build(); - String msg = String.format("the sequence pattern '%s' has guesses of %s", token, expectedGuesses); - assertEquals(msg, expectedGuesses, new SequenceGuess(context).exec(match), 0.0); - } - - @Parameterized.Parameters(name = "{0}") - public static Collection data() { - return Arrays.asList(new Object[][]{ - {"ab", true, 4 * 2}, - {"XYZ", true, 26 * 3}, - {"4567", true, 10 * 4}, - {"7654", false, 10 * 4 * 2}, - {"ZYX", false, 4 * 3 * 2} - }); - } + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[][] { + {0, 0, 1}, + {1, 0, 1}, + {5, 0, 1}, + {0, 1, 0}, + {0, 5, 0}, + {2, 1, 2}, + {4, 2, 6}, + {33, 7, 4272048} + }); + } + } + + @RunWith(Parameterized.class) + public static class RepeatGuessesTest { + private final String token; + private final String baseToken; + private final int repeatCount; + + public RepeatGuessesTest(String token, String baseToken, int repeatCount) { + this.token = token; + this.baseToken = baseToken; + this.repeatCount = repeatCount; } - public static class DictionaryGuessesTest { - - @Test - public void testDictionaryGuessesSameWithRank() throws Exception { - Context context = StandardContext.build(); - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "aaaa").rank(32).build(); - String msg = "base guesses == the rank"; - assertEquals(msg, 32, new DictionaryGuess(context).exec(match), 0.0); - } - - @Test - public void testDictionaryGuessesCapitalization() throws Exception { - Context context = StandardContext.build(); - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "AAAaaa").rank(32).build(); - String msg = "extra guesses are added for capitalization"; - assertEquals(msg, 32 * new DictionaryGuess(context).uppercaseVariations(match), new DictionaryGuess(context).exec(match), 0.0); - } - - @Test - public void testDictionaryGuessesReverse() throws Exception { - Context context = StandardContext.build(); - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "aaa").reversed(true).rank(32).build(); - String msg = "guesses are doubled when word is reversed"; - assertEquals(msg, 32 * 2, new DictionaryGuess(context).exec(match), 0.0); - } - - @Test - public void testDictionaryGuesses133t() throws Exception { - Context context = StandardContext.build(); - Map sub = new HashMap<>(); - sub.put('@', 'a'); - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "aaa@@@").sub(sub).l33t(true).rank(32).build(); - String msg = "extra guesses are added for common l33t substitutions"; - assertEquals(msg, 32 * new DictionaryGuess(context).l33tVariations(match), new DictionaryGuess(context).exec(match), 0.0); - } - - @Test - public void testDictionaryGuessesMixed() throws Exception { - Context context = StandardContext.build(); - Map sub = new HashMap<>(); - sub.put('@', 'a'); - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "AaA@@@").sub(sub).l33t(true).rank(32).build(); - String msg = "extra guesses are added for both capitalization and common l33t substitutions"; - int expected = 32 * new DictionaryGuess(context).l33tVariations(match) * new DictionaryGuess(context).uppercaseVariations(match); - assertEquals(msg, expected, new DictionaryGuess(context).exec(match), 0.0); - } + @Test + public void testRepeatGuesses() throws Exception { + Context context = StandardContext.build(); + Scoring scoring = new Scoring(context); + double baseGuesses = + scoring + .mostGuessableMatchSequence( + baseToken, + new Matching(context, Collections.emptyList()).omnimatch(baseToken)) + .getGuesses(); + Match match = + new Match.Builder(Pattern.Repeat, 0, 0, token) + .baseToken(baseToken) + .baseGuesses(baseGuesses) + .repeatCount(repeatCount) + .build(); + double expectedGuesses = baseGuesses * repeatCount; + String msg = + String.format("the repeat pattern '%s' has guesses of %s", token, expectedGuesses); + assertEquals(msg, expectedGuesses, new RepeatGuess(context).exec(match), 0.0); + } + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + {"aa", "a", 2}, + {"999", "9", 3}, + {"$$$$", "$", 4}, + {"abab", "ab", 2}, + {"batterystaplebatterystaplebatterystaple", "batterystaple", 3} + }); + } + } + + @RunWith(Parameterized.class) + public static class SequenceGuessesTest { + private final String token; + private final boolean ascending; + private final int expectedGuesses; + + public SequenceGuessesTest(String token, boolean ascending, int expectedGuesses) { + this.token = token; + this.ascending = ascending; + this.expectedGuesses = expectedGuesses; } - @RunWith(Parameterized.class) - public static class UppercaseVariantsTest { - private final String word; - private final int variants; - - public UppercaseVariantsTest(String word, int variants) { - this.word = word; - this.variants = variants; - } - - @Test - public void testUppercaseVariants() throws Exception { - Context context = StandardContext.build(); - DictionaryGuess dictionaryGuess = new DictionaryGuess(context); - Method uppercaseVariationsMethod = DictionaryGuess.class.getDeclaredMethod("uppercaseVariations", Match.class); - uppercaseVariationsMethod.setAccessible(true); - - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, word).sub(new HashMap()).l33t(true).build(); - String msg = String.format("guess multiplier of %s is %s", word, variants); - assertEquals(msg, variants, uppercaseVariationsMethod.invoke(dictionaryGuess, match)); - } - - @Parameterized.Parameters(name = "{0}") - public static Collection data() throws Exception { - Context context = StandardContext.build(); - BaseGuess baseGuess = new BaseGuess(context) { - @Override - public double exec(Match match) { - return 0; - } - }; - Method method = BaseGuess.class.getDeclaredMethod("nCk", int.class, int.class); - method.setAccessible(true); - return Arrays.asList(new Object[][]{ - {"", 1}, - {"a", 1}, - {"A", 2}, - {"abcdef", 1}, - {"Abcdef", 2}, - {"abcdeF", 2}, - {"ABCDEF", 2}, - {"aBcdef", method.invoke(baseGuess, 6, 1)}, - {"aBcDef", (int) method.invoke(baseGuess, 6, 1) + (int) method.invoke(baseGuess, 6, 2)}, - {"ABCDEf", method.invoke(baseGuess, 6, 1)}, - {"aBCDEf", (int) method.invoke(baseGuess, 6, 1) + (int) method.invoke(baseGuess, 6, 2)}, - {"ABCdef", (int) method.invoke(baseGuess, 6, 1) + (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 3)}, - }); - } + @Test + public void testSequenceGuesses() throws Exception { + Context context = StandardContext.build(); + Match match = new Match.Builder(Pattern.Sequence, 0, 0, token).ascending(ascending).build(); + String msg = + String.format("the sequence pattern '%s' has guesses of %s", token, expectedGuesses); + assertEquals(msg, expectedGuesses, new SequenceGuess(context).exec(match), 0.0); + } + + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList( + new Object[][] { + {"ab", true, 4 * 2}, + {"XYZ", true, 26 * 3}, + {"4567", true, 10 * 4}, + {"7654", false, 10 * 4 * 2}, + {"ZYX", false, 4 * 3 * 2} + }); + } + } + + public static class DictionaryGuessesTest { + + @Test + public void testDictionaryGuessesSameWithRank() throws Exception { + Context context = StandardContext.build(); + Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "aaaa").rank(32).build(); + String msg = "base guesses == the rank"; + assertEquals(msg, 32, new DictionaryGuess(context).exec(match), 0.0); + } + + @Test + public void testDictionaryGuessesCapitalization() throws Exception { + Context context = StandardContext.build(); + Match match = new Match.Builder(Pattern.Dictionary, 0, 0, "AAAaaa").rank(32).build(); + String msg = "extra guesses are added for capitalization"; + assertEquals( + msg, + 32 * new DictionaryGuess(context).uppercaseVariations(match), + new DictionaryGuess(context).exec(match), + 0.0); + } + + @Test + public void testDictionaryGuessesReverse() throws Exception { + Context context = StandardContext.build(); + Match match = + new Match.Builder(Pattern.Dictionary, 0, 0, "aaa").reversed(true).rank(32).build(); + String msg = "guesses are doubled when word is reversed"; + assertEquals(msg, 32 * 2, new DictionaryGuess(context).exec(match), 0.0); + } + + @Test + public void testDictionaryGuesses133t() throws Exception { + Context context = StandardContext.build(); + Map sub = new HashMap<>(); + sub.put('@', 'a'); + Match match = + new Match.Builder(Pattern.Dictionary, 0, 0, "aaa@@@") + .sub(sub) + .l33t(true) + .rank(32) + .build(); + String msg = "extra guesses are added for common l33t substitutions"; + assertEquals( + msg, + 32 * new DictionaryGuess(context).l33tVariations(match), + new DictionaryGuess(context).exec(match), + 0.0); + } + + @Test + public void testDictionaryGuessesMixed() throws Exception { + Context context = StandardContext.build(); + Map sub = new HashMap<>(); + sub.put('@', 'a'); + Match match = + new Match.Builder(Pattern.Dictionary, 0, 0, "AaA@@@") + .sub(sub) + .l33t(true) + .rank(32) + .build(); + String msg = "extra guesses are added for both capitalization and common l33t substitutions"; + int expected = + 32 + * new DictionaryGuess(context).l33tVariations(match) + * new DictionaryGuess(context).uppercaseVariations(match); + assertEquals(msg, expected, new DictionaryGuess(context).exec(match), 0.0); + } + } + + @RunWith(Parameterized.class) + public static class UppercaseVariantsTest { + private final String word; + private final int variants; + + public UppercaseVariantsTest(String word, int variants) { + this.word = word; + this.variants = variants; } - @RunWith(Parameterized.class) - public static class L33tVariantsTest { - private final String word; - private final int variants; - private final Map sub; - - public L33tVariantsTest(String word, int variants, Map sub) { - this.word = word; - this.variants = variants; - this.sub = sub; - } - - @Test - public void testL33tVariants() throws Exception { - Context context = StandardContext.build(); - Match match = new Match.Builder(Pattern.Dictionary, 0, 0, word).sub(sub).l33t(!sub.isEmpty()).build(); - String msg = String.format("extra l33t guesses of %s is %s", word, variants); - assertEquals(msg, variants, new DictionaryGuess(context).l33tVariations(match)); - } - - @Parameterized.Parameters(name = "{0}") - public static Collection data() throws Exception { - Context context = StandardContext.build(); - BaseGuess baseGuess = new BaseGuess(context) { - @Override - public double exec(Match match) { - return 0; + @Test + public void testUppercaseVariants() throws Exception { + Context context = StandardContext.build(); + DictionaryGuess dictionaryGuess = new DictionaryGuess(context); + Method uppercaseVariationsMethod = + DictionaryGuess.class.getDeclaredMethod("uppercaseVariations", Match.class); + uppercaseVariationsMethod.setAccessible(true); + + Match match = + new Match.Builder(Pattern.Dictionary, 0, 0, word) + .sub(new HashMap()) + .l33t(true) + .build(); + String msg = String.format("guess multiplier of %s is %s", word, variants); + assertEquals(msg, variants, uppercaseVariationsMethod.invoke(dictionaryGuess, match)); + } + + @Parameterized.Parameters(name = "{0}") + public static Collection data() throws Exception { + Context context = StandardContext.build(); + BaseGuess baseGuess = + new BaseGuess(context) { + @Override + public double exec(Match match) { + return 0; + } + }; + Method method = BaseGuess.class.getDeclaredMethod("nCk", int.class, int.class); + method.setAccessible(true); + return Arrays.asList( + new Object[][] { + {"", 1}, + {"a", 1}, + {"A", 2}, + {"abcdef", 1}, + {"Abcdef", 2}, + {"abcdeF", 2}, + {"ABCDEF", 2}, + {"aBcdef", method.invoke(baseGuess, 6, 1)}, + {"aBcDef", (int) method.invoke(baseGuess, 6, 1) + (int) method.invoke(baseGuess, 6, 2)}, + {"ABCDEf", method.invoke(baseGuess, 6, 1)}, + {"aBCDEf", (int) method.invoke(baseGuess, 6, 1) + (int) method.invoke(baseGuess, 6, 2)}, + { + "ABCdef", + (int) method.invoke(baseGuess, 6, 1) + + (int) method.invoke(baseGuess, 6, 2) + + (int) method.invoke(baseGuess, 6, 3) + }, + }); + } + } + + @RunWith(Parameterized.class) + public static class L33tVariantsTest { + private final String word; + private final int variants; + private final Map sub; + + public L33tVariantsTest(String word, int variants, Map sub) { + this.word = word; + this.variants = variants; + this.sub = sub; + } + + @Test + public void testL33tVariants() throws Exception { + Context context = StandardContext.build(); + Match match = + new Match.Builder(Pattern.Dictionary, 0, 0, word).sub(sub).l33t(!sub.isEmpty()).build(); + String msg = String.format("extra l33t guesses of %s is %s", word, variants); + assertEquals(msg, variants, new DictionaryGuess(context).l33tVariations(match)); + } + + @Parameterized.Parameters(name = "{0}") + public static Collection data() throws Exception { + Context context = StandardContext.build(); + BaseGuess baseGuess = + new BaseGuess(context) { + @Override + public double exec(Match match) { + return 0; + } + }; + Method method = BaseGuess.class.getDeclaredMethod("nCk", int.class, int.class); + method.setAccessible(true); + + return Arrays.asList( + new Object[][] { + {"", 1, Collections.emptyMap()}, + {"a", 1, Collections.emptyMap()}, + {"4", 2, Collections.singletonMap('4', 'a')}, + {"4pple", 2, Collections.singletonMap('4', 'a')}, + {"abcet", 1, Collections.emptyMap()}, + {"4bcet", 2, Collections.singletonMap('4', 'a')}, + {"a8cet", 2, Collections.singletonMap('8', 'b')}, + {"abce+", 2, Collections.singletonMap('+', 't')}, + { + "48cet", + 4, + new HashMap() { + { + put('4', 'a'); + put('8', 'b'); + } + } + }, + { + "a4a4aa", + (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 1), + Collections.singletonMap('4', 'a') + }, + { + "4a4a44", + (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 1), + Collections.singletonMap('4', 'a') + }, + { + "a44att+", + ((int) method.invoke(baseGuess, 4, 2) + (int) method.invoke(baseGuess, 4, 1)) + * (int) method.invoke(baseGuess, 3, 1), + new HashMap() { + { + put('4', 'a'); + put('+', 't'); } - }; - Method method = BaseGuess.class.getDeclaredMethod("nCk", int.class, int.class); - method.setAccessible(true); - - return Arrays.asList(new Object[][]{ - {"", 1, Collections.emptyMap()}, - {"a", 1, Collections.emptyMap()}, - {"4", 2, Collections.singletonMap('4', 'a')}, - {"4pple", 2, Collections.singletonMap('4', 'a')}, - {"abcet", 1, Collections.emptyMap()}, - {"4bcet", 2, Collections.singletonMap('4', 'a')}, - {"a8cet", 2, Collections.singletonMap('8', 'b')}, - {"abce+", 2, Collections.singletonMap('+', 't')}, - {"48cet", 4, new HashMap() {{ - put('4', 'a'); - put('8', 'b'); - }}}, - {"a4a4aa", (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 1), Collections.singletonMap('4', 'a')}, - {"4a4a44", (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 1), Collections.singletonMap('4', 'a')}, - {"a44att+", ((int) method.invoke(baseGuess, 4, 2) + (int) method.invoke(baseGuess, 4, 1)) * (int) method.invoke(baseGuess, 3, 1), - new HashMap() {{ - put('4', 'a'); - put('+', 't'); - }}}, - {"Aa44aA", (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 1), Collections.singletonMap('4', 'a')} - }); - } + } + }, + { + "Aa44aA", + (int) method.invoke(baseGuess, 6, 2) + (int) method.invoke(baseGuess, 6, 1), + Collections.singletonMap('4', 'a') + } + }); + } + } + + public static class RestScoringTest { + @Test + public void testCalcGuessesPassword() throws Exception { + Context context = StandardContext.build(); + Match match = new Match.Builder(Pattern.Dictionary, 0, 8, "password").guesses(1.0).build(); + String msg = "estimate_guesses returns cached guesses when available"; + assertEquals(msg, 1, new EstimateGuess(context, "password").exec(match), 0.0); + } + + @Test + public void testCalcGuessesYear() throws Exception { + Context context = StandardContext.build(); + Match match = MatchFactory.createDateMatch(0, 0, "1977", "/", 1977, 7, 14); + String msg = "estimate_guesses delegates based on pattern"; + assertEquals( + msg, + new EstimateGuess(context, "1977").exec(match), + new DateGuess(context).exec(match), + 0.0); } - public static class RestScoringTest { - @Test - public void testCalcGuessesPassword() throws Exception { - Context context = StandardContext.build(); - Match match = new Match.Builder(Pattern.Dictionary, 0, 8, "password").guesses(1.0).build(); - String msg = "estimate_guesses returns cached guesses when available"; - assertEquals(msg, 1, new EstimateGuess(context, "password").exec(match), 0.0); - } - - @Test - public void testCalcGuessesYear() throws Exception { - Context context = StandardContext.build(); - Match match = MatchFactory.createDateMatch(0, 0, "1977", "/", 1977, 7, 14); - String msg = "estimate_guesses delegates based on pattern"; - assertEquals(msg, new EstimateGuess(context, "1977").exec(match), new DateGuess(context).exec(match), 0.0); - } - - @Test - public void testL33tVariants() throws Exception { - Context context = StandardContext.build(); - Match match = MatchFactory.createDictionaryMatch(0, 0, "", "", 0, ""); - assertEquals("1 variant for non-l33t matches", 1.0, new DictionaryGuess(context).l33tVariations(match), 0.0); - } + @Test + public void testL33tVariants() throws Exception { + Context context = StandardContext.build(); + Match match = MatchFactory.createDictionaryMatch(0, 0, "", "", 0, ""); + assertEquals( + "1 variant for non-l33t matches", + 1.0, + new DictionaryGuess(context).l33tVariations(match), + 0.0); } + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/WipeableStringTest.java b/src/test/java/com/nulabinc/zxcvbn/WipeableStringTest.java index 01d205e..a0abb6d 100644 --- a/src/test/java/com/nulabinc/zxcvbn/WipeableStringTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/WipeableStringTest.java @@ -1,107 +1,110 @@ package com.nulabinc.zxcvbn; -import org.junit.Test; +import static junit.framework.TestCase.*; import java.nio.CharBuffer; - -import static junit.framework.TestCase.*; +import org.junit.Test; public class WipeableStringTest { - @Test - public void testHashCode() { - assertEquals("testing".hashCode(), new WipeableString("testing").hashCode()); - } - - @Test - public void testEquals() { - assertTrue(new WipeableString("hello").equals("hello")); - assertFalse(new WipeableString("goodbye").equals("hello")); - } - - @Test - public void testLowerCase() { - assertEquals("abc", WipeableString.lowerCase("ABC").toString()); - assertEquals("abc", WipeableString.lowerCase("abc").toString()); - assertEquals("abcxyz", WipeableString.lowerCase("abcXYZ").toString()); - assertEquals("", WipeableString.lowerCase("").toString()); - } - - @Test - public void testReversed() { - assertEquals("CBA", WipeableString.reversed("ABC").toString()); - assertEquals("", WipeableString.reversed("").toString()); - assertEquals("X", WipeableString.reversed("X").toString()); - } - - @Test - public void testWipeIfPossible() { - testWipeIfPossible(new StringBuffer("password"), "wiping StringBuffer"); - testWipeIfPossible(new StringBuilder("password"), "wiping StringBuilder"); - testWipeIfPossible(CharBuffer.wrap("password".toCharArray()), "wiping CharBuffer"); - } - - private void testWipeIfPossible(CharSequence text, String message) { - assertEquals(message+" (before)", "password", text.toString()); - WipeableString.wipeIfPossible(text); - assertEquals(message, "", text.toString().trim()); - } - - @Test - public void testWipeStrength() { - Strength strength = new Zxcvbn().measure(new WipeableString("pa55w0rd")); - - assertEquals("pa55w0rd", strength.getPassword().toString()); - assertEquals("pa55w0rd", strength.getSequence().get(0).token.toString()); - - strength.wipe(); - - assertEquals( "", strength.getPassword().toString()); - assertEquals("", strength.getSequence().get(0).token.toString()); - } - - @Test - public void testParseInt() { - assertEquals(1, WipeableString.parseInt("1")); - assertEquals(1, WipeableString.parseInt("+1")); - assertEquals(1, WipeableString.parseInt("+01")); - assertEquals(1928, WipeableString.parseInt("1928")); - assertEquals(19369, WipeableString.parseInt("00019369")); - assertEquals(-101, WipeableString.parseInt("-101")); - assertEquals(5, WipeableString.parseInt("101",2)); - } - - @Test - public void testWipeStrengthWithStringPassword() { - Strength strength = new Zxcvbn().measure("pa55w0rd"); - - assertEquals("pa55w0rd", strength.getPassword().toString()); - - strength.wipe(); - - assertEquals("string passwords cannot be wiped","pa55w0rd", strength.getPassword().toString()); - } - - @Test - public void testParseIntWithTrailingSpaces() { - assertEquals(2001, WipeableString.parseInt("2001 ")); - assertEquals(1, WipeableString.parseInt("1 ")); - assertEquals(2001, WipeableString.parseInt("2001 ")); - } - - @Test - public void testParseIntWithTrailingCRLF() { - assertEquals(2001, WipeableString.parseInt(CharBuffer.wrap(new char[]{'2','0','0','1',(char)13,(char)10}))); - } - - @Test - public void testParseIntWithTrailingCR() { - assertEquals(2001, WipeableString.parseInt(CharBuffer.wrap(new char[]{'2','0','0','1',(char)13}))); - } - - @Test - public void testParseIntWithTrailingLF() { - assertEquals(2001, WipeableString.parseInt(CharBuffer.wrap(new char[]{'2','0','0','1',(char)10}))); - } - + @Test + public void testHashCode() { + assertEquals("testing".hashCode(), new WipeableString("testing").hashCode()); + } + + @Test + public void testEquals() { + assertTrue(new WipeableString("hello").equals("hello")); + assertFalse(new WipeableString("goodbye").equals("hello")); + } + + @Test + public void testLowerCase() { + assertEquals("abc", WipeableString.lowerCase("ABC").toString()); + assertEquals("abc", WipeableString.lowerCase("abc").toString()); + assertEquals("abcxyz", WipeableString.lowerCase("abcXYZ").toString()); + assertEquals("", WipeableString.lowerCase("").toString()); + } + + @Test + public void testReversed() { + assertEquals("CBA", WipeableString.reversed("ABC").toString()); + assertEquals("", WipeableString.reversed("").toString()); + assertEquals("X", WipeableString.reversed("X").toString()); + } + + @Test + public void testWipeIfPossible() { + testWipeIfPossible(new StringBuffer("password"), "wiping StringBuffer"); + testWipeIfPossible(new StringBuilder("password"), "wiping StringBuilder"); + testWipeIfPossible(CharBuffer.wrap("password".toCharArray()), "wiping CharBuffer"); + } + + private void testWipeIfPossible(CharSequence text, String message) { + assertEquals(message + " (before)", "password", text.toString()); + WipeableString.wipeIfPossible(text); + assertEquals(message, "", text.toString().trim()); + } + + @Test + public void testWipeStrength() { + Strength strength = new Zxcvbn().measure(new WipeableString("pa55w0rd")); + + assertEquals("pa55w0rd", strength.getPassword().toString()); + assertEquals("pa55w0rd", strength.getSequence().get(0).token.toString()); + + strength.wipe(); + + assertEquals("", strength.getPassword().toString()); + assertEquals("", strength.getSequence().get(0).token.toString()); + } + + @Test + public void testParseInt() { + assertEquals(1, WipeableString.parseInt("1")); + assertEquals(1, WipeableString.parseInt("+1")); + assertEquals(1, WipeableString.parseInt("+01")); + assertEquals(1928, WipeableString.parseInt("1928")); + assertEquals(19369, WipeableString.parseInt("00019369")); + assertEquals(-101, WipeableString.parseInt("-101")); + assertEquals(5, WipeableString.parseInt("101", 2)); + } + + @Test + public void testWipeStrengthWithStringPassword() { + Strength strength = new Zxcvbn().measure("pa55w0rd"); + + assertEquals("pa55w0rd", strength.getPassword().toString()); + + strength.wipe(); + + assertEquals("string passwords cannot be wiped", "pa55w0rd", strength.getPassword().toString()); + } + + @Test + public void testParseIntWithTrailingSpaces() { + assertEquals(2001, WipeableString.parseInt("2001 ")); + assertEquals(1, WipeableString.parseInt("1 ")); + assertEquals(2001, WipeableString.parseInt("2001 ")); + } + + @Test + public void testParseIntWithTrailingCRLF() { + assertEquals( + 2001, + WipeableString.parseInt( + CharBuffer.wrap(new char[] {'2', '0', '0', '1', (char) 13, (char) 10}))); + } + + @Test + public void testParseIntWithTrailingCR() { + assertEquals( + 2001, WipeableString.parseInt(CharBuffer.wrap(new char[] {'2', '0', '0', '1', (char) 13}))); + } + + @Test + public void testParseIntWithTrailingLF() { + assertEquals( + 2001, WipeableString.parseInt(CharBuffer.wrap(new char[] {'2', '0', '0', '1', (char) 10}))); + } } diff --git a/src/test/java/com/nulabinc/zxcvbn/ZxcvbnBuilderTest.java b/src/test/java/com/nulabinc/zxcvbn/ZxcvbnBuilderTest.java index 6560ff2..0f18b63 100644 --- a/src/test/java/com/nulabinc/zxcvbn/ZxcvbnBuilderTest.java +++ b/src/test/java/com/nulabinc/zxcvbn/ZxcvbnBuilderTest.java @@ -1,51 +1,66 @@ package com.nulabinc.zxcvbn; +import static org.junit.Assert.assertNotNull; + import com.nulabinc.zxcvbn.io.ClasspathResource; import com.nulabinc.zxcvbn.matchers.AlignedKeyboardLoader; import com.nulabinc.zxcvbn.matchers.DictionaryLoader; import com.nulabinc.zxcvbn.matchers.SlantedKeyboardLoader; -import org.junit.*; - import java.io.IOException; - -import static org.junit.Assert.assertNotNull; +import org.junit.*; public class ZxcvbnBuilderTest { - @Test - public void testBuild1() { - Zxcvbn zxcvbn = new ZxcvbnBuilder().build(); - assertNotNull(zxcvbn); - } + @Test + public void testBuild1() { + Zxcvbn zxcvbn = new ZxcvbnBuilder().build(); + assertNotNull(zxcvbn); + } - @Test - public void testBuild2() throws IOException { - // This way is same as "new Zxcvbn();" - Zxcvbn zxcvbn = new ZxcvbnBuilder() - .dictionaries(StandardDictionaries.loadAllDictionaries()) - .keyboards(StandardKeyboards.loadAllKeyboards()) - .build(); - assertNotNull(zxcvbn); - } + @Test + public void testBuild2() throws IOException { + // This way is same as "new Zxcvbn();" + Zxcvbn zxcvbn = + new ZxcvbnBuilder() + .dictionaries(StandardDictionaries.loadAllDictionaries()) + .keyboards(StandardKeyboards.loadAllKeyboards()) + .build(); + assertNotNull(zxcvbn); + } - @Test - public void testBuild3() throws IOException { - Zxcvbn zxcvbn = new ZxcvbnBuilder() - .dictionary(StandardDictionaries.ENGLISH_WIKIPEDIA_LOADER.load()) - .dictionary(StandardDictionaries.PASSWORDS_LOADER.load()) - .keyboard(StandardKeyboards.QWERTY_LOADER.load()) - .keyboard(StandardKeyboards.DVORAK_LOADER.load()) - .build(); - assertNotNull(zxcvbn); - } + @Test + public void testBuild3() throws IOException { + Zxcvbn zxcvbn = + new ZxcvbnBuilder() + .dictionary(StandardDictionaries.ENGLISH_WIKIPEDIA_LOADER.load()) + .dictionary(StandardDictionaries.PASSWORDS_LOADER.load()) + .keyboard(StandardKeyboards.QWERTY_LOADER.load()) + .keyboard(StandardKeyboards.DVORAK_LOADER.load()) + .build(); + assertNotNull(zxcvbn); + } - @Test - public void testBuild4() throws IOException { - Zxcvbn zxcvbn = new ZxcvbnBuilder() - .dictionary(new DictionaryLoader("us_tv_and_film", new ClasspathResource("/com/nulabinc/zxcvbn/matchers/dictionaries/us_tv_and_film.txt")).load()) - .keyboard(new SlantedKeyboardLoader("qwerty", new ClasspathResource("/com/nulabinc/zxcvbn/matchers/keyboards/qwerty.txt")).load()) - .keyboard(new AlignedKeyboardLoader("keypad", new ClasspathResource("/com/nulabinc/zxcvbn/matchers/keyboards/keypad.txt")).load()) - .build(); - assertNotNull(zxcvbn); - } + @Test + public void testBuild4() throws IOException { + Zxcvbn zxcvbn = + new ZxcvbnBuilder() + .dictionary( + new DictionaryLoader( + "us_tv_and_film", + new ClasspathResource( + "/com/nulabinc/zxcvbn/matchers/dictionaries/us_tv_and_film.txt")) + .load()) + .keyboard( + new SlantedKeyboardLoader( + "qwerty", + new ClasspathResource("/com/nulabinc/zxcvbn/matchers/keyboards/qwerty.txt")) + .load()) + .keyboard( + new AlignedKeyboardLoader( + "keypad", + new ClasspathResource("/com/nulabinc/zxcvbn/matchers/keyboards/keypad.txt")) + .load()) + .build(); + assertNotNull(zxcvbn); + } }