150 lines
3.6 KiB
Java
150 lines
3.6 KiB
Java
import java.nio.ByteBuffer;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.security.MessageDigest;
|
|
import java.time.Instant;
|
|
import java.util.HexFormat;
|
|
import java.util.Map;
|
|
import java.util.Scanner;
|
|
import java.util.stream.Collectors;
|
|
import javax.crypto.Mac;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
public class AuthWithTOTP {
|
|
|
|
private static final byte[] INVALID_HASH =
|
|
"----------------------------------------------------------------".getBytes();
|
|
|
|
// hex-encoded: 3c2bc45f2de6568bb285aa1c6fcac1b6965cc770
|
|
// base32-encoded: HQV4IXZN4ZLIXMUFVIOG7SWBW2LFZR3Q
|
|
private static final byte[] K = new byte[] {
|
|
60,
|
|
43,
|
|
-60,
|
|
95,
|
|
45,
|
|
-26,
|
|
86,
|
|
-117,
|
|
-78,
|
|
-123,
|
|
-86,
|
|
28,
|
|
111,
|
|
-54,
|
|
-63,
|
|
-74,
|
|
-106,
|
|
92,
|
|
-57,
|
|
112,
|
|
};
|
|
|
|
public static void main(String[] args) {
|
|
// I changed it to a scanner cause my terminal had issues with the other thingy
|
|
try (Scanner sc = new Scanner(System.in)) {
|
|
Map<String, byte[]> passwd = Files.readAllLines(Path.of("passwd"))
|
|
.stream()
|
|
.filter(line -> line.indexOf(":") > 1 && line.length() > 3)
|
|
.collect(
|
|
Collectors.toMap(
|
|
line -> line.substring(0, line.indexOf(':')),
|
|
line ->
|
|
HexFormat.of().parseHex(
|
|
line.substring(line.indexOf(':') + 1)
|
|
)
|
|
)
|
|
);
|
|
|
|
System.out.println(
|
|
"Chocolate Factory SCADA Command Line Interface v2.2.144"
|
|
);
|
|
System.out.println();
|
|
System.out.println(
|
|
"Please, enter your authentication credentials."
|
|
);
|
|
System.out.println();
|
|
|
|
String username;
|
|
String password;
|
|
String totpCode;
|
|
|
|
long timeout = 500;
|
|
while (true) {
|
|
System.out.print("> Username: ");
|
|
username = sc.nextLine();
|
|
System.out.print("> Password: ");
|
|
password = sc.nextLine();
|
|
System.out.print("> TOTP Code: ");
|
|
totpCode = sc.nextLine();
|
|
|
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
byte[] encodedHash = digest.digest(password.getBytes());
|
|
|
|
// constant time comparison to prevent timing attacks
|
|
if (
|
|
MessageDigest.isEqual(
|
|
passwd.getOrDefault(username, INVALID_HASH),
|
|
encodedHash
|
|
)
|
|
) {
|
|
// Get the counter from the unix seconds
|
|
final var counter = (int) Math.floor(
|
|
Instant.now().getEpochSecond() / 30.0
|
|
);
|
|
|
|
// Compute the hmac
|
|
final var mac = Mac.getInstance("HmacSHA1");
|
|
mac.init(new SecretKeySpec(K, "HmacSHA1"));
|
|
mac.update(ByteBuffer.allocate(8).putLong(counter).array());
|
|
final var hmacResult = mac.doFinal();
|
|
|
|
// Do the truncating + modulo
|
|
int offset = hmacResult[19] & 0x0f;
|
|
int binaryCode =
|
|
((hmacResult[offset] & 0x7f) << 24) |
|
|
((hmacResult[offset + 1] & 0xff) << 16) |
|
|
((hmacResult[offset + 2] & 0xff) << 8) |
|
|
(hmacResult[offset + 3] & 0xff);
|
|
binaryCode = binaryCode % 1000000;
|
|
|
|
// Validate the code + padding
|
|
final var code = String.format("%06d", binaryCode);
|
|
if (!code.equals(totpCode)) {
|
|
System.out.println(
|
|
"Invalid username, password and/or TOTP code."
|
|
);
|
|
Thread.sleep(timeout);
|
|
timeout *= 2;
|
|
continue;
|
|
}
|
|
|
|
System.out.printf("Welcome %s!%n", username);
|
|
Thread.sleep(150);
|
|
break;
|
|
} else {
|
|
// exponential timeout to prevent brute force attacks
|
|
System.out.println(
|
|
"Invalid username, password and/or TOTP code."
|
|
);
|
|
Thread.sleep(timeout);
|
|
timeout *= 2;
|
|
}
|
|
}
|
|
|
|
printSystemStatus();
|
|
printSecretRecipe();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private static void printSystemStatus() throws Exception {
|
|
// SECRET
|
|
}
|
|
|
|
private static void printSecretRecipe() throws Exception {
|
|
// SECRET
|
|
}
|
|
}
|