Implement CrackerFactory
The idea with this was to get away from the singleton pattern, since it makes tests difficult and fragile. Dependency injection would have been an option too, but for simplicity's sake, I chose the factory method. There were also some strange errors on Windows that I've corrected, so the program should run on Windows now as well.
This commit is contained in:
parent
81db6720c9
commit
599e155490
8 changed files with 63 additions and 63 deletions
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,4 +1,5 @@
|
|||
#Sun Apr 15 14:52:35 CDT 2018
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
#Wed Apr 18 08:19:13 CDT 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.wbrawner.keecrack.cli;
|
||||
|
||||
import com.wbrawner.keecrack.lib.Code;
|
||||
import com.wbrawner.keecrack.lib.CrackerFactory;
|
||||
import com.wbrawner.keecrack.lib.KeeCrack;
|
||||
import com.wbrawner.keecrack.lib.view.CrackingView;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
|
@ -71,7 +72,7 @@ public class Main {
|
|||
Namespace res = parser.parseArgs(args);
|
||||
isVerbose = res.getBoolean("verbose");
|
||||
isIncremental = res.getBoolean("incremental");
|
||||
KeeCrack keeCrack = KeeCrack.getInstance();
|
||||
KeeCrack keeCrack = new CrackerFactory().getCracker(true);
|
||||
keeCrack.setCrackingView(new CLICrackingView());
|
||||
|
||||
String databasePath = res.getString("database");
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.wbrawner.keecrack.gui;
|
||||
|
||||
import com.wbrawner.keecrack.lib.Code;
|
||||
import com.wbrawner.keecrack.lib.CrackerFactory;
|
||||
import com.wbrawner.keecrack.lib.KeeCrack;
|
||||
import com.wbrawner.keecrack.lib.view.CrackingView;
|
||||
import javafx.application.Platform;
|
||||
|
@ -41,10 +42,11 @@ public class CrackingController implements Initializable, CrackingView {
|
|||
private Label timeElapsed;
|
||||
|
||||
private Stage stage;
|
||||
private KeeCrack keeCrack;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
final KeeCrack keeCrack = KeeCrack.getInstance();
|
||||
keeCrack = new CrackerFactory().getCracker(true);
|
||||
keeCrack.setCrackingView(this);
|
||||
new Thread(keeCrack::attack).start();
|
||||
}
|
||||
|
@ -88,7 +90,6 @@ public class CrackingController implements Initializable, CrackingView {
|
|||
}
|
||||
|
||||
private void onClose() {
|
||||
KeeCrack keeCrack = KeeCrack.getInstance();
|
||||
keeCrack.abort();
|
||||
keeCrack.setCrackingView(null);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.wbrawner.keecrack.gui;
|
||||
|
||||
import com.wbrawner.keecrack.lib.Code;
|
||||
import com.wbrawner.keecrack.lib.CrackerFactory;
|
||||
import com.wbrawner.keecrack.lib.KeeCrack;
|
||||
import com.wbrawner.keecrack.lib.view.FormView;
|
||||
import javafx.fxml.FXML;
|
||||
|
@ -48,7 +49,6 @@ public class MainController implements Initializable, FormView {
|
|||
private TextField wordlist;
|
||||
@FXML
|
||||
private Button crackButton;
|
||||
@FXML
|
||||
private ToggleGroup wordlistType;
|
||||
@FXML
|
||||
private RadioButton wordlistFile;
|
||||
|
@ -57,10 +57,10 @@ public class MainController implements Initializable, FormView {
|
|||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
keeCrack = KeeCrack.getInstance();
|
||||
keeCrack = new CrackerFactory().getCracker(true);
|
||||
keeCrack.setFormView(this);
|
||||
database.setOnMouseClicked(event -> {
|
||||
if (KeeCrack.getInstance().isCracking()) {
|
||||
if (keeCrack.isCracking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ public class MainController implements Initializable, FormView {
|
|||
});
|
||||
|
||||
key.setOnMouseClicked(event -> {
|
||||
if (KeeCrack.getInstance().isCracking()) {
|
||||
if (keeCrack.isCracking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ public class MainController implements Initializable, FormView {
|
|||
|
||||
crackButton.setOnMouseClicked(event -> {
|
||||
try {
|
||||
if (KeeCrack.getInstance().isCracking()) {
|
||||
if (keeCrack.isCracking()) {
|
||||
return;
|
||||
}
|
||||
Stage stage = new Stage();
|
||||
|
@ -107,13 +107,16 @@ public class MainController implements Initializable, FormView {
|
|||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
if (wordlistType == null)
|
||||
wordlistType = new ToggleGroup();
|
||||
wordlistFile.setToggleGroup(wordlistType);
|
||||
wordlistPattern.setToggleGroup(wordlistType);
|
||||
wordlistType.selectedToggleProperty().addListener((observableValue, oldToggle, newToggle) ->
|
||||
updateWordListHandler());
|
||||
|
||||
if (keeCrack.getDatabaseFile() != null)
|
||||
onDatabaseFileSet(keeCrack.getDatabaseFile().getName());
|
||||
if (keeCrack.getKeyFile() != null)
|
||||
onKeyFileSet(keeCrack.getKeyFile().getName());
|
||||
if (keeCrack.getWordListName() != null)
|
||||
onWordListSet(keeCrack.getWordListName());
|
||||
}
|
||||
|
@ -127,7 +130,7 @@ public class MainController implements Initializable, FormView {
|
|||
new FileChooser.ExtensionFilter(description, extensions)
|
||||
);
|
||||
Stage stage = new Stage();
|
||||
stage.setOnHidden(e -> KeeCrack.getInstance().setFormView(null));
|
||||
stage.setOnHidden(e -> keeCrack.setFormView(null));
|
||||
return fileChooser.showOpenDialog(stage);
|
||||
}
|
||||
|
||||
|
@ -137,7 +140,7 @@ public class MainController implements Initializable, FormView {
|
|||
wordlist.setEditable(false);
|
||||
wordlist.setCursor(Cursor.HAND);
|
||||
wordlist.setOnMouseClicked(event -> {
|
||||
if (KeeCrack.getInstance().isCracking()) {
|
||||
if (keeCrack.isCracking()) {
|
||||
return;
|
||||
}
|
||||
File wordlistFile = getFile("Text Files", "txt");
|
||||
|
|
|
@ -69,12 +69,8 @@
|
|||
<HBox.margin>
|
||||
<Insets left="20.0"/>
|
||||
</HBox.margin>
|
||||
<toggleGroup>
|
||||
<ToggleGroup fx:id="wordlistType"/>
|
||||
</toggleGroup>
|
||||
</RadioButton>
|
||||
<RadioButton fx:id="wordlistFile" selected="true" text="From File"
|
||||
toggleGroup="wordlistType">
|
||||
<RadioButton fx:id="wordlistFile" selected="true" text="From File">
|
||||
<HBox.margin>
|
||||
<Insets left="20.0"/>
|
||||
</HBox.margin>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Class responsible for acquiring a {@link KeeCrack} instance
|
||||
*/
|
||||
public class CrackerFactory {
|
||||
private static final Object singletonLock = new Object();
|
||||
private static KeeCrack singleton;
|
||||
|
||||
public KeeCrack getCracker(boolean asSingleton) {
|
||||
if (asSingleton) {
|
||||
synchronized (singletonLock) {
|
||||
if (singleton == null) {
|
||||
singleton = new KeeCrack();
|
||||
}
|
||||
return singleton;
|
||||
}
|
||||
}
|
||||
return new KeeCrack();
|
||||
}
|
||||
}
|
|
@ -31,11 +31,11 @@ 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
|
||||
* KeeCrack instance directly, but rather call {@link CrackerFactory#getCracker(boolean)}. 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
|
||||
|
@ -49,7 +49,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
* first parameter will be null.
|
||||
*/
|
||||
public class KeeCrack {
|
||||
private static final AtomicReference<KeeCrack> singleton = new AtomicReference<>(null);
|
||||
private final Object keyFileLock = new Object();
|
||||
private final AtomicBoolean isCracking = new AtomicBoolean(false);
|
||||
/**
|
||||
|
@ -65,27 +64,10 @@ public class KeeCrack {
|
|||
private WordList wordList;
|
||||
private int guessCount = 0;
|
||||
|
||||
private KeeCrack() {
|
||||
}
|
||||
|
||||
public static KeeCrack getInstance() {
|
||||
if (singleton.get() == null) {
|
||||
singleton.set(new KeeCrack());
|
||||
}
|
||||
|
||||
return singleton.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to reset the state of the KeeCrack instance. Note that you will need to set the views again after
|
||||
* calling this
|
||||
* To get an instance of the class, use the {@link CrackerFactory}
|
||||
*/
|
||||
public void reset() {
|
||||
setDatabaseFile(null);
|
||||
setKeyFile(null);
|
||||
setWordListFile(null);
|
||||
setCrackingView(null);
|
||||
setFormView(null);
|
||||
KeeCrack() {
|
||||
}
|
||||
|
||||
public void abort() {
|
||||
|
|
|
@ -21,13 +21,9 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
@ -41,30 +37,14 @@ public class KeeCrackTest {
|
|||
public void setUp() {
|
||||
mockCrackingView = mock(CrackingView.class);
|
||||
mockFormView = mock(FormView.class);
|
||||
keeCrack = KeeCrack.getInstance();
|
||||
keeCrack = new CrackerFactory().getCracker(false);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
keeCrack.reset();
|
||||
Utils.rmdir(Utils.getTmpDir());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetTest() {
|
||||
keeCrack.setDatabaseFile(new File("Database"));
|
||||
keeCrack.setKeyFile(new File("Keyfile"));
|
||||
keeCrack.setWordListPattern("Some pattern");
|
||||
assertNotNull(keeCrack.getDatabaseFile());
|
||||
assertNotNull(keeCrack.getKeyFile());
|
||||
assertNotNull(keeCrack.getWordList());
|
||||
keeCrack.reset();
|
||||
assertNull(keeCrack.getDatabaseFile());
|
||||
assertNull(keeCrack.getKeyFile());
|
||||
assertNull(keeCrack.getWordList());
|
||||
assertFalse(keeCrack.isCracking());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void abortTest() throws IOException {
|
||||
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));
|
||||
|
|
Loading…
Reference in a new issue