fix salt problem for yescrypt hashes in HashGenerator (#96)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #96 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
parent
fbd17a21e2
commit
8e02610679
@ -31,22 +31,37 @@ public final class HashGenerator {
|
||||
|
||||
public enum Algorithm {
|
||||
LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"),
|
||||
LINUX_YESCRYPT(LinuxEtcShadowHashGenerator::hash, "y"),
|
||||
LINUX_YESCRYPT(LinuxEtcShadowHashGenerator::hash, "y", "j9T$") {
|
||||
@Override
|
||||
String enrichedSalt(final String salt) {
|
||||
return prefix + "$" + (salt.startsWith(optionalParam) ? salt : optionalParam + salt);
|
||||
}
|
||||
},
|
||||
MYSQL_NATIVE(MySQLNativePasswordHashGenerator::hash, "*"),
|
||||
SCRAM_SHA256(PostgreSQLScramSHA256::hash, "SCRAM-SHA-256");
|
||||
|
||||
final BiFunction<HashGenerator, String, String> implementation;
|
||||
final String prefix;
|
||||
final String optionalParam;
|
||||
|
||||
Algorithm(BiFunction<HashGenerator, String, String> implementation, final String prefix) {
|
||||
Algorithm(BiFunction<HashGenerator, String, String> implementation, final String prefix, final String optionalParam) {
|
||||
this.implementation = implementation;
|
||||
this.prefix = prefix;
|
||||
this.optionalParam = optionalParam;
|
||||
}
|
||||
|
||||
Algorithm(BiFunction<HashGenerator, String, String> implementation, final String prefix) {
|
||||
this(implementation, prefix, null);
|
||||
}
|
||||
|
||||
static Algorithm byPrefix(final String prefix) {
|
||||
return Arrays.stream(Algorithm.values()).filter(a -> a.prefix.equals(prefix)).findAny()
|
||||
.orElseThrow(() -> new IllegalArgumentException("unknown hash algorithm: '" + prefix + "'"));
|
||||
}
|
||||
|
||||
String enrichedSalt(final String salt) {
|
||||
return prefix + "$" + salt;
|
||||
}
|
||||
}
|
||||
|
||||
private final Algorithm algorithm;
|
||||
@ -60,7 +75,7 @@ public final class HashGenerator {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public static void enableChouldBeHash(final boolean enable) {
|
||||
public static void enableCouldBeHash(final boolean enable) {
|
||||
couldBeHashEnabled = enable;
|
||||
}
|
||||
|
||||
@ -73,7 +88,11 @@ public final class HashGenerator {
|
||||
throw new IllegalStateException("no password given");
|
||||
}
|
||||
|
||||
return algorithm.implementation.apply(this, plaintextPassword);
|
||||
final var hash = algorithm.implementation.apply(this, plaintextPassword);
|
||||
if (hash.length() < plaintextPassword.length()) {
|
||||
throw new AssertionError("generated hash too short: " + hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public String hashIfNotYetHashed(final String plaintextPasswordOrHash) {
|
||||
@ -102,4 +121,10 @@ public final class HashGenerator {
|
||||
}
|
||||
return withSalt(stringBuilder.toString());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(
|
||||
HashGenerator.using(Algorithm.LINUX_YESCRYPT).withRandomSalt().hash("my plaintext domain transfer passphrase")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class LinuxEtcShadowHashGenerator {
|
||||
throw new IllegalStateException("no salt given");
|
||||
}
|
||||
|
||||
return NativeCryptLibrary.INSTANCE.crypt(payload, "$" + generator.getAlgorithm().prefix + "$" + generator.getSalt());
|
||||
return NativeCryptLibrary.INSTANCE.crypt(payload, "$" + generator.getAlgorithm().enrichedSalt(generator.getSalt()));
|
||||
}
|
||||
|
||||
public static void verify(final String givenHash, final String payload) {
|
||||
@ -22,8 +22,8 @@ public class LinuxEtcShadowHashGenerator {
|
||||
|
||||
final var algorithm = HashGenerator.Algorithm.byPrefix(parts[1]);
|
||||
final var salt = parts.length == 4 ? parts[2] : parts[2] + "$" + parts[3];
|
||||
final var calcualatedHash = HashGenerator.using(algorithm).withSalt(salt).hash(payload);
|
||||
if (!calcualatedHash.equals(givenHash)) {
|
||||
final var calculatedHash = HashGenerator.using(algorithm).withSalt(salt).hash(payload);
|
||||
if (!calculatedHash.equals(givenHash)) {
|
||||
throw new IllegalArgumentException("invalid password");
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.nio.charset.Charset;
|
||||
import java.util.Base64;
|
||||
|
||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.LINUX_SHA512;
|
||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.LINUX_YESCRYPT;
|
||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.MYSQL_NATIVE;
|
||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.SCRAM_SHA256;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -57,6 +58,18 @@ class HashGeneratorUnitTest {
|
||||
assertThat(throwable).hasMessage("invalid password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesLinuxSha512PasswordHash() {
|
||||
final var hash = HashGenerator.using(LINUX_SHA512).withSalt("ooei1HK6JXVaI7KC").hash(GIVEN_PASSWORD);
|
||||
assertThat(hash).isEqualTo(GIVEN_LINUX_GENERATED_SHA512_HASH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesLinuxYescriptPasswordHash() {
|
||||
final var hash = HashGenerator.using(LINUX_YESCRYPT).withSalt("wgYACPmBXvlMg2MzeZA0p1").hash(GIVEN_PASSWORD);
|
||||
assertThat(hash).isEqualTo(GIVEN_LINUX_GENERATED_YESCRYPT_HASH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesMySqlNativePasswordHash() {
|
||||
final var hash = HashGenerator.using(MYSQL_NATIVE).hash("Test1234");
|
||||
|
Loading…
Reference in New Issue
Block a user