Browse Source
Constant time comparison helps to mitigate timing attacks. See the following link for more information * http://rdist.root.org/2010/07/19/exploiting-remote-timing-attacks/ * http://en.wikipedia.org/wiki/Timing_attack for more information.3.0.x
7 changed files with 124 additions and 9 deletions
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
package org.springframework.security.authentication.encoding; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
|
||||
/** |
||||
* Utility for constant time comparison to prevent against timing attacks. |
||||
* |
||||
* @author Rob Winch |
||||
*/ |
||||
class PasswordEncoderUtils { |
||||
|
||||
/** |
||||
* Constant time comparison to prevent against timing attacks. |
||||
* @param expected |
||||
* @param actual |
||||
* @return |
||||
*/ |
||||
static boolean equals(String expected, String actual) { |
||||
byte[] expectedBytes = bytesUtf8(expected); |
||||
byte[] actualBytes = bytesUtf8(actual); |
||||
int expectedLength = expectedBytes == null ? -1 : expectedBytes.length; |
||||
int actualLength = actualBytes == null ? -1 : actualBytes.length; |
||||
if (expectedLength != actualLength) { |
||||
return false; |
||||
} |
||||
|
||||
int result = 0; |
||||
for (int i = 0; i < expectedLength; i++) { |
||||
result |= expectedBytes[i] ^ actualBytes[i]; |
||||
} |
||||
return result == 0; |
||||
} |
||||
|
||||
private static byte[] bytesUtf8(String s) { |
||||
if(s == null) { |
||||
return null; |
||||
} |
||||
try { |
||||
return s.getBytes("UTF-8"); |
||||
} catch (UnsupportedEncodingException e) { |
||||
throw new IllegalStateException("Could not get bytes in UTF-8 format",e); |
||||
} |
||||
} |
||||
private PasswordEncoderUtils() {} |
||||
} |
||||
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
package org.springframework.security.authentication.encoding; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
import org.junit.Test; |
||||
/** |
||||
* @author Rob Winch |
||||
*/ |
||||
public class PasswordEncoderUtilsTests { |
||||
|
||||
@Test |
||||
public void differentLength() { |
||||
assertFalse(PasswordEncoderUtils.equals("abc", "a")); |
||||
assertFalse(PasswordEncoderUtils.equals("a", "abc")); |
||||
} |
||||
|
||||
@Test |
||||
public void equalsNull() { |
||||
assertFalse(PasswordEncoderUtils.equals(null, "a")); |
||||
assertFalse(PasswordEncoderUtils.equals("a", null)); |
||||
assertTrue(PasswordEncoderUtils.equals(null, null)); |
||||
} |
||||
|
||||
@Test |
||||
public void equalsCaseSensitive() { |
||||
assertFalse(PasswordEncoderUtils.equals("aBc", "abc")); |
||||
} |
||||
|
||||
@Test |
||||
public void equalsSuccess() { |
||||
assertTrue(PasswordEncoderUtils.equals("abcdef", "abcdef")); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue