Implement incremental cracking
This commit is contained in:
parent
43cf53c0ca
commit
ef15a10a4e
28 changed files with 833 additions and 189 deletions
12
README.md
12
README.md
|
@ -3,7 +3,10 @@
|
||||||
[![Test Coverage](https://api.codeclimate.com/v1/badges/a35dff49221e36abf189/test_coverage)](https://codeclimate.com/github/wbrawner/keecrack/test_coverage)
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/a35dff49221e36abf189/test_coverage)](https://codeclimate.com/github/wbrawner/keecrack/test_coverage)
|
||||||
|
|
||||||
KeeCrack is a Java program used for brute-forcing KeePass database file master passwords. This should go without saying
|
KeeCrack is a Java program used for brute-forcing KeePass database file master passwords. This should go without saying
|
||||||
but use of this application is prohibited without the express consent of the owner of the database file.
|
but use of this application is prohibited without the express consent of the owner of the database file. KeeCrack works
|
||||||
|
by taking a KeePass database file, an optional key file, and a word list, then attempts to open the database with the
|
||||||
|
give key file/password pair until it finds a successful password. KeeCrack does not do incremental word list generation
|
||||||
|
at this time, though you can
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -17,6 +20,13 @@ KeeCrack makes use of Gradle, so to build it yourself, you can just run
|
||||||
|
|
||||||
./gradlew jfxJar
|
./gradlew jfxJar
|
||||||
|
|
||||||
|
This will produce a JAR output, though you can also create platform-specific binaries with the following:
|
||||||
|
|
||||||
|
./gradlew jfxNative
|
||||||
|
|
||||||
|
For more information on building for your OS, please see the README for the
|
||||||
|
[javafx-gradle-plugin](https://github .com/FibreFoX/javafx-gradle-plugin#requirements)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you'd like to contribute, please fork the repository, make your changes, squash your commits, and send a pull request
|
If you'd like to contribute, please fork the repository, make your changes, squash your commits, and send a pull request
|
||||||
|
|
15
build.gradle
15
build.gradle
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
#Sat Apr 07 16:39:20 CDT 2018
|
#Sun Apr 15 14:52:35 CDT 2018
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':keecrack-lib')
|
compile project(':keecrack-lib')
|
||||||
compile project(':keecrack-gui')
|
compile project(':keecrack-gui')
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.cli;
|
package com.wbrawner.keecrack.cli;
|
||||||
|
|
||||||
import com.wbrawner.keecrack.lib.Code;
|
import com.wbrawner.keecrack.lib.Code;
|
||||||
|
@ -10,13 +25,22 @@ import net.sourceforge.argparse4j.inf.ArgumentParserException;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
private static final String LOG_SEPARATOR = " - ";
|
||||||
|
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss.SSS]");
|
||||||
private static boolean isVerbose = false;
|
private static boolean isVerbose = false;
|
||||||
|
private static boolean isIncremental = false;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
if (args.length == 0) {
|
||||||
|
com.wbrawner.keecrack.gui.Main.main(new String[]{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
ArgumentParser parser = ArgumentParsers.newFor("KeeCrack")
|
ArgumentParser parser = ArgumentParsers.newFor("KeeCrack")
|
||||||
.build()
|
.build()
|
||||||
.description("Brute force KeePass database files");
|
.description("Brute force KeePass database files");
|
||||||
|
@ -24,13 +48,15 @@ public class Main {
|
||||||
.help("Increase logging output")
|
.help("Increase logging output")
|
||||||
.action(Arguments.storeTrue())
|
.action(Arguments.storeTrue())
|
||||||
.dest("verbose");
|
.dest("verbose");
|
||||||
parser.addArgument("--gui")
|
parser.addArgument("--incremental", "-i")
|
||||||
.action(Arguments.storeTrue())
|
.action(Arguments.storeTrue())
|
||||||
.help("launch the graphical interface (ignores other options)")
|
.help("Use pattern-based (incremental) guesses instead of a list of words from a file")
|
||||||
.dest("gui");
|
.dest("incremental");
|
||||||
parser.addArgument("--word-list", "-w")
|
parser.addArgument("--word-list", "-w")
|
||||||
.help("a file containing newline-separated words to use as the passwords")
|
.help("a file containing newline-separated words to use as the passwords, or the pattern to generate " +
|
||||||
|
"words from if the --incremental flag is set")
|
||||||
.dest("wordlist")
|
.dest("wordlist")
|
||||||
|
.required(true)
|
||||||
.metavar("WORD-LIST-FILE");
|
.metavar("WORD-LIST-FILE");
|
||||||
parser.addArgument("--key-file", "-k")
|
parser.addArgument("--key-file", "-k")
|
||||||
.help("the key file to use with the database")
|
.help("the key file to use with the database")
|
||||||
|
@ -44,6 +70,7 @@ public class Main {
|
||||||
try {
|
try {
|
||||||
Namespace res = parser.parseArgs(args);
|
Namespace res = parser.parseArgs(args);
|
||||||
isVerbose = res.getBoolean("verbose");
|
isVerbose = res.getBoolean("verbose");
|
||||||
|
isIncremental = res.getBoolean("incremental");
|
||||||
KeeCrack keeCrack = KeeCrack.getInstance();
|
KeeCrack keeCrack = KeeCrack.getInstance();
|
||||||
keeCrack.setCrackingView(new CLICrackingView());
|
keeCrack.setCrackingView(new CLICrackingView());
|
||||||
|
|
||||||
|
@ -57,25 +84,38 @@ public class Main {
|
||||||
keeCrack.setKeyFile(new File(keyfilePath));
|
keeCrack.setKeyFile(new File(keyfilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
String wordlistPath = res.getString("wordlist");
|
String wordlist = res.getString("wordlist");
|
||||||
if (wordlistPath != null) ;
|
if (wordlist != null) {
|
||||||
keeCrack.setWordlistFile(new File(wordlistPath));
|
if (isIncremental) {
|
||||||
|
keeCrack.setWordListPattern(wordlist);
|
||||||
if (res.getBoolean("gui")) {
|
} else {
|
||||||
com.wbrawner.keecrack.gui.Main.main(new String[]{});
|
keeCrack.setWordListFile(new File(wordlist));
|
||||||
} else {
|
}
|
||||||
keeCrack.attack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keeCrack.attack();
|
||||||
} catch (ArgumentParserException e) {
|
} catch (ArgumentParserException e) {
|
||||||
parser.handleError(e);
|
parser.handleError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void print(String message) {
|
||||||
|
System.out.print(dateFormat.format(new Date()));
|
||||||
|
System.out.print(LOG_SEPARATOR);
|
||||||
|
System.out.println(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void error(String message) {
|
||||||
|
System.err.print(dateFormat.format(new Date()));
|
||||||
|
System.err.print(LOG_SEPARATOR);
|
||||||
|
System.err.println(message);
|
||||||
|
}
|
||||||
|
|
||||||
static class CLICrackingView implements CrackingView {
|
static class CLICrackingView implements CrackingView {
|
||||||
@Override
|
@Override
|
||||||
public void onPasswordGuess(String password) {
|
public void onPasswordGuess(String password) {
|
||||||
if (isVerbose)
|
if (isVerbose)
|
||||||
System.out.println("Guessing password: " + password);
|
print("Guessing password: " + password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,17 +135,17 @@ public class Main {
|
||||||
timeElapsed.toString().toLowerCase().substring(2),
|
timeElapsed.toString().toLowerCase().substring(2),
|
||||||
password
|
password
|
||||||
);
|
);
|
||||||
System.out.println(message);
|
print(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Code code) {
|
public void onError(Code code) {
|
||||||
String message = "";
|
String message = "";
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case ERROR_MISSING_DATABASE_FILE:
|
case ERROR_INVALID_DATABASE_FILE:
|
||||||
message = "Please specify a database file that you have read access to";
|
message = "Please specify a database file that you have read access to";
|
||||||
break;
|
break;
|
||||||
case ERROR_MISSING_WORD_LIST_FILE:
|
case ERROR_INVALID_WORD_LIST:
|
||||||
message = "Please specify a word list file that you have read access to";
|
message = "Please specify a word list file that you have read access to";
|
||||||
break;
|
break;
|
||||||
case ERROR_CRACKING_INTERRUPTED:
|
case ERROR_CRACKING_INTERRUPTED:
|
||||||
|
@ -115,7 +155,7 @@ public class Main {
|
||||||
message = "An error occurred while trying to read one of the files";
|
message = "An error occurred while trying to read one of the files";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
System.err.println(message);
|
error(message);
|
||||||
System.exit(code.ordinal());
|
System.exit(code.ordinal());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':keecrack-lib')
|
compile project(':keecrack-lib')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.gui;
|
package com.wbrawner.keecrack.gui;
|
||||||
|
|
||||||
import com.wbrawner.keecrack.lib.Code;
|
import com.wbrawner.keecrack.lib.Code;
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.gui;
|
package com.wbrawner.keecrack.gui;
|
||||||
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
@ -10,15 +25,15 @@ import java.io.IOException;
|
||||||
|
|
||||||
public class Main extends Application {
|
public class Main extends Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws IOException {
|
public void start(Stage primaryStage) throws IOException {
|
||||||
Parent root = FXMLLoader.load(getClass().getResource("/fxml/main.fxml"));
|
Parent root = FXMLLoader.load(getClass().getResource("/fxml/main.fxml"));
|
||||||
primaryStage.setTitle("Keecrack");
|
primaryStage.setTitle("Keecrack");
|
||||||
primaryStage.setScene(new Scene(root, 400, 400));
|
primaryStage.setScene(new Scene(root, 400, 300));
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
launch(args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.gui;
|
package com.wbrawner.keecrack.gui;
|
||||||
|
|
||||||
import com.wbrawner.keecrack.lib.Code;
|
import com.wbrawner.keecrack.lib.Code;
|
||||||
|
@ -6,10 +21,13 @@ import com.wbrawner.keecrack.lib.view.FormView;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Cursor;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
@ -21,14 +39,25 @@ import java.util.ResourceBundle;
|
||||||
|
|
||||||
public class MainController implements Initializable, FormView {
|
public class MainController implements Initializable, FormView {
|
||||||
|
|
||||||
@FXML private TextField database;
|
private static KeeCrack keeCrack;
|
||||||
@FXML private TextField key;
|
@FXML
|
||||||
@FXML private TextField wordlist;
|
private TextField database;
|
||||||
@FXML private Button crackButton;
|
@FXML
|
||||||
|
private TextField key;
|
||||||
|
@FXML
|
||||||
|
private TextField wordlist;
|
||||||
|
@FXML
|
||||||
|
private Button crackButton;
|
||||||
|
@FXML
|
||||||
|
private ToggleGroup wordlistType;
|
||||||
|
@FXML
|
||||||
|
private RadioButton wordlistFile;
|
||||||
|
@FXML
|
||||||
|
private RadioButton wordlistPattern;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
KeeCrack keeCrack = KeeCrack.getInstance();
|
keeCrack = KeeCrack.getInstance();
|
||||||
keeCrack.setFormView(this);
|
keeCrack.setFormView(this);
|
||||||
database.setOnMouseClicked(event -> {
|
database.setOnMouseClicked(event -> {
|
||||||
if (KeeCrack.getInstance().isCracking()) {
|
if (KeeCrack.getInstance().isCracking()) {
|
||||||
|
@ -48,14 +77,7 @@ public class MainController implements Initializable, FormView {
|
||||||
keeCrack.setKeyFile(keyFile);
|
keeCrack.setKeyFile(keyFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
wordlist.setOnMouseClicked(event -> {
|
updateWordListHandler();
|
||||||
if (KeeCrack.getInstance().isCracking()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File wordlistFile = getFile("Text Files", "txt");
|
|
||||||
keeCrack.setWordlistFile(wordlistFile);
|
|
||||||
});
|
|
||||||
|
|
||||||
crackButton.setOnMouseClicked(event -> {
|
crackButton.setOnMouseClicked(event -> {
|
||||||
try {
|
try {
|
||||||
|
@ -79,19 +101,21 @@ public class MainController implements Initializable, FormView {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
Parent root = loader.load();
|
Parent root = loader.load();
|
||||||
stage.setScene(new Scene(root, 200, 200));
|
stage.setScene(new Scene(root, 400, 350));
|
||||||
stage.showAndWait();
|
stage.showAndWait();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
wordlistType.selectedToggleProperty().addListener((observableValue, oldToggle, newToggle) ->
|
||||||
|
updateWordListHandler());
|
||||||
|
|
||||||
if (keeCrack.getDatabaseFile() != null)
|
if (keeCrack.getDatabaseFile() != null)
|
||||||
onDatabaseFileSet(keeCrack.getDatabaseFile().getName());
|
onDatabaseFileSet(keeCrack.getDatabaseFile().getName());
|
||||||
if (keeCrack.getKeyFile() != null)
|
if (keeCrack.getKeyFile() != null)
|
||||||
onKeyFileSet(keeCrack.getKeyFile().getName());
|
onKeyFileSet(keeCrack.getKeyFile().getName());
|
||||||
if (keeCrack.getWordlistFile() != null)
|
if (keeCrack.getWordListName() != null)
|
||||||
onWordListFileSet(keeCrack.getWordlistFile().getName());
|
onWordListSet(keeCrack.getWordListName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,6 +131,26 @@ public class MainController implements Initializable, FormView {
|
||||||
return fileChooser.showOpenDialog(stage);
|
return fileChooser.showOpenDialog(stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateWordListHandler() {
|
||||||
|
wordlist.clear();
|
||||||
|
if (wordlistFile.isSelected()) {
|
||||||
|
wordlist.setEditable(false);
|
||||||
|
wordlist.setCursor(Cursor.HAND);
|
||||||
|
wordlist.setOnMouseClicked(event -> {
|
||||||
|
if (KeeCrack.getInstance().isCracking()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File wordlistFile = getFile("Text Files", "txt");
|
||||||
|
keeCrack.setWordListFile(wordlistFile);
|
||||||
|
});
|
||||||
|
} else if (wordlistPattern.isSelected()) {
|
||||||
|
wordlist.setEditable(true);
|
||||||
|
wordlist.setCursor(Cursor.TEXT);
|
||||||
|
wordlist.setOnMouseExited(mouseEvent -> keeCrack.setWordListPattern(wordlist.getText()));
|
||||||
|
wordlist.setOnMouseClicked(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDatabaseFileSet(String name) {
|
public void onDatabaseFileSet(String name) {
|
||||||
database.setText(name);
|
database.setText(name);
|
||||||
|
@ -118,7 +162,7 @@ public class MainController implements Initializable, FormView {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWordListFileSet(String name) {
|
public void onWordListSet(String name) {
|
||||||
wordlist.setText(name);
|
wordlist.setText(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,32 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ProgressIndicator?>
|
<?import javafx.scene.control.ProgressIndicator?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8"
|
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8"
|
||||||
fx:controller="com.wbrawner.keecrack.gui.CrackingController">
|
fx:controller="com.wbrawner.keecrack.gui.CrackingController">
|
||||||
<children>
|
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0"
|
||||||
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<ProgressIndicator fx:id="progress"/>
|
||||||
<ProgressIndicator fx:id="progress" />
|
<Label fx:id="timeElapsed"/>
|
||||||
<Label fx:id="timeElapsed" />
|
<Label fx:id="passwordLabel" text="Trying password: "/>
|
||||||
<Label fx:id="passwordLabel" text="Trying password: " />
|
<Label fx:id="password"/>
|
||||||
<Label fx:id="password" />
|
</VBox>
|
||||||
</children>
|
|
||||||
</VBox>
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|
|
@ -1,61 +1,96 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.*?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.Cursor?>
|
<?import javafx.scene.Cursor?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8"
|
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8"
|
||||||
fx:controller="com.wbrawner.keecrack.gui.MainController">
|
fx:controller="com.wbrawner.keecrack.gui.MainController">
|
||||||
<children>
|
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0"
|
||||||
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<GridPane alignment="CENTER">
|
||||||
<GridPane alignment="CENTER" prefHeight="90.0" prefWidth="539.0">
|
<columnConstraints>
|
||||||
<columnConstraints>
|
<ColumnConstraints halignment="RIGHT" minWidth="100.0" prefWidth="20.0"/>
|
||||||
<ColumnConstraints halignment="RIGHT" minWidth="100.0" prefWidth="20.0" />
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
</columnConstraints>
|
||||||
</columnConstraints>
|
<rowConstraints>
|
||||||
<rowConstraints>
|
<RowConstraints maxHeight="-Infinity" minHeight="20.0" percentHeight="25.0" prefHeight="60.0"
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
vgrow="ALWAYS"/>
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="-Infinity" minHeight="20.0" percentHeight="25.0" prefHeight="60.0"
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
vgrow="ALWAYS"/>
|
||||||
</rowConstraints>
|
<RowConstraints maxHeight="-Infinity" minHeight="20.0" percentHeight="25.0" prefHeight="60.0"
|
||||||
<children>
|
vgrow="ALWAYS"/>
|
||||||
<Label alignment="CENTER_RIGHT" text="Database" />
|
<RowConstraints maxHeight="-Infinity" minHeight="20.0" percentHeight="25.0" prefHeight="60.0"
|
||||||
<TextField fx:id="database" editable="false" promptText="Select" GridPane.columnIndex="1">
|
vgrow="ALWAYS"/>
|
||||||
<cursor>
|
</rowConstraints>
|
||||||
<Cursor fx:constant="HAND" />
|
<padding>
|
||||||
</cursor>
|
<Insets left="20.0" right="20.0"/>
|
||||||
<GridPane.margin>
|
</padding>
|
||||||
<Insets left="20.0" />
|
<Label alignment="CENTER_RIGHT" text="Database"/>
|
||||||
</GridPane.margin>
|
<TextField fx:id="database" editable="false" promptText="Select" GridPane.columnIndex="1">
|
||||||
</TextField>
|
<cursor>
|
||||||
<Label text="Key File" GridPane.rowIndex="1" />
|
<Cursor fx:constant="HAND"/>
|
||||||
<TextField fx:id="key" editable="false" promptText="Select (Optional)" GridPane.columnIndex="1"
|
</cursor>
|
||||||
GridPane.rowIndex="1">
|
<GridPane.margin>
|
||||||
<cursor>
|
<Insets left="20.0"/>
|
||||||
<Cursor fx:constant="HAND" />
|
</GridPane.margin>
|
||||||
</cursor>
|
</TextField>
|
||||||
<GridPane.margin>
|
<Label text="Key File" GridPane.rowIndex="1"/>
|
||||||
<Insets left="20.0" />
|
<TextField fx:id="key" editable="false" promptText="Select (Optional)" GridPane.columnIndex="1"
|
||||||
</GridPane.margin>
|
GridPane.rowIndex="1">
|
||||||
</TextField>
|
<cursor>
|
||||||
<TextField fx:id="wordlist" editable="false" promptText="Select" GridPane.columnIndex="1"
|
<Cursor fx:constant="HAND"/>
|
||||||
GridPane.rowIndex="2">
|
</cursor>
|
||||||
<cursor>
|
<GridPane.margin>
|
||||||
<Cursor fx:constant="HAND" />
|
<Insets left="20.0"/>
|
||||||
</cursor>
|
</GridPane.margin>
|
||||||
<GridPane.margin>
|
</TextField>
|
||||||
<Insets left="20.0" />
|
<Label alignment="CENTER_RIGHT" text="Word List Type" GridPane.rowIndex="2"/>
|
||||||
</GridPane.margin>
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1"
|
||||||
</TextField>
|
GridPane.rowIndex="2">
|
||||||
<Label text="Word List" GridPane.rowIndex="2" />
|
<RadioButton fx:id="wordlistPattern" text="From Pattern">
|
||||||
</children>
|
<HBox.margin>
|
||||||
<padding>
|
<Insets left="20.0"/>
|
||||||
<Insets left="20.0" right="20.0" />
|
</HBox.margin>
|
||||||
</padding>
|
<toggleGroup>
|
||||||
</GridPane>
|
<ToggleGroup fx:id="wordlistType"/>
|
||||||
<Button fx:id="crackButton" mnemonicParsing="false" prefHeight="25.0" prefWidth="111.0" text="Crack" />
|
</toggleGroup>
|
||||||
</children>
|
</RadioButton>
|
||||||
</VBox>
|
<RadioButton fx:id="wordlistFile" selected="true" text="From File"
|
||||||
</children>
|
toggleGroup="wordlistType">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets left="20.0"/>
|
||||||
|
</HBox.margin>
|
||||||
|
</RadioButton>
|
||||||
|
</HBox>
|
||||||
|
<TextField fx:id="wordlist" editable="false" promptText="Select" GridPane.columnIndex="1"
|
||||||
|
GridPane.rowIndex="3">
|
||||||
|
<cursor>
|
||||||
|
<Cursor fx:constant="HAND"/>
|
||||||
|
</cursor>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="20.0"/>
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
|
<Label text="Word List" GridPane.rowIndex="3"/>
|
||||||
|
</GridPane>
|
||||||
|
<Button fx:id="crackButton" mnemonicParsing="false" prefHeight="25.0" prefWidth="111.0" text="Crack"/>
|
||||||
|
</VBox>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|
|
@ -1,13 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
apply plugin: 'jacoco'
|
apply plugin: 'jacoco'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||||
|
implementation group: 'com.github.mifmif', name: 'generex', version: '1.0.2'
|
||||||
implementation group: 'org.linguafranca.pwdb', name: 'KeePassJava2-kdbx', version: '2.1.4'
|
implementation group: 'org.linguafranca.pwdb', name: 'KeePassJava2-kdbx', version: '2.1.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
jacocoTestReport {
|
jacocoTestReport {
|
||||||
reports {
|
reports {
|
||||||
xml.enabled true
|
xml.enabled = true
|
||||||
html.enabled true
|
html.enabled = true
|
||||||
html.destination file("${buildDir}/jacoco")
|
html.destination file("${buildDir}/jacoco")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib;
|
package com.wbrawner.keecrack.lib;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +22,7 @@ public enum Code {
|
||||||
ERROR_FILE_READ,
|
ERROR_FILE_READ,
|
||||||
ERROR_CRACKING_INTERRUPTED,
|
ERROR_CRACKING_INTERRUPTED,
|
||||||
ERROR_CRACKING_IN_PROGRESS,
|
ERROR_CRACKING_IN_PROGRESS,
|
||||||
ERROR_MISSING_DATABASE_FILE,
|
ERROR_INVALID_DATABASE_FILE,
|
||||||
ERROR_MISSING_WORD_LIST_FILE
|
ERROR_INVALID_WORD_LIST
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib;
|
package com.wbrawner.keecrack.lib;
|
||||||
|
|
||||||
import com.wbrawner.keecrack.lib.view.CrackingView;
|
import com.wbrawner.keecrack.lib.view.CrackingView;
|
||||||
import com.wbrawner.keecrack.lib.view.FormView;
|
import com.wbrawner.keecrack.lib.view.FormView;
|
||||||
|
import com.wbrawner.keecrack.lib.wordlist.WordList;
|
||||||
import org.linguafranca.pwdb.kdbx.KdbxCreds;
|
import org.linguafranca.pwdb.kdbx.KdbxCreds;
|
||||||
import org.linguafranca.pwdb.kdbx.stream_3_1.KdbxHeader;
|
import org.linguafranca.pwdb.kdbx.stream_3_1.KdbxHeader;
|
||||||
import org.linguafranca.pwdb.kdbx.stream_3_1.KdbxSerializer;
|
import org.linguafranca.pwdb.kdbx.stream_3_1.KdbxSerializer;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main class responsible for handling the brute forcing of the KeePass database. You do not contruct the
|
||||||
|
* KeeCrack instance directly, but rather call {@link #getInstance()}. To begin, you should set the form view and
|
||||||
|
* cracking view with {@link #setFormView(FormView)} and {@link #setCrackingView(CrackingView)} respectively. These
|
||||||
|
* views will be responsible for displaying information like error messages and status updates. The cracking will
|
||||||
|
* work without these, though it's highly recommended to set them prior to beginning. The database and wordlist must
|
||||||
|
* be set, while the key file is also optional. If either of the required parameters are missing, the
|
||||||
|
* {@link #attack()} operation will abort, sending either {@link Code#ERROR_INVALID_DATABASE_FILE} or
|
||||||
|
* {@link Code#ERROR_INVALID_WORD_LIST}, respectively. The word list can either be a pattern, in which case
|
||||||
|
* incremental guessing will take place, or a file, in which case each line of the file will be considered a password
|
||||||
|
* to guess. Use {@link #setWordListPattern(String)} or {@link #setWordListFile(File)} respectively to achieve the
|
||||||
|
* desired guess strategy. If you need to interrupt the attack for any reason, you can call {@link #abort} and the
|
||||||
|
* cracking will stop on the next guess, sending {@link Code#ERROR_CRACKING_INTERRUPTED} to the views, provided they
|
||||||
|
* are set. For each guess, {@link CrackingView#onPasswordGuess(String)} is called, in case you'd like to do
|
||||||
|
* something with the passwords that have already been attempted. Upon a successful password guess, the
|
||||||
|
* {@link CrackingView#onResult(String, int, Duration)} method will be called with the first parameter as the correct
|
||||||
|
* password. If the password cannot be guessed with the words provided, then the same method will be called but the
|
||||||
|
* first parameter will be null.
|
||||||
|
*/
|
||||||
public class KeeCrack {
|
public class KeeCrack {
|
||||||
private static final AtomicReference<KeeCrack> singleton = new AtomicReference<>(null);
|
private static final AtomicReference<KeeCrack> singleton = new AtomicReference<>(null);
|
||||||
private final Object keyFileLock = new Object();
|
private final Object keyFileLock = new Object();
|
||||||
private WeakReference<FormView> formView = new WeakReference<>(null);
|
|
||||||
private WeakReference<CrackingView> crackingView = new WeakReference<>(null);
|
|
||||||
private final AtomicBoolean isCracking = new AtomicBoolean(false);
|
private final AtomicBoolean isCracking = new AtomicBoolean(false);
|
||||||
private File databaseFile;
|
|
||||||
private File keyFile;
|
|
||||||
private File wordlistFile;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to abort cracking
|
* This is used to abort cracking
|
||||||
*/
|
*/
|
||||||
private final AtomicBoolean abort = new AtomicBoolean(false);
|
private final AtomicBoolean abort = new AtomicBoolean(false);
|
||||||
|
private final AtomicReference<FormView> formView = new AtomicReference<>(null);
|
||||||
|
private final AtomicReference<CrackingView> crackingView = new AtomicReference<>(null);
|
||||||
|
private File databaseFile;
|
||||||
|
private File keyFile;
|
||||||
|
private byte[] databaseBytes;
|
||||||
|
private byte[] keyBytes;
|
||||||
|
private WordList wordList;
|
||||||
private int guessCount = 0;
|
private int guessCount = 0;
|
||||||
|
|
||||||
private KeeCrack() {
|
private KeeCrack() {
|
||||||
|
@ -49,7 +85,7 @@ public class KeeCrack {
|
||||||
public void reset() {
|
public void reset() {
|
||||||
setDatabaseFile(null);
|
setDatabaseFile(null);
|
||||||
setKeyFile(null);
|
setKeyFile(null);
|
||||||
setWordlistFile(null);
|
setWordListFile(null);
|
||||||
setCrackingView(null);
|
setCrackingView(null);
|
||||||
setFormView(null);
|
setFormView(null);
|
||||||
}
|
}
|
||||||
|
@ -63,12 +99,12 @@ public class KeeCrack {
|
||||||
*/
|
*/
|
||||||
public void attack() {
|
public void attack() {
|
||||||
if (databaseFile == null || !databaseFile.exists() || !databaseFile.canRead()) {
|
if (databaseFile == null || !databaseFile.exists() || !databaseFile.canRead()) {
|
||||||
sendErrorCode(Code.ERROR_MISSING_DATABASE_FILE);
|
sendErrorCode(Code.ERROR_INVALID_DATABASE_FILE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordlistFile == null || !wordlistFile.exists() || !wordlistFile.canRead()) {
|
if (wordList == null) {
|
||||||
sendErrorCode(Code.ERROR_MISSING_WORD_LIST_FILE);
|
sendErrorCode(Code.ERROR_INVALID_WORD_LIST);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,56 +117,73 @@ public class KeeCrack {
|
||||||
guessCount = 0;
|
guessCount = 0;
|
||||||
Instant startTime = Instant.now();
|
Instant startTime = Instant.now();
|
||||||
|
|
||||||
try (BufferedReader wordReader = new BufferedReader(new FileReader(wordlistFile))) {
|
String line = null;
|
||||||
String line = null;
|
boolean haveCorrectPassword = false;
|
||||||
boolean haveCorrectPassword = false;
|
prepareByteArrays();
|
||||||
while (!haveCorrectPassword && (line = wordReader.readLine()) != null) {
|
while (!haveCorrectPassword && wordList.hasNext()) {
|
||||||
if (abort.get()) {
|
if (abort.get()) {
|
||||||
sendErrorCode(Code.ERROR_CRACKING_INTERRUPTED);
|
sendErrorCode(Code.ERROR_CRACKING_INTERRUPTED);
|
||||||
abort.set(false);
|
isCracking.set(false);
|
||||||
return;
|
abort.set(false);
|
||||||
}
|
return;
|
||||||
CrackingView view = crackingView.get();
|
|
||||||
if (view != null)
|
|
||||||
view.onPasswordGuess(line);
|
|
||||||
haveCorrectPassword = guessPassword(line);
|
|
||||||
}
|
}
|
||||||
|
line = wordList.nextWord();
|
||||||
|
try {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
crackingView.get().onPasswordGuess(line);
|
||||||
|
} catch (NullPointerException ignored) {
|
||||||
|
}
|
||||||
|
haveCorrectPassword = guessPassword(line);
|
||||||
|
}
|
||||||
|
|
||||||
CrackingView view = crackingView.get();
|
Duration duration = Duration.between(startTime, Instant.now());
|
||||||
if (view != null) {
|
String password = null;
|
||||||
Duration duration = Duration.between(startTime, Instant.now());
|
if (haveCorrectPassword) {
|
||||||
String password = null;
|
password = line;
|
||||||
if (haveCorrectPassword) {
|
}
|
||||||
password = line;
|
try {
|
||||||
}
|
//noinspection ConstantConditions
|
||||||
view.onResult(password, guessCount, duration);
|
crackingView.get().onResult(password, guessCount, duration);
|
||||||
|
} catch (NullPointerException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
isCracking.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
private void prepareByteArrays() {
|
||||||
|
databaseBytes = Utils.getFileBytes(databaseFile);
|
||||||
|
synchronized (keyFileLock) {
|
||||||
|
if (keyFile == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
keyBytes = Utils.getFileBytes(keyFile);
|
||||||
e.printStackTrace();
|
|
||||||
sendErrorCode(Code.ERROR_FILE_READ);
|
|
||||||
} finally {
|
|
||||||
isCracking.set(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean guessPassword(String password) {
|
private boolean guessPassword(String password) {
|
||||||
guessCount++;
|
guessCount++;
|
||||||
try (InputStream inputStream = new FileInputStream(databaseFile)) {
|
InputStream databaseInput = null;
|
||||||
|
InputStream keyFileInput = null;
|
||||||
|
try {
|
||||||
|
databaseInput = new ByteArrayInputStream(databaseBytes);
|
||||||
KdbxHeader databaseHeader = new KdbxHeader();
|
KdbxHeader databaseHeader = new KdbxHeader();
|
||||||
KdbxCreds credentials;
|
KdbxCreds credentials;
|
||||||
synchronized (keyFileLock) {
|
if (keyBytes == null || keyBytes.length == 0) {
|
||||||
if (keyFile == null) {
|
credentials = new KdbxCreds(password.getBytes());
|
||||||
credentials = new KdbxCreds(password.getBytes());
|
} else {
|
||||||
} else {
|
keyFileInput = new ByteArrayInputStream(keyBytes);
|
||||||
credentials = new KdbxCreds(password.getBytes(), new FileInputStream(keyFile));
|
credentials = new KdbxCreds(password.getBytes(), keyFileInput);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
KdbxSerializer.createUnencryptedInputStream(credentials, databaseHeader, inputStream);
|
KdbxSerializer.createUnencryptedInputStream(credentials, databaseHeader, databaseInput);
|
||||||
return true;
|
return true;
|
||||||
} catch (IllegalStateException ignored) {
|
} catch (IllegalStateException ignored) {
|
||||||
// This happens when an incorrect guess occurs. Expected behavior, so we ignore it
|
// This happens when an incorrect guess occurs. Expected behavior, so we ignore it
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
Utils.closeQuietly(databaseInput);
|
||||||
|
Utils.closeQuietly(keyFileInput);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -178,26 +231,63 @@ public class KeeCrack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getWordlistFile() {
|
public void setWordListFile(File wordlistFile) {
|
||||||
return wordlistFile;
|
String response = null;
|
||||||
}
|
if (wordlistFile == null) {
|
||||||
|
this.wordList = null;
|
||||||
public void setWordlistFile(File wordlistFile) {
|
} else {
|
||||||
this.wordlistFile = wordlistFile;
|
if (!wordlistFile.exists() || !wordlistFile.canRead()) {
|
||||||
|
try {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
crackingView.get().onError(Code.ERROR_INVALID_WORD_LIST);
|
||||||
|
} catch (NullPointerException ignored) {
|
||||||
|
}
|
||||||
|
this.wordList = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.wordList = new WordList(wordlistFile);
|
||||||
|
response = wordlistFile.getName();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
String response = (wordlistFile == null) ? null : wordlistFile.getName();
|
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
formView.get().onWordListFileSet(response);
|
formView.get().onWordListSet(response);
|
||||||
} catch (NullPointerException ignored) {
|
} catch (NullPointerException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setWordListPattern(String pattern) {
|
||||||
|
if (pattern == null) {
|
||||||
|
this.wordList = null;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.wordList = new WordList(pattern);
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
// This can be thrown if the user has entered an invalid regular expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
formView.get().onWordListSet(pattern);
|
||||||
|
} catch (NullPointerException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WordList getWordList() {
|
||||||
|
return this.wordList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWordListName() {
|
||||||
|
if (this.wordList == null)
|
||||||
|
return null;
|
||||||
|
return this.wordList.getName();
|
||||||
|
}
|
||||||
|
|
||||||
public void setFormView(FormView formView) {
|
public void setFormView(FormView formView) {
|
||||||
this.formView = new WeakReference<>(formView);
|
this.formView.set(formView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCrackingView(CrackingView crackingView) {
|
public void setCrackingView(CrackingView crackingView) {
|
||||||
this.crackingView = new WeakReference<>(crackingView);
|
this.crackingView.set(crackingView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCracking() {
|
public boolean isCracking() {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.wbrawner.keecrack.lib;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
static void closeQuietly(Closeable closeable) {
|
||||||
|
if (closeable == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
static byte[] getFileBytes(File file) {
|
||||||
|
byte[] fileBytes;
|
||||||
|
try (InputStream inputStream = new FileInputStream(file)) {
|
||||||
|
fileBytes = new byte[inputStream.available()];
|
||||||
|
inputStream.read(fileBytes);
|
||||||
|
return fileBytes;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new byte[]{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib.view;
|
package com.wbrawner.keecrack.lib.view;
|
||||||
|
|
||||||
import com.wbrawner.keecrack.lib.Code;
|
import com.wbrawner.keecrack.lib.Code;
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib.view;
|
package com.wbrawner.keecrack.lib.view;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
@ -5,14 +20,16 @@ import java.time.Duration;
|
||||||
public interface CrackingView extends BaseView {
|
public interface CrackingView extends BaseView {
|
||||||
/**
|
/**
|
||||||
* Called prior to each guess
|
* Called prior to each guess
|
||||||
|
*
|
||||||
* @param password The password that will be guessed
|
* @param password The password that will be guessed
|
||||||
*/
|
*/
|
||||||
void onPasswordGuess(final String password);
|
void onPasswordGuess(final String password);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the password has been successfully guessed, or there are no more passwords to guess
|
* Called when the password has been successfully guessed, or there are no more passwords to guess
|
||||||
* @param password The password, if successfully guessed, or null if no passwords were successful
|
*
|
||||||
* @param guessCount The number of passwords guessed
|
* @param password The password, if successfully guessed, or null if no passwords were successful
|
||||||
|
* @param guessCount The number of passwords guessed
|
||||||
* @param timeElapsed The time taken to guess the password
|
* @param timeElapsed The time taken to guess the password
|
||||||
*/
|
*/
|
||||||
void onResult(final String password, final int guessCount, final Duration timeElapsed);
|
void onResult(final String password, final int guessCount, final Duration timeElapsed);
|
||||||
|
|
|
@ -1,7 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib.view;
|
package com.wbrawner.keecrack.lib.view;
|
||||||
|
|
||||||
public interface FormView extends BaseView {
|
public interface FormView extends BaseView {
|
||||||
void onDatabaseFileSet(String name);
|
void onDatabaseFileSet(String name);
|
||||||
|
|
||||||
void onKeyFileSet(String name);
|
void onKeyFileSet(String name);
|
||||||
void onWordListFileSet(String name);
|
|
||||||
|
void onWordListSet(String name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.wbrawner.keecrack.lib.wordlist;
|
||||||
|
|
||||||
|
import com.mifmif.common.regex.Generex;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class WordList {
|
||||||
|
private WordListIterator iterator;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public WordList(File wordFile) {
|
||||||
|
java.util.Iterator<String> wordListIterator = null;
|
||||||
|
this.name = wordFile.getName();
|
||||||
|
try {
|
||||||
|
wordListIterator = FileUtils.lineIterator(wordFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator = new WordListIterator(wordListIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WordList(String pattern) {
|
||||||
|
Generex generex = new Generex(pattern);
|
||||||
|
this.name = pattern;
|
||||||
|
iterator = new WordListIterator(generex.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String nextWord() {
|
||||||
|
return iterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.wbrawner.keecrack.lib.wordlist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since Generex uses their own Iterator interface, that complicates things a bit for us, so this class serves as
|
||||||
|
* nothing more than a wrapper around both interfaces to prevent complication in other areas of the code;
|
||||||
|
*/
|
||||||
|
public class WordListIterator {
|
||||||
|
private com.mifmif.common.regex.util.Iterator generexIterator;
|
||||||
|
private java.util.Iterator<String> standardIterator;
|
||||||
|
|
||||||
|
WordListIterator(com.mifmif.common.regex.util.Iterator generexIterator) {
|
||||||
|
this.generexIterator = generexIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
WordListIterator(java.util.Iterator<String> standardIterator) {
|
||||||
|
this.standardIterator = standardIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (generexIterator != null) {
|
||||||
|
return generexIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return standardIterator != null && standardIterator.hasNext();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String next() {
|
||||||
|
if (generexIterator != null) {
|
||||||
|
return generexIterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (standardIterator != null) {
|
||||||
|
return standardIterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib;
|
package com.wbrawner.keecrack.lib;
|
||||||
|
|
||||||
import com.wbrawner.keecrack.lib.view.CrackingView;
|
import com.wbrawner.keecrack.lib.view.CrackingView;
|
||||||
|
@ -10,6 +25,7 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
@ -38,26 +54,30 @@ public class KeeCrackTest {
|
||||||
public void resetTest() {
|
public void resetTest() {
|
||||||
keeCrack.setDatabaseFile(new File("Database"));
|
keeCrack.setDatabaseFile(new File("Database"));
|
||||||
keeCrack.setKeyFile(new File("Keyfile"));
|
keeCrack.setKeyFile(new File("Keyfile"));
|
||||||
keeCrack.setWordlistFile(new File("WordList"));
|
keeCrack.setWordListPattern("Some pattern");
|
||||||
assertNotNull(keeCrack.getDatabaseFile());
|
assertNotNull(keeCrack.getDatabaseFile());
|
||||||
assertNotNull(keeCrack.getKeyFile());
|
assertNotNull(keeCrack.getKeyFile());
|
||||||
assertNotNull(keeCrack.getWordlistFile());
|
assertNotNull(keeCrack.getWordList());
|
||||||
keeCrack.reset();
|
keeCrack.reset();
|
||||||
assertNull(keeCrack.getDatabaseFile());
|
assertNull(keeCrack.getDatabaseFile());
|
||||||
assertNull(keeCrack.getKeyFile());
|
assertNull(keeCrack.getKeyFile());
|
||||||
assertNull(keeCrack.getWordlistFile());
|
assertNull(keeCrack.getWordList());
|
||||||
|
assertFalse(keeCrack.isCracking());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void abortTest() throws IOException {
|
public void abortTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.abort();
|
keeCrack.abort();
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(1)).onError(Code.ERROR_CRACKING_INTERRUPTED);
|
verify(mockCrackingView, times(1)).onError(Code.ERROR_CRACKING_INTERRUPTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This ensures that both views receive any errors sent
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void sendErrorTest() {
|
public void sendErrorTest() {
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
|
@ -67,6 +87,9 @@ public class KeeCrackTest {
|
||||||
verify(mockFormView, times(1)).onError(Code.ERROR_CRACKING_IN_PROGRESS);
|
verify(mockFormView, times(1)).onError(Code.ERROR_CRACKING_IN_PROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This ensures that the form view still receives the error if the cracking view is null
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void sendErrorWithoutCrackingViewTest() {
|
public void sendErrorWithoutCrackingViewTest() {
|
||||||
keeCrack.setFormView(mockFormView);
|
keeCrack.setFormView(mockFormView);
|
||||||
|
@ -74,6 +97,9 @@ public class KeeCrackTest {
|
||||||
verify(mockFormView, times(1)).onError(Code.ERROR_CRACKING_IN_PROGRESS);
|
verify(mockFormView, times(1)).onError(Code.ERROR_CRACKING_IN_PROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This ensures that the cracking view still receives the error if the form view is null
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void sendErrorWithoutFormViewTest() {
|
public void sendErrorWithoutFormViewTest() {
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
|
@ -83,10 +109,10 @@ public class KeeCrackTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void errorWithoutDatabaseFileTest() throws IOException {
|
public void errorWithoutDatabaseFileTest() throws IOException {
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(1)).onError(Code.ERROR_MISSING_DATABASE_FILE);
|
verify(mockCrackingView, times(1)).onError(Code.ERROR_INVALID_DATABASE_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -94,14 +120,14 @@ public class KeeCrackTest {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(1)).onError(Code.ERROR_MISSING_WORD_LIST_FILE);
|
verify(mockCrackingView, times(1)).onError(Code.ERROR_INVALID_WORD_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void guessCorrectPasswordTest() throws IOException {
|
public void guessCorrectPasswordFromFileTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(1)).onPasswordGuess("123456");
|
verify(mockCrackingView, times(1)).onPasswordGuess("123456");
|
||||||
|
@ -109,20 +135,42 @@ public class KeeCrackTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void guessCorrectPasswordAndKeyTest() throws IOException {
|
public void guessCorrectPasswordFromFileAndKeyTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456-key.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456-key.kdbx"));
|
||||||
keeCrack.setKeyFile(Utils.getKeyFile());
|
keeCrack.setKeyFile(Utils.getKeyFile());
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(1)).onPasswordGuess("123456");
|
verify(mockCrackingView, times(1)).onPasswordGuess("123456");
|
||||||
verify(mockCrackingView, times(1)).onResult(eq("123456"), eq(1), any(Duration.class));
|
verify(mockCrackingView, times(1)).onResult(eq("123456"), eq(1), any(Duration.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void keepGuessingUntilCorrectPasswordTest() throws IOException {
|
public void guessCorrectPasswordFromPatternTest() throws IOException {
|
||||||
|
keeCrack.setDatabaseFile(Utils.getDatabase("0000.kdbx"));
|
||||||
|
keeCrack.setWordListPattern("[0-9]{4}");
|
||||||
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
|
keeCrack.attack();
|
||||||
|
verify(mockCrackingView, times(1)).onPasswordGuess(anyString());
|
||||||
|
verify(mockCrackingView, times(1)).onResult(eq("0000"), eq(1), any(Duration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void guessCorrectPasswordFromPatternAndKeyTest() throws IOException {
|
||||||
|
keeCrack.setDatabaseFile(Utils.getDatabase("0000-key.kdbx"));
|
||||||
|
keeCrack.setWordListPattern("[0-9]{4}");
|
||||||
|
keeCrack.setKeyFile(Utils.getKeyFile());
|
||||||
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
|
keeCrack.attack();
|
||||||
|
verify(mockCrackingView, times(1)).onPasswordGuess(anyString());
|
||||||
|
verify(mockCrackingView, times(1)).onResult(eq("0000"), eq(1), any(Duration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepGuessingUntilCorrectPasswordFromFileTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("redwings.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("redwings.kdbx"));
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(2)).onPasswordGuess(anyString());
|
verify(mockCrackingView, times(2)).onPasswordGuess(anyString());
|
||||||
|
@ -130,20 +178,41 @@ public class KeeCrackTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void keepGuessingUntilCorrectPasswordAndKeyTest() throws IOException {
|
public void keepGuessingUntilCorrectPasswordFromFileAndKeyTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("redwings-key.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("redwings-key.kdbx"));
|
||||||
keeCrack.setKeyFile(Utils.getKeyFile());
|
keeCrack.setKeyFile(Utils.getKeyFile());
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(2)).onPasswordGuess(anyString());
|
verify(mockCrackingView, times(2)).onPasswordGuess(anyString());
|
||||||
verify(mockCrackingView, times(1)).onResult(eq("redwings"), eq(2), any(Duration.class));
|
verify(mockCrackingView, times(1)).onResult(eq("redwings"), eq(2), any(Duration.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepGuessingUntilCorrectPasswordFromPatternTest() throws IOException {
|
||||||
|
keeCrack.setDatabaseFile(Utils.getDatabase("ab.kdbx"));
|
||||||
|
keeCrack.setWordListPattern("[a-z]{2}");
|
||||||
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
|
keeCrack.attack();
|
||||||
|
verify(mockCrackingView, times(2)).onPasswordGuess(anyString());
|
||||||
|
verify(mockCrackingView, times(1)).onResult(eq("ab"), eq(2), any(Duration.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void keepGuessingUntilCorrectPasswordFromPatternAndKeyTest() throws IOException {
|
||||||
|
keeCrack.setDatabaseFile(Utils.getDatabase("ab-key.kdbx"));
|
||||||
|
keeCrack.setWordListPattern("[a-z]{2}");
|
||||||
|
keeCrack.setKeyFile(Utils.getKeyFile());
|
||||||
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
|
keeCrack.attack();
|
||||||
|
verify(mockCrackingView, times(2)).onPasswordGuess(anyString());
|
||||||
|
verify(mockCrackingView, times(1)).onResult(eq("ab"), eq(2), any(Duration.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void failToCrackPasswordTest() throws IOException {
|
public void failToCrackPasswordTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("invalid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("invalid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(3)).onPasswordGuess(anyString());
|
verify(mockCrackingView, times(3)).onPasswordGuess(anyString());
|
||||||
|
@ -154,7 +223,7 @@ public class KeeCrackTest {
|
||||||
public void failToCrackInvalidPasswordAndValidKeyTest() throws IOException {
|
public void failToCrackInvalidPasswordAndValidKeyTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456-key.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456-key.kdbx"));
|
||||||
keeCrack.setKeyFile(Utils.getKeyFile());
|
keeCrack.setKeyFile(Utils.getKeyFile());
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("invalid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("invalid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(3)).onPasswordGuess(anyString());
|
verify(mockCrackingView, times(3)).onPasswordGuess(anyString());
|
||||||
|
@ -165,7 +234,7 @@ public class KeeCrackTest {
|
||||||
public void failToCrackValidPasswordAndInvalidKeyTest() throws IOException {
|
public void failToCrackValidPasswordAndInvalidKeyTest() throws IOException {
|
||||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456-key.kdbx"));
|
keeCrack.setDatabaseFile(Utils.getDatabase("123456-key.kdbx"));
|
||||||
keeCrack.setKeyFile(Utils.getInvalidKeyFile());
|
keeCrack.setKeyFile(Utils.getInvalidKeyFile());
|
||||||
keeCrack.setWordlistFile(Utils.getWordList("valid-words.txt"));
|
keeCrack.setWordListFile(Utils.getWordList("valid-words.txt"));
|
||||||
keeCrack.setCrackingView(mockCrackingView);
|
keeCrack.setCrackingView(mockCrackingView);
|
||||||
keeCrack.attack();
|
keeCrack.attack();
|
||||||
verify(mockCrackingView, times(3)).onPasswordGuess(anyString());
|
verify(mockCrackingView, times(3)).onPasswordGuess(anyString());
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package com.wbrawner.keecrack.lib;
|
package com.wbrawner.keecrack.lib;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
BIN
keecrack-lib/src/test/resources/databases/0000-key.kdbx
Normal file
BIN
keecrack-lib/src/test/resources/databases/0000-key.kdbx
Normal file
Binary file not shown.
BIN
keecrack-lib/src/test/resources/databases/0000.kdbx
Normal file
BIN
keecrack-lib/src/test/resources/databases/0000.kdbx
Normal file
Binary file not shown.
BIN
keecrack-lib/src/test/resources/databases/ab-key.kdbx
Normal file
BIN
keecrack-lib/src/test/resources/databases/ab-key.kdbx
Normal file
Binary file not shown.
BIN
keecrack-lib/src/test/resources/databases/ab.kdbx
Normal file
BIN
keecrack-lib/src/test/resources/databases/ab.kdbx
Normal file
Binary file not shown.
|
@ -1 +1,16 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 William Brawner
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
include 'keecrack-lib', 'keecrack-gui', 'keecrack-cli'
|
include 'keecrack-lib', 'keecrack-gui', 'keecrack-cli'
|
Loading…
Reference in a new issue