Forráskód Böngészése

Allow upgrading between different SCrypt encodings

Fixes gh-7057
Lars Grefer 6 éve
szülő
commit
e95effc839

+ 24 - 0
crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java

@@ -135,6 +135,30 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
 		return decodeAndCheckMatches(rawPassword, encodedPassword);
 	}
 
+	@Override
+	public boolean upgradeEncoding(String encodedPassword) {
+		if (encodedPassword == null || encodedPassword.isEmpty()) {
+			return false;
+		}
+
+		String[] parts = encodedPassword.split("\\$");
+
+		if (parts.length != 4) {
+			throw new IllegalArgumentException("Encoded password does not look like SCrypt: " + encodedPassword);
+		}
+
+		long params = Long.parseLong(parts[1], 16);
+
+		int cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);
+		int memoryCost = (int) params >> 8 & 0xff;
+		int parallelization = (int) params & 0xff;
+
+		return cpuCost < this.cpuCost
+				|| memoryCost < this.memoryCost
+				|| parallelization < this.parallelization;
+		
+	}
+
 	private boolean decodeAndCheckMatches(CharSequence rawPassword, String encodedPassword) {
 		String[] parts = encodedPassword.split("\\$");
 

+ 30 - 0
crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java

@@ -116,5 +116,35 @@ public class SCryptPasswordEncoderTests {
 		new SCryptPasswordEncoder(2, 8, 1, -1, 16);
 	}
 
+	@Test
+	public void upgradeEncoding_nullOrEmptyInput() {
+		SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
+		assertThat(encoder.upgradeEncoding(null)).isFalse();
+		assertThat(encoder.upgradeEncoding("")).isFalse();
+	}
+
+	@Test
+	public void upgradeEncoding_sameEncoder() {
+		SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
+		String encoded = encoder.encode("password");
+		assertThat(encoder.upgradeEncoding(encoded)).isFalse();
+	}
+
+	@Test
+	public void upgradeEncoding_weakerToStronger() {
+		SCryptPasswordEncoder weakEncoder = new SCryptPasswordEncoder((int) Math.pow(2, 10), 4, 1, 32, 64);
+		SCryptPasswordEncoder strongEncoder = new SCryptPasswordEncoder((int) Math.pow(2, 16), 8, 1, 32, 64);
+
+		String weakPassword = weakEncoder.encode("password");
+		String strongPassword = strongEncoder.encode("password");
+
+		assertThat(strongEncoder.upgradeEncoding(weakPassword)).isTrue();
+		assertThat(weakEncoder.upgradeEncoding(strongPassword)).isFalse();
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void upgradeEncoding_invalidInput() {
+		new SCryptPasswordEncoder().upgradeEncoding("not-a-scrypt-password");
+	}
 }