Skip to content

Commit f841900

Browse files
committed
Update default configuration for SCryptPasswordEncoder
The recommended minimums for scrypt, as per OWASP Cheat Sheet Series (https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html), are: Use scrypt with a minimum CPU/memory cost parameter of (2^16), a minimum block size of 8 (1024 bytes), and a parallelization parameter of 1. Previous default configuration: cpuCost=16384, memoryCost=8, parallelism=1 New default configuration: cpuCost=65536, memoryCost=8, parallelism=1 The default salt length was also updated from 64 to 16. Issue gh-10506
1 parent 2ea62d0 commit f841900

File tree

4 files changed

+69
-21
lines changed

4 files changed

+69
-21
lines changed

crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ private PasswordEncoderFactories() {
5353
* <li>noop -
5454
* {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}</li>
5555
* <li>pbkdf2 - {@link Pbkdf2PasswordEncoder}</li>
56-
* <li>scrypt - {@link SCryptPasswordEncoder}</li>
56+
* <li>scrypt - {@link SCryptPasswordEncoder#defaultsForSpringSecurity_v4_1()}</li>
57+
* <li>scrypt@SpringSecurity_v5_8 -
58+
* {@link SCryptPasswordEncoder#defaultsForSpringSecurity_v5_8()}</li>
5759
* <li>SHA-1 - {@code new MessageDigestPasswordEncoder("SHA-1")}</li>
5860
* <li>SHA-256 - {@code new MessageDigestPasswordEncoder("SHA-256")}</li>
5961
* <li>sha256 -
@@ -74,7 +76,8 @@ public static PasswordEncoder createDelegatingPasswordEncoder() {
7476
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
7577
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
7678
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
77-
encoders.put("scrypt", new SCryptPasswordEncoder());
79+
encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
80+
encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
7881
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
7982
encoders.put("SHA-256",
8083
new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));

crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -58,6 +58,16 @@
5858
*/
5959
public class SCryptPasswordEncoder implements PasswordEncoder {
6060

61+
private static final int DEFAULT_CPU_COST = 65536;
62+
63+
private static final int DEFAULT_MEMORY_COST = 8;
64+
65+
private static final int DEFAULT_PARALLELISM = 1;
66+
67+
private static final int DEFAULT_KEY_LENGTH = 32;
68+
69+
private static final int DEFAULT_SALT_LENGTH = 16;
70+
6171
private final Log logger = LogFactory.getLog(getClass());
6272

6373
private final int cpuCost;
@@ -70,14 +80,20 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
7080

7181
private final BytesKeyGenerator saltGenerator;
7282

83+
/**
84+
* Constructs a SCrypt password encoder with cpu cost of 16,384, memory cost of 8,
85+
* parallelization of 1, a key length of 32 and a salt length of 64 bytes.
86+
* @deprecated Use {@link #defaultsForSpringSecurity_v4_1()} instead
87+
*/
88+
@Deprecated
7389
public SCryptPasswordEncoder() {
7490
this(16384, 8, 1, 32, 64);
7591
}
7692

7793
/**
78-
* Creates a new instance
94+
* Constructs a SCrypt password encoder with the provided parameters.
7995
* @param cpuCost cpu cost of the algorithm (as defined in scrypt this is N). must be
80-
* power of 2 greater than 1. Default is currently 16,384 or 2^14)
96+
* power of 2 greater than 1. Default is currently 65,536 or 2^16)
8197
* @param memoryCost memory cost of the algorithm (as defined in scrypt this is r)
8298
* Default is currently 8.
8399
* @param parallelization the parallelization of the algorithm (as defined in scrypt
@@ -86,7 +102,7 @@ public SCryptPasswordEncoder() {
86102
* @param keyLength key length for the algorithm (as defined in scrypt this is dkLen).
87103
* The default is currently 32.
88104
* @param saltLength salt length (as defined in scrypt this is the length of S). The
89-
* default is currently 64.
105+
* default is currently 16.
90106
*/
91107
public SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, int keyLength, int saltLength) {
92108
if (cpuCost <= 1) {
@@ -116,6 +132,29 @@ public SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, i
116132
this.saltGenerator = KeyGenerators.secureRandom(saltLength);
117133
}
118134

135+
/**
136+
* Constructs a SCrypt password encoder with cpu cost of 16,384, memory cost of 8,
137+
* parallelization of 1, a key length of 32 and a salt length of 64 bytes.
138+
* @return the {@link SCryptPasswordEncoder}
139+
* @since 5.8
140+
* @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead
141+
*/
142+
@Deprecated
143+
public static SCryptPasswordEncoder defaultsForSpringSecurity_v4_1() {
144+
return new SCryptPasswordEncoder(16384, 8, 1, 32, 64);
145+
}
146+
147+
/**
148+
* Constructs a SCrypt password encoder with cpu cost of 65,536, memory cost of 8,
149+
* parallelization of 1, a key length of 32 and a salt length of 16 bytes.
150+
* @return the {@link SCryptPasswordEncoder}
151+
* @since 5.8
152+
*/
153+
public static SCryptPasswordEncoder defaultsForSpringSecurity_v5_8() {
154+
return new SCryptPasswordEncoder(DEFAULT_CPU_COST, DEFAULT_MEMORY_COST, DEFAULT_PARALLELISM, DEFAULT_KEY_LENGTH,
155+
DEFAULT_SALT_LENGTH);
156+
}
157+
119158
@Override
120159
public String encode(CharSequence rawPassword) {
121160
return digest(rawPassword, this.saltGenerator.generateKey());

crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ public void matchesWhenSCryptThenWorks() {
8181
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
8282
}
8383

84+
@Test
85+
public void matchesWhenSCryptSpringSecurity_v5_8ThenWorks() {
86+
String encodedPassword = "{scrypt@SpringSecurity_v5_8}$e0801$vSriIassJwvdNBF1vpSoCenqBxvpT4e+NcLKVsrOVpaZfyRfpUJ6KctkpmketuacWelLU5njpILXM9LLkMXLMw==$vIQQljL257HOcnumyiy1hJBGYHmoXgENIh+NkFvmrGY=";
87+
assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
88+
}
89+
8490
@Test
8591
public void matchesWhenSHA1ThenWorks() {
8692
String encodedPassword = "{SHA-1}{6581QepZz2qd8jVrT2QYPVtK8DuM2n45dVslmc3UTWc=}4f31573948ddbfb8ac9dd80107dfad13fd8f2454";

crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,23 +29,23 @@ public class SCryptPasswordEncoderTests {
2929

3030
@Test
3131
public void matches() {
32-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
32+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
3333
String result = encoder.encode("password");
3434
assertThat(result).isNotEqualTo("password");
3535
assertThat(encoder.matches("password", result)).isTrue();
3636
}
3737

3838
@Test
3939
public void unicode() {
40-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
40+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
4141
String result = encoder.encode("passw\u9292rd");
4242
assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
4343
assertThat(encoder.matches("passw\u9292rd", result)).isTrue();
4444
}
4545

4646
@Test
4747
public void notMatches() {
48-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
48+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
4949
String result = encoder.encode("password");
5050
assertThat(encoder.matches("bogus", result)).isFalse();
5151
}
@@ -60,35 +60,35 @@ public void customParameters() {
6060

6161
@Test
6262
public void differentPasswordHashes() {
63-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
63+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
6464
String password = "secret";
6565
assertThat(encoder.encode(password)).isNotEqualTo(encoder.encode(password));
6666
}
6767

6868
@Test
6969
public void samePasswordWithDifferentParams() {
70-
SCryptPasswordEncoder oldEncoder = new SCryptPasswordEncoder(16384, 8, 1, 32, 64);
71-
SCryptPasswordEncoder newEncoder = new SCryptPasswordEncoder();
70+
SCryptPasswordEncoder oldEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
71+
SCryptPasswordEncoder newEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
7272
String password = "secret";
7373
String oldEncodedPassword = oldEncoder.encode(password);
7474
assertThat(newEncoder.matches(password, oldEncodedPassword)).isTrue();
7575
}
7676

7777
@Test
7878
public void doesntMatchNullEncodedValue() {
79-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
79+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
8080
assertThat(encoder.matches("password", null)).isFalse();
8181
}
8282

8383
@Test
8484
public void doesntMatchEmptyEncodedValue() {
85-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
85+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
8686
assertThat(encoder.matches("password", "")).isFalse();
8787
}
8888

8989
@Test
9090
public void doesntMatchBogusEncodedValue() {
91-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
91+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
9292
assertThat(encoder.matches("password", "012345678901234567890123456789")).isFalse();
9393
}
9494

@@ -122,19 +122,19 @@ public void invalidKeyLengthParameter() {
122122

123123
@Test
124124
public void upgradeEncodingWhenNullThenFalse() {
125-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
125+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
126126
assertThat(encoder.upgradeEncoding(null)).isFalse();
127127
}
128128

129129
@Test
130130
public void upgradeEncodingWhenEmptyThenFalse() {
131-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
131+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
132132
assertThat(encoder.upgradeEncoding("")).isFalse();
133133
}
134134

135135
@Test
136136
public void upgradeEncodingWhenSameEncoderThenFalse() {
137-
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
137+
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();
138138
String encoded = encoder.encode("password");
139139
assertThat(encoder.upgradeEncoding(encoded)).isFalse();
140140
}
@@ -159,8 +159,8 @@ public void upgradeEncodingWhenStrongerToWeakerThenTrue() {
159159

160160
@Test
161161
public void upgradeEncodingWhenInvalidInputThenException() {
162-
assertThatIllegalArgumentException()
163-
.isThrownBy(() -> new SCryptPasswordEncoder().upgradeEncoding("not-a-scrypt-password"));
162+
assertThatIllegalArgumentException().isThrownBy(
163+
() -> SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1().upgradeEncoding("not-a-scrypt-password"));
164164
}
165165

166166
}

0 commit comments

Comments
 (0)