Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/me/gosimple/nbvcxz/matching/DateMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ private static ValidDateSplit isDateValid(String day, String month, String year)
}
}

public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{

List<Match> dateMatches = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ else if (m == 0)
return -1;
}

public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
DictionaryBuilder userInputBuilder = new DictionaryBuilder()
.setDictionaryName("userInput")
Expand Down
86 changes: 86 additions & 0 deletions src/main/java/me/gosimple/nbvcxz/matching/LeakMatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package me.gosimple.nbvcxz.matching;

import me.gosimple.nbvcxz.matching.match.LeakMatch;
import me.gosimple.nbvcxz.matching.match.Match;
import me.gosimple.nbvcxz.resources.Configuration;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
* Look for the password by querying the Leak API
*
* @author Adam Brusselback
*/
public final class LeakMatcher implements PasswordMatcher
{
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
ArrayList<Match> result = new ArrayList<>();

if (!configuration.getLeakApiEnabled())
return result;

String hashedPassword = hashPassword(password);
String prefix = hashedPassword.substring(0, 5);
String suffix = hashedPassword.substring(5).toUpperCase();
String url = configuration.getLeakApiEndpoint() + prefix;

try {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", "HIBP-Java-API");

int responseCode = connection.getResponseCode();
if (responseCode == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
HashMap<String, Integer> response = new HashMap<>();
while ((inputLine = in.readLine()) != null) {
String[] line = inputLine.split(":");
response.put(line[0], Integer.valueOf(line[1]));
}
in.close();

if (response.containsKey(suffix)) {
LeakMatch match = new LeakMatch(password, configuration, response.get(suffix), 0, password.length());
result.add(match);
return result;
} else {
return result;
}
} else {
System.out.println("Error with request, response code: " + responseCode);
return result;
}
} catch (IOException e) {
System.out.println("Unable to run LeakMatcher");
e.printStackTrace();
}
return null;
}

private static String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
sb.append(Integer.toString((digest[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
public final class RepeatMatcher implements PasswordMatcher
{
public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
List<Match> matches = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public final class SeparatorMatcher implements PasswordMatcher

private static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[^a-zA-Z\\d]");

public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
Matcher matcher = NON_ALPHA_NUMERIC.matcher(password);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/
public final class SequenceMatcher implements PasswordMatcher
{
public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
List<Match> matches = new ArrayList<>();
char[] characters = password.toCharArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/
public final class SpacialMatcher implements PasswordMatcher
{
public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
List<Match> matches = new ArrayList<>();
Map<Integer, Set<Character>> neighbors = new HashMap<>();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/me/gosimple/nbvcxz/matching/YearMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
public final class YearMatcher implements PasswordMatcher
{
public List<Match> match(final Configuration configuration, final String password, String... userInput)
public List<Match> match(final Configuration configuration, final String password, final String... userInput)
{
Pattern pattern = configuration.getYearPattern();
Matcher matcher = pattern.matcher(password);
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/me/gosimple/nbvcxz/matching/match/LeakMatch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package me.gosimple.nbvcxz.matching.match;

import me.gosimple.nbvcxz.resources.Configuration;

import java.util.ResourceBundle;

/**
* @author Adam Brusselback
*/
public final class LeakMatch extends BaseMatch
{
private int count;
public LeakMatch(String match, Configuration configuration, int count, int start_index, int end_index)
{
super(match, configuration, start_index, end_index);

this.count = count;

super.setEntropy(getEntropy());
}

private double getEntropy()
{
return 0d;
}

public int getCount()
{
return count;
}

public String getDetails()
{
ResourceBundle mainResource = configuration.getMainResource();
StringBuilder detailBuilder = new StringBuilder();
detailBuilder.append(super.getDetails());
detailBuilder.append("\n");
detailBuilder.append(mainResource.getString("main.match.count")).append(" ").append(getCount());
detailBuilder.append("\n");
return detailBuilder.toString();
}


}
20 changes: 19 additions & 1 deletion src/main/java/me/gosimple/nbvcxz/resources/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class Configuration
private final ResourceBundle mainResource;
private final ResourceBundle feedbackResource;
private final long combinationAlgorithmTimeout;
private final String leakApiEndpoint;
private final boolean leakApiEnabled;

/**
* @param passwordMatchers The list of {@link PasswordMatcher}s which will be used for matching
Expand All @@ -44,7 +46,7 @@ public class Configuration
* @param distanceCalc Enable or disable levenshtein distance calculation for dictionary matches
* @param combinationAlgorithmTimeout Timeout for the findBestMatches algorithm
*/
public Configuration(List<PasswordMatcher> passwordMatchers, Map<String, Long> guessTypes, List<Dictionary> dictionaries, List<AdjacencyGraph> adjacencyGraphs, Map<Character, Character[]> leetTable, Pattern yearPattern, Double minimumEntropy, Integer maxLength, Locale locale, boolean distanceCalc, long combinationAlgorithmTimeout)
public Configuration(List<PasswordMatcher> passwordMatchers, Map<String, Long> guessTypes, List<Dictionary> dictionaries, List<AdjacencyGraph> adjacencyGraphs, Map<Character, Character[]> leetTable, Pattern yearPattern, Double minimumEntropy, Integer maxLength, Locale locale, boolean distanceCalc, long combinationAlgorithmTimeout, String leakApiEndpoint, boolean leakApiEnabled)
{
this.passwordMatchers = passwordMatchers;
this.guessTypes = guessTypes;
Expand All @@ -59,6 +61,8 @@ public Configuration(List<PasswordMatcher> passwordMatchers, Map<String, Long> g
this.mainResource = ResourceBundle.getBundle("main", locale);
this.feedbackResource = ResourceBundle.getBundle("feedback", locale);
this.combinationAlgorithmTimeout = combinationAlgorithmTimeout;
this.leakApiEndpoint = leakApiEndpoint;
this.leakApiEnabled = leakApiEnabled;
}

/**
Expand Down Expand Up @@ -148,6 +152,20 @@ public long getCombinationAlgorithmTimeout()
return combinationAlgorithmTimeout;
}

/**
* @return The endpoint for the Leak API, used by the LeakMatcher
*/
public String getLeakApiEndpoint() {
return leakApiEndpoint;
}

/**
* @return The API key used for the Leak API Endpoint
*/
public boolean getLeakApiEnabled() {
return leakApiEnabled;
}

/**
* @return Return the resource bundle which contains the text for everything but feedback
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package me.gosimple.nbvcxz.resources;

import me.gosimple.nbvcxz.Nbvcxz;
import me.gosimple.nbvcxz.matching.DateMatcher;
import me.gosimple.nbvcxz.matching.DictionaryMatcher;
import me.gosimple.nbvcxz.matching.PasswordMatcher;
import me.gosimple.nbvcxz.matching.RepeatMatcher;
import me.gosimple.nbvcxz.matching.SeparatorMatcher;
import me.gosimple.nbvcxz.matching.SequenceMatcher;
import me.gosimple.nbvcxz.matching.SpacialMatcher;
import me.gosimple.nbvcxz.matching.YearMatcher;
import me.gosimple.nbvcxz.matching.*;
import me.gosimple.nbvcxz.matching.match.Match;

import java.math.BigDecimal;
Expand Down Expand Up @@ -44,6 +37,7 @@ public class ConfigurationBuilder
defaultPasswordMatchers.add(new SpacialMatcher());
defaultPasswordMatchers.add(new DictionaryMatcher());
defaultPasswordMatchers.add(new SeparatorMatcher());
defaultPasswordMatchers.add(new LeakMatcher());

defaultDictionaries.add(new Dictionary("passwords", DictionaryUtil.loadRankedDictionary(DictionaryUtil.passwords), false));
defaultDictionaries.add(new Dictionary("male_names", DictionaryUtil.loadRankedDictionary(DictionaryUtil.male_names), false));
Expand Down Expand Up @@ -92,6 +86,8 @@ public class ConfigurationBuilder
private Boolean distanceCalc;
private Long combinationAlgorithmTimeout;
private Long crackingHardwareCost;
private String leakApiEndpoint;
private Boolean leakApiEnabled;

/**
* @return Includes all standard password matchers included with Nbvcxz.
Expand Down Expand Up @@ -236,6 +232,22 @@ public static long getDefaultCrackingHardwareCost()
return 20000;
}

/**
* @return The default Leak API Endpoint, HIBP(v3) by default
*/
public static String getDefaultLeakApiEndpoint()
{
return "https://api.pwnedpasswords.com/range/";
}

/**
* @return true, the leak API is enabled by default
*/
public static boolean getDefaultLeakApiEnabled()
{
return true;
}

/**
* {@link PasswordMatcher} are what look for different patterns within the password and create an associated {@link Match} object.
* <br>
Expand Down Expand Up @@ -418,6 +430,27 @@ public ConfigurationBuilder setCrackingHardwareCost(final Long crackingHardwareC
return this;
}

/**
* Sets the Leak API Endpoint to be used by the LeakMatcher.
* By default, this is the HIBP(v3) api. You can point it to any compatible API endpoint.
* @param leakApiEndpoint the API endpoint URL
* @return Builder
*/
public ConfigurationBuilder setLeakApiEndpoint(final String leakApiEndpoint) {
this.leakApiEndpoint = leakApiEndpoint;
return this;
}

/**
* Enables the LeakMatcher using the configured LeakApiEndpoint
* @param leakApiEnabled the key used to authenticate to the Leak API
* @return Builder
*/
public ConfigurationBuilder setLeakApiEnabled(final boolean leakApiEnabled) {
this.leakApiEnabled = leakApiEnabled;
return this;
}

/**
* Creates the {@link Configuration} object using all values set in this builder, or default values if unset.
*
Expand Down Expand Up @@ -473,7 +506,15 @@ public Configuration createConfiguration()
{
combinationAlgorithmTimeout = getDefaultCombinationAlgorithmTimeout();
}
return new Configuration(passwordMatchers, guessTypes, dictionaries, adjacencyGraphs, leetTable, yearPattern, minimumEntropy, maxLength, locale, distanceCalc, combinationAlgorithmTimeout);
if (leakApiEndpoint == null)
{
leakApiEndpoint = getDefaultLeakApiEndpoint();
}
if (leakApiEnabled == null)
{
leakApiEnabled = getDefaultLeakApiEnabled();
}
return new Configuration(passwordMatchers, guessTypes, dictionaries, adjacencyGraphs, leetTable, yearPattern, minimumEntropy, maxLength, locale, distanceCalc, combinationAlgorithmTimeout, leakApiEndpoint, leakApiEnabled);
}


Expand Down
1 change: 1 addition & 0 deletions src/main/resources/main.properties
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ main.match.year=Year:
main.match.month=Month:
main.match.day=Day:
main.match.separator=Separator:
main.match.count=Leak Count:
main.estimate.greaterCenturies=infinite (>100000 centuries)
main.estimate.centuries=centuries
main.estimate.years=years
Expand Down
Loading