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 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 } }