Skip to content

Commit b1a7a45

Browse files
committed
Password filter generator / lookup improvements
1 parent 5ddd327 commit b1a7a45

File tree

2 files changed

+43
-38
lines changed

2 files changed

+43
-38
lines changed

src/main/java/org/fastfilter/tools/BuildFilterFile.java

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package org.fastfilter.tools;
22

33
import java.io.BufferedInputStream;
4-
import java.io.File;
5-
import java.io.FileInputStream;
4+
import java.io.BufferedOutputStream;
5+
import java.io.DataOutputStream;
6+
import java.io.FileOutputStream;
67
import java.io.IOException;
78
import java.io.InputStreamReader;
89
import java.io.LineNumberReader;
9-
import java.io.RandomAccessFile;
1010
import java.nio.charset.Charset;
1111
import java.util.ArrayList;
1212

@@ -19,23 +19,19 @@ public class BuildFilterFile {
1919

2020
public static void main(String... args) throws IOException {
2121
if (args.length != 1) {
22-
System.out.println("Usage: java " + BuildFilterFile.class.getName() + " <textFile>\n"
23-
+ "Builds a .filter file from a text file that contains SHA-1 hashes and counts.\n"
24-
+ "You can get the hash file from https://haveibeenpwned.com/passwords\n"
25-
+ "It needs to be a list of SHA-1 hashes, ordered by hash, line format <hash>:<count>.");
22+
System.out.println("Usage: java " + BuildFilterFile.class.getName() + " <filterFile>\n"
23+
+ "Reads a text file from standard in and writes a filter file.\n"
24+
+ "The input is a text file that contains sorted SHA-1 hashes and counts.\n"
25+
+ "You may get the file from https://haveibeenpwned.com/passwords.");
2626
return;
2727
}
28-
String textFile = args[0];
29-
String filterFileName = textFile + ".filter";
28+
LineNumberReader lineReader = new LineNumberReader(
29+
new InputStreamReader(new BufferedInputStream(System.in), Charset.forName("LATIN1")));
3030
long start = System.nanoTime();
31-
LineNumberReader lineReader = new LineNumberReader(new InputStreamReader(
32-
new BufferedInputStream(new FileInputStream(textFile)), Charset.forName("LATIN1")));
33-
new File(filterFileName).delete();
34-
RandomAccessFile out = new RandomAccessFile(filterFileName, "rw");
31+
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(args[0])));
32+
long outPos = 0;
3533
int lines = 0;
3634
long[] segmentStarts = new long[1 << SEGMENT_BITS];
37-
// header
38-
out.write(new byte[8 << SEGMENT_BITS]);
3935
int currentSegment = 0;
4036
long lastHash = 0;
4137
ArrayList<Long> keys = new ArrayList<Long>();
@@ -66,8 +62,10 @@ public static void main(String... args) throws IOException {
6662
}
6763
int segment = (int) (key >>> (64 - SEGMENT_BITS));
6864
if (segment != currentSegment) {
69-
segmentStarts[currentSegment] = out.getFilePointer();
70-
out.write(getSegment(keys));
65+
segmentStarts[currentSegment] = outPos;
66+
byte[] data = getSegment(keys);
67+
out.write(data);
68+
outPos += data.length;
7169
keys.clear();
7270
currentSegment = segment;
7371
}
@@ -77,11 +75,10 @@ public static void main(String... args) throws IOException {
7775
}
7876
keys.add(key);
7977
}
80-
segmentStarts[currentSegment] = out.getFilePointer();
78+
segmentStarts[currentSegment] = outPos;
8179
out.write(getSegment(keys));
8280
lineReader.close();
83-
out.seek(0);
84-
for(long s : segmentStarts) {
81+
for (long s : segmentStarts) {
8582
out.writeLong(s);
8683
}
8784
out.close();

src/main/java/org/fastfilter/tools/PasswordLookup.java

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import java.io.BufferedInputStream;
44
import java.io.Console;
55
import java.io.DataInputStream;
6+
import java.io.File;
67
import java.io.FileInputStream;
7-
import java.io.RandomAccessFile;
88
import java.nio.charset.Charset;
99
import java.security.MessageDigest;
1010
import java.util.Scanner;
@@ -15,11 +15,17 @@ public class PasswordLookup {
1515

1616
public static void main(String... args) throws Exception {
1717
if (args.length != 1) {
18-
System.out.println("Usage: java " + PasswordLookup.class.getName() + " <filterFileName> \n"
18+
System.out.println("Usage: java " + PasswordLookup.class.getName() + " <filterFile> \n"
1919
+ "Requires a filter file generated by " + BuildFilterFile.class.getName());
2020
return;
2121
}
22-
String filterFileName = args[0];
22+
String filterFile = args[0];
23+
DataInputStream in = openFile(filterFile, new File(filterFile).length() - (8 << BuildFilterFile.SEGMENT_BITS));
24+
long[] segmentStarts = new long[1 << BuildFilterFile.SEGMENT_BITS];
25+
for (int i = 0; i < segmentStarts.length; i++) {
26+
segmentStarts[i] = in.readLong();
27+
}
28+
in.close();
2329
Scanner scanner = new Scanner(System.in);
2430
while (true) {
2531
Console console = System.console();
@@ -33,13 +39,14 @@ public static void main(String... args) throws Exception {
3339
if (password.length() == 0) {
3440
break;
3541
}
36-
testPassword(filterFileName, password);
42+
testPassword(filterFile, segmentStarts, password);
3743
}
3844
scanner.close();
3945
}
4046

41-
private static void testPassword(String filterFileName, String password) throws Exception {
42-
// it's unclear which character set was used; ASCII gave good results, as umlauts are converted to '?'
47+
private static void testPassword(String filterFile, long[] segmentStarts, String password) throws Exception {
48+
// it's unclear which character set was used; ASCII gave good results, as
49+
// umlauts are converted to '?'
4350
byte[] passwordBytes = password.getBytes(Charset.forName("ASCII"));
4451
MessageDigest md = MessageDigest.getInstance("SHA-1");
4552
byte[] sha1 = md.digest(passwordBytes);
@@ -49,19 +56,8 @@ private static void testPassword(String filterFileName, String password) throws
4956
}
5057
// set the lowest bit to 0
5158
long key = hash ^ (hash & 1);
52-
RandomAccessFile f = new RandomAccessFile(filterFileName, "r");
5359
int segment = (int) (key >>> (64 - BuildFilterFile.SEGMENT_BITS));
54-
f.seek(segment * 8);
55-
long skip = f.readLong();
56-
f.close();
57-
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(filterFileName)));
58-
while (skip > 0) {
59-
long skipped = in.skip(skip);
60-
if (skipped <= 0) {
61-
break;
62-
}
63-
skip -= skipped;
64-
}
60+
DataInputStream in = openFile(filterFile, segmentStarts[segment]);
6561
XorPlus8 filter = new XorPlus8(in);
6662
in.close();
6763
if (filter.mayContain(key)) {
@@ -73,4 +69,16 @@ private static void testPassword(String filterFileName, String password) throws
7369
}
7470
}
7571

72+
static DataInputStream openFile(String fileName, long skip) throws Exception {
73+
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
74+
while (skip > 0) {
75+
long skipped = in.skip(skip);
76+
if (skipped <= 0) {
77+
break;
78+
}
79+
skip -= skipped;
80+
}
81+
return in;
82+
}
83+
7684
}

0 commit comments

Comments
 (0)