diff --git a/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoder.java index b3991cb8c2..e7248776a5 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,9 +52,9 @@ public class Argon2PasswordEncoder implements PasswordEncoder { private static final int DEFAULT_PARALLELISM = 1; - private static final int DEFAULT_MEMORY = 1 << 12; + private static final int DEFAULT_MEMORY = 1 << 14; - private static final int DEFAULT_ITERATIONS = 3; + private static final int DEFAULT_ITERATIONS = 2; private final Log logger = LogFactory.getLog(getClass()); @@ -68,10 +68,24 @@ public class Argon2PasswordEncoder implements PasswordEncoder { private final BytesKeyGenerator saltGenerator; + /** + * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length + * of 32 bytes, parallelism of 1, memory cost of 1 << 12 and 3 iterations. + * @deprecated Use {@link #defaultsForSpringSecurity_v5_2()} instead + */ + @Deprecated public Argon2PasswordEncoder() { - this(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY, DEFAULT_ITERATIONS); + this(16, 32, 1, 1 << 12, 3); } + /** + * Constructs an Argon2 password encoder with the provided parameters. + * @param saltLength the salt length (in bytes) + * @param hashLength the hash length (in bytes) + * @param parallelism the parallelism + * @param memory the memory cost + * @param iterations the number of iterations + */ public Argon2PasswordEncoder(int saltLength, int hashLength, int parallelism, int memory, int iterations) { this.hashLength = hashLength; this.parallelism = parallelism; @@ -80,6 +94,29 @@ public class Argon2PasswordEncoder implements PasswordEncoder { this.saltGenerator = KeyGenerators.secureRandom(saltLength); } + /** + * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length + * of 32 bytes, parallelism of 1, memory cost of 1 << 12 and 3 iterations. + * @return the {@link Argon2PasswordEncoder} + * @since 5.8 + * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead + */ + @Deprecated + public static Argon2PasswordEncoder defaultsForSpringSecurity_v5_2() { + return new Argon2PasswordEncoder(16, 32, 1, 1 << 12, 3); + } + + /** + * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length + * of 32 bytes, parallelism of 1, memory cost of 1 << 14 and 2 iterations. + * @return the {@link Argon2PasswordEncoder} + * @since 5.8 + */ + public static Argon2PasswordEncoder defaultsForSpringSecurity_v5_8() { + return new Argon2PasswordEncoder(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY, + DEFAULT_ITERATIONS); + } + @Override public String encode(CharSequence rawPassword) { byte[] salt = this.saltGenerator.generateKey(); diff --git a/crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java b/crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java index eb27d4a058..67b1b9576f 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java +++ b/crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,13 +52,19 @@ public final class PasswordEncoderFactories { *
  • MD5 - {@code new MessageDigestPasswordEncoder("MD5")}
  • *
  • noop - * {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}
  • - *
  • pbkdf2 - {@link Pbkdf2PasswordEncoder}
  • - *
  • scrypt - {@link SCryptPasswordEncoder}
  • + *
  • pbkdf2 - {@link Pbkdf2PasswordEncoder#defaultsForSpringSecurity_v5_5()}
  • + *
  • pbkdf2@SpringSecurity_v5_8 - + * {@link Pbkdf2PasswordEncoder#defaultsForSpringSecurity_v5_8()}
  • + *
  • scrypt - {@link SCryptPasswordEncoder#defaultsForSpringSecurity_v4_1()}
  • + *
  • scrypt@SpringSecurity_v5_8 - + * {@link SCryptPasswordEncoder#defaultsForSpringSecurity_v5_8()}
  • *
  • SHA-1 - {@code new MessageDigestPasswordEncoder("SHA-1")}
  • *
  • SHA-256 - {@code new MessageDigestPasswordEncoder("SHA-256")}
  • *
  • sha256 - * {@link org.springframework.security.crypto.password.StandardPasswordEncoder}
  • - *
  • argon2 - {@link Argon2PasswordEncoder}
  • + *
  • argon2 - {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_2()}
  • + *
  • argon2@SpringSecurity_v5_8 - + * {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_8()}
  • * * @return the {@link PasswordEncoder} to use */ @@ -71,13 +77,16 @@ public final class PasswordEncoderFactories { encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder()); encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5")); encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()); - encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); - encoders.put("scrypt", new SCryptPasswordEncoder()); + encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()); + encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()); + encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()); + encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()); encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1")); encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256")); encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder()); - encoders.put("argon2", new Argon2PasswordEncoder()); + encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()); + encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()); return new DelegatingPasswordEncoder(encodingId, encoders); } diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoder.java index 4e5b292d4f..f26e74b1ba 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,8 +36,6 @@ import org.springframework.security.crypto.util.EncodingUtils; *
  • a configurable random salt value length (default is {@value #DEFAULT_SALT_LENGTH} * bytes)
  • *
  • a configurable number of iterations (default is {@value #DEFAULT_ITERATIONS})
  • - *
  • a configurable output hash width (default is {@value #DEFAULT_HASH_WIDTH} - * bits)
  • *
  • a configurable key derivation function (see {@link SecretKeyFactoryAlgorithm})
  • *
  • a configurable secret appended to the random salt (default is empty)
  • * @@ -50,72 +48,97 @@ import org.springframework.security.crypto.util.EncodingUtils; */ public class Pbkdf2PasswordEncoder implements PasswordEncoder { - private static final int DEFAULT_SALT_LENGTH = 8; + private static final int DEFAULT_SALT_LENGTH = 16; - private static final int DEFAULT_HASH_WIDTH = 256; + private static final SecretKeyFactoryAlgorithm DEFAULT_ALGORITHM = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256; - private static final int DEFAULT_ITERATIONS = 185000; + private static final int DEFAULT_HASH_WIDTH = 256; // SHA-256 + + private static final int DEFAULT_ITERATIONS = 310000; private final BytesKeyGenerator saltGenerator; private final byte[] secret; - private final int hashWidth; - private final int iterations; - private String algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(); + private String algorithm = DEFAULT_ALGORITHM.name(); + + private int hashWidth = DEFAULT_HASH_WIDTH; + + // @formatter:off + /* + The length of the hash should be derived from the hashing algorithm. + + For example: + SHA-1 - 160 bits (20 bytes) + SHA-256 - 256 bits (32 bytes) + SHA-512 - 512 bits (64 bytes) + + However, the original configuration for PBKDF2 was hashWidth=256 and algorithm=SHA-1, which is incorrect. + The default configuration has been updated to hashWidth=256 and algorithm=SHA-256 (see gh-10506). + In order to preserve backwards compatibility, the variable 'overrideHashWidth' has been introduced + to indicate usage of the deprecated constructor that honors the hashWidth parameter. + */ + // @formatter:on + private boolean overrideHashWidth = true; private boolean encodeHashAsBase64; /** * Constructs a PBKDF2 password encoder with no additional secret value. There will be - * a salt length of {@value #DEFAULT_SALT_LENGTH} bytes, {@value #DEFAULT_ITERATIONS} - * iterations and a hash width of {@value #DEFAULT_HASH_WIDTH} bits. The default is - * based upon aiming for .5 seconds to validate the password when this class was - * added. Users should tune password verification to their own systems. + * a salt length of 8 bytes, 185,000 iterations, SHA-1 algorithm and a hash length of + * 256 bits. The default is based upon aiming for .5 seconds to validate the password + * when this class was added. Users should tune password verification to their own + * systems. + * @deprecated Use {@link #defaultsForSpringSecurity_v5_5()} instead */ + @Deprecated public Pbkdf2PasswordEncoder() { this(""); } /** - * Constructs a standard password encoder with a secret value which is also included - * in the password hash. There will be a salt length of {@value #DEFAULT_SALT_LENGTH} - * bytes, {@value #DEFAULT_ITERATIONS} iterations and a hash width of - * {@value #DEFAULT_HASH_WIDTH} bits. + * Constructs a PBKDF2 password encoder with a secret value which is also included in + * the password hash. There will be a salt length of 8 bytes, 185,000 iterations, + * SHA-1 algorithm and a hash length of 256 bits. * @param secret the secret key used in the encoding process (should not be shared) + * @deprecated Use {@link #Pbkdf2PasswordEncoder(CharSequence, int, int, int)} instead */ + @Deprecated public Pbkdf2PasswordEncoder(CharSequence secret) { - this(secret, DEFAULT_SALT_LENGTH, DEFAULT_ITERATIONS, DEFAULT_HASH_WIDTH); + this(secret, 8); } /** - * Constructs a standard password encoder with a secret value as well as salt length. - * There will be {@value #DEFAULT_ITERATIONS} iterations and a hash width of - * {@value #DEFAULT_HASH_WIDTH} bits. + * Constructs a PBKDF2 password encoder with a secret value as well as salt length. + * There will be 185,000 iterations, SHA-1 algorithm and a hash length of 256 bits. * @param secret the secret * @param saltLength the salt length (in bytes) * @since 5.5 + * @deprecated Use {@link #Pbkdf2PasswordEncoder(CharSequence, int, int, int)} instead */ + @Deprecated public Pbkdf2PasswordEncoder(CharSequence secret, int saltLength) { - this(secret, saltLength, DEFAULT_ITERATIONS, DEFAULT_HASH_WIDTH); + this(secret, saltLength, 185000, 256); } /** - * Constructs a standard password encoder with a secret value as well as iterations - * and hash width. The salt length will be of {@value #DEFAULT_SALT_LENGTH} bytes. + * Constructs a PBKDF2 password encoder with a secret value as well as iterations and + * hash width. The salt length will be 8 bytes. * @param secret the secret * @param iterations the number of iterations. Users should aim for taking about .5 * seconds on their own system. * @param hashWidth the size of the hash (in bits) + * @deprecated Use {@link #Pbkdf2PasswordEncoder(CharSequence, int, int, int)} instead */ + @Deprecated public Pbkdf2PasswordEncoder(CharSequence secret, int iterations, int hashWidth) { - this(secret, DEFAULT_SALT_LENGTH, iterations, hashWidth); + this(secret, 8, iterations, hashWidth); } /** - * Constructs a standard password encoder with a secret value as well as salt length, + * Constructs a PBKDF2 password encoder with a secret value as well as salt length, * iterations and hash width. * @param secret the secret * @param saltLength the salt length (in bytes) @@ -123,12 +146,65 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder { * seconds on their own system. * @param hashWidth the size of the hash (in bits) * @since 5.5 + * @deprecated Use + * {@link #Pbkdf2PasswordEncoder(CharSequence, int, int, SecretKeyFactoryAlgorithm)} + * instead */ + @Deprecated public Pbkdf2PasswordEncoder(CharSequence secret, int saltLength, int iterations, int hashWidth) { this.secret = Utf8.encode(secret); this.saltGenerator = KeyGenerators.secureRandom(saltLength); this.iterations = iterations; this.hashWidth = hashWidth; + this.algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(); + this.overrideHashWidth = false; // Honor 'hashWidth' to preserve backwards + // compatibility + } + + /** + * Constructs a PBKDF2 password encoder with a secret value as well as salt length, + * iterations and algorithm. + * @param secret the secret + * @param saltLength the salt length (in bytes) + * @param iterations the number of iterations. Users should aim for taking about .5 + * seconds on their own system. + * @param secretKeyFactoryAlgorithm the algorithm to use + * @since 5.8 + */ + public Pbkdf2PasswordEncoder(CharSequence secret, int saltLength, int iterations, + SecretKeyFactoryAlgorithm secretKeyFactoryAlgorithm) { + this.secret = Utf8.encode(secret); + this.saltGenerator = KeyGenerators.secureRandom(saltLength); + this.iterations = iterations; + setAlgorithm(secretKeyFactoryAlgorithm); + } + + /** + * Constructs a PBKDF2 password encoder with no additional secret value. There will be + * a salt length of 8 bytes, 185,000 iterations, SHA-1 algorithm and a hash length of + * 256 bits. The default is based upon aiming for .5 seconds to validate the password + * when this class was added. Users should tune password verification to their own + * systems. + * @return the {@link Pbkdf2PasswordEncoder} + * @since 5.8 + * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead + */ + @Deprecated + public static Pbkdf2PasswordEncoder defaultsForSpringSecurity_v5_5() { + return new Pbkdf2PasswordEncoder("", 8, 185000, 256); + } + + /** + * Constructs a PBKDF2 password encoder with no additional secret value. There will be + * a salt length of 16 bytes, 310,000 iterations, SHA-256 algorithm and a hash length + * of 256 bits. The default is based upon aiming for .5 seconds to validate the + * password when this class was added. Users should tune password verification to + * their own systems. + * @return the {@link Pbkdf2PasswordEncoder} + * @since 5.8 + */ + public static Pbkdf2PasswordEncoder defaultsForSpringSecurity_v5_8() { + return new Pbkdf2PasswordEncoder("", DEFAULT_SALT_LENGTH, DEFAULT_ITERATIONS, DEFAULT_ALGORITHM); } /** @@ -153,6 +229,10 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder { catch (NoSuchAlgorithmException ex) { throw new IllegalArgumentException("Invalid algorithm '" + algorithmName + "'.", ex); } + if (this.overrideHashWidth) { + this.hashWidth = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.equals(secretKeyFactoryAlgorithm) ? 160 + : SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256.equals(secretKeyFactoryAlgorithm) ? 256 : 512; + } } /** diff --git a/crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java index 98bafd4be2..43ec6e3d1a 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,6 +58,16 @@ import org.springframework.security.crypto.password.PasswordEncoder; */ public class SCryptPasswordEncoder implements PasswordEncoder { + private static final int DEFAULT_CPU_COST = 65536; + + private static final int DEFAULT_MEMORY_COST = 8; + + private static final int DEFAULT_PARALLELISM = 1; + + private static final int DEFAULT_KEY_LENGTH = 32; + + private static final int DEFAULT_SALT_LENGTH = 16; + private final Log logger = LogFactory.getLog(getClass()); private final int cpuCost; @@ -70,14 +80,20 @@ public class SCryptPasswordEncoder implements PasswordEncoder { private final BytesKeyGenerator saltGenerator; + /** + * Constructs a SCrypt password encoder with cpu cost of 16,384, memory cost of 8, + * parallelization of 1, a key length of 32 and a salt length of 64 bytes. + * @deprecated Use {@link #defaultsForSpringSecurity_v4_1()} instead + */ + @Deprecated public SCryptPasswordEncoder() { this(16384, 8, 1, 32, 64); } /** - * Creates a new instance + * Constructs a SCrypt password encoder with the provided parameters. * @param cpuCost cpu cost of the algorithm (as defined in scrypt this is N). must be - * power of 2 greater than 1. Default is currently 16,384 or 2^14) + * power of 2 greater than 1. Default is currently 65,536 or 2^16) * @param memoryCost memory cost of the algorithm (as defined in scrypt this is r) * Default is currently 8. * @param parallelization the parallelization of the algorithm (as defined in scrypt @@ -86,7 +102,7 @@ public class SCryptPasswordEncoder implements PasswordEncoder { * @param keyLength key length for the algorithm (as defined in scrypt this is dkLen). * The default is currently 32. * @param saltLength salt length (as defined in scrypt this is the length of S). The - * default is currently 64. + * default is currently 16. */ public SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, int keyLength, int saltLength) { if (cpuCost <= 1) { @@ -116,6 +132,29 @@ public class SCryptPasswordEncoder implements PasswordEncoder { this.saltGenerator = KeyGenerators.secureRandom(saltLength); } + /** + * Constructs a SCrypt password encoder with cpu cost of 16,384, memory cost of 8, + * parallelization of 1, a key length of 32 and a salt length of 64 bytes. + * @return the {@link SCryptPasswordEncoder} + * @since 5.8 + * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead + */ + @Deprecated + public static SCryptPasswordEncoder defaultsForSpringSecurity_v4_1() { + return new SCryptPasswordEncoder(16384, 8, 1, 32, 64); + } + + /** + * Constructs a SCrypt password encoder with cpu cost of 65,536, memory cost of 8, + * parallelization of 1, a key length of 32 and a salt length of 16 bytes. + * @return the {@link SCryptPasswordEncoder} + * @since 5.8 + */ + public static SCryptPasswordEncoder defaultsForSpringSecurity_v5_8() { + return new SCryptPasswordEncoder(DEFAULT_CPU_COST, DEFAULT_MEMORY_COST, DEFAULT_PARALLELISM, DEFAULT_KEY_LENGTH, + DEFAULT_SALT_LENGTH); + } + @Override public String encode(CharSequence rawPassword) { return digest(rawPassword, this.saltGenerator.generateKey()); diff --git a/crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoderTests.java index 6aaa690491..9fb2a7412e 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public class Argon2PasswordEncoderTests { @Mock private BytesKeyGenerator keyGeneratorMock; - private Argon2PasswordEncoder encoder = new Argon2PasswordEncoder(); + private Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2(); @Test public void encodeDoesNotEqualPassword() { @@ -127,6 +127,15 @@ public class Argon2PasswordEncoderTests { "$argon2id$v=19$m=512,t=5,p=4$QUFBQUFBQUFBQUFBQUFBQQ$PNv4C3K50bz3rmON+LtFpdisD7ePieLNq+l5iUHgc1k"); } + @Test + public void encodeWhenUsingPredictableSaltWithDefaultsForSpringSecurity_v5_8ThenEqualTestHash() throws Exception { + this.encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8(); + injectPredictableSaltGen(); + String hash = this.encoder.encode("sometestpassword"); + assertThat(hash).isEqualTo( + "$argon2id$v=19$m=16384,t=2,p=1$QUFBQUFBQUFBQUFBQUFBQQ$zGt5MiNPSUOo4/7jBcJMayCPfcsLJ4c0WUxhwGDIYPw"); + } + @Test public void upgradeEncodingWhenSameEncodingThenFalse() { String hash = this.encoder.encode("password"); @@ -135,7 +144,7 @@ public class Argon2PasswordEncoderTests { @Test public void upgradeEncodingWhenSameStandardParamsThenFalse() { - Argon2PasswordEncoder newEncoder = new Argon2PasswordEncoder(); + Argon2PasswordEncoder newEncoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2(); String hash = this.encoder.encode("password"); assertThat(newEncoder.upgradeEncoding(hash)).isFalse(); } diff --git a/crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java b/crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java index ab2ca94a94..0b2dbd4a02 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,12 +75,24 @@ public class PasswordEncoderFactoriesTests { assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue(); } + @Test + public void matchesWhenPbkdf2SpringSecurity_v5_8ThenWorks() { + String encodedPassword = "{pbkdf2@SpringSecurity_v5_8}fefe5120467e5d4ccff442dbb2fa86d276262d97435c0c54e5eebced51ffd144fcb05eb53fea2677216c4f3250010006"; + assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue(); + } + @Test public void matchesWhenSCryptThenWorks() { String encodedPassword = "{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc="; assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue(); } + @Test + public void matchesWhenSCryptSpringSecurity_v5_8ThenWorks() { + String encodedPassword = "{scrypt@SpringSecurity_v5_8}$e0801$vSriIassJwvdNBF1vpSoCenqBxvpT4e+NcLKVsrOVpaZfyRfpUJ6KctkpmketuacWelLU5njpILXM9LLkMXLMw==$vIQQljL257HOcnumyiy1hJBGYHmoXgENIh+NkFvmrGY="; + assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue(); + } + @Test public void matchesWhenSHA1ThenWorks() { String encodedPassword = "{SHA-1}{6581QepZz2qd8jVrT2QYPVtK8DuM2n45dVslmc3UTWc=}4f31573948ddbfb8ac9dd80107dfad13fd8f2454"; @@ -105,4 +117,10 @@ public class PasswordEncoderFactoriesTests { assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue(); } + @Test + public void matchesWhenArgon2SpringSecurity_v5_8ThenWorks() { + String encodedPassword = "{argon2@SpringSecurity_v5_8}$argon2id$v=19$m=16384,t=2,p=1$v7fN5p91BQbdbA2HfdSPRg$MULpa02CO/6FKfqwuerCFvS7OhMxGFCKUOoWfzt86Rc"; + assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue(); + } + } diff --git a/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java index 1a8ea46f74..7a693d2fce 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -198,6 +198,14 @@ public class Pbkdf2PasswordEncoderTests { assertThat(this.encoderSalt16.matches(rawPassword, encodedPassword)).isTrue(); } + @Test + public void matchWhenDefaultsForSpringSecurity_v5_8ThenSuccess() { + Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8(); + String rawPassword = "password"; + String encodedPassword = "fefe5120467e5d4ccff442dbb2fa86d276262d97435c0c54e5eebced51ffd144fcb05eb53fea2677216c4f3250010006"; + assertThat(encoder.matches(rawPassword, encodedPassword)).isTrue(); + } + /** * Used to find the iteration count that takes .5 seconds. */ diff --git a/crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java index ad43ffc5e2..59a6d48e67 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ public class SCryptPasswordEncoderTests { @Test public void matches() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); String result = encoder.encode("password"); assertThat(result).isNotEqualTo("password"); assertThat(encoder.matches("password", result)).isTrue(); @@ -37,7 +37,7 @@ public class SCryptPasswordEncoderTests { @Test public void unicode() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); String result = encoder.encode("passw\u9292rd"); assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse(); assertThat(encoder.matches("passw\u9292rd", result)).isTrue(); @@ -45,7 +45,7 @@ public class SCryptPasswordEncoderTests { @Test public void notMatches() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); String result = encoder.encode("password"); assertThat(encoder.matches("bogus", result)).isFalse(); } @@ -60,15 +60,15 @@ public class SCryptPasswordEncoderTests { @Test public void differentPasswordHashes() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); String password = "secret"; assertThat(encoder.encode(password)).isNotEqualTo(encoder.encode(password)); } @Test public void samePasswordWithDifferentParams() { - SCryptPasswordEncoder oldEncoder = new SCryptPasswordEncoder(16384, 8, 1, 32, 64); - SCryptPasswordEncoder newEncoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder oldEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); + SCryptPasswordEncoder newEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8(); String password = "secret"; String oldEncodedPassword = oldEncoder.encode(password); assertThat(newEncoder.matches(password, oldEncodedPassword)).isTrue(); @@ -76,19 +76,19 @@ public class SCryptPasswordEncoderTests { @Test public void doesntMatchNullEncodedValue() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); assertThat(encoder.matches("password", null)).isFalse(); } @Test public void doesntMatchEmptyEncodedValue() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); assertThat(encoder.matches("password", "")).isFalse(); } @Test public void doesntMatchBogusEncodedValue() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); assertThat(encoder.matches("password", "012345678901234567890123456789")).isFalse(); } @@ -122,19 +122,19 @@ public class SCryptPasswordEncoderTests { @Test public void upgradeEncodingWhenNullThenFalse() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); assertThat(encoder.upgradeEncoding(null)).isFalse(); } @Test public void upgradeEncodingWhenEmptyThenFalse() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); assertThat(encoder.upgradeEncoding("")).isFalse(); } @Test public void upgradeEncodingWhenSameEncoderThenFalse() { - SCryptPasswordEncoder encoder = new SCryptPasswordEncoder(); + SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1(); String encoded = encoder.encode("password"); assertThat(encoder.upgradeEncoding(encoded)).isFalse(); } @@ -159,8 +159,8 @@ public class SCryptPasswordEncoderTests { @Test public void upgradeEncodingWhenInvalidInputThenException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new SCryptPasswordEncoder().upgradeEncoding("not-a-scrypt-password")); + assertThatIllegalArgumentException().isThrownBy( + () -> SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1().upgradeEncoding("not-a-scrypt-password")); } }