mirror of
https://github.com/i2p/i2p.i2p.git
synced 2026-06-03 19:41:30 +00:00
Util: Use constant-time comparison in various password checkers
reported by: bottomlineit.co.za
This commit is contained in:
@@ -1129,6 +1129,33 @@ public class DataHelper {
|
||||
}
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This throws NPE if either lhs or rhs is null.
|
||||
* Constant time, almost.
|
||||
* Warning: not constant time if secret is empty.
|
||||
*
|
||||
* @param user user-supplied String, will attempt for time to be proportional to this length
|
||||
* @param secret internal String, will attempt for time to be independent of this length
|
||||
* @throws NullPointerException if either arg is null
|
||||
* @since 0.9.70
|
||||
*/
|
||||
public final static boolean eqCT(String user, String secret) {
|
||||
int ul = user.length();
|
||||
int sl = secret.length();
|
||||
if (ul == 0)
|
||||
return sl == 0;
|
||||
int v = ul ^ sl;
|
||||
if (sl == 0) {
|
||||
// so charAt() below works
|
||||
secret = "\0";
|
||||
sl = 1;
|
||||
}
|
||||
for (int i = 0; i < ul; i++) {
|
||||
v |= user.charAt(i) ^ secret.charAt(i % sl);
|
||||
}
|
||||
return v == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Big endian compare, treats bytes as unsigned.
|
||||
@@ -2242,4 +2269,26 @@ public class DataHelper {
|
||||
oidx = idx + to.length();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static void main(String[] args) {
|
||||
test("123", "123");
|
||||
test("a", "b");
|
||||
test("ba", "b");
|
||||
test("ba", "baa");
|
||||
test("", "xxx");
|
||||
test("xxx", "");
|
||||
test("xxx", null);
|
||||
test(null, "xxx");
|
||||
}
|
||||
|
||||
private static void test(String a, String b) {
|
||||
try {
|
||||
boolean r = eqCT(a, b);
|
||||
System.out.println(" test: " + a + ' ' + b + " equals? " + r);
|
||||
} catch (Exception e) {
|
||||
System.out.println(" test: " + a + ' ' + b + " fails " + e);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -31,8 +31,17 @@ public class PasswordManager {
|
||||
protected static final String PROP_B64 = ".b64";
|
||||
/** stored as the hex of the MD5 hash of the UTF-8 bytes. Compatible with Jetty. */
|
||||
protected static final String PROP_MD5 = ".md5";
|
||||
/** stored as a Unix crypt string */
|
||||
|
||||
/**
|
||||
* Stored as a Unix crypt string
|
||||
* Originally intended as a Jetty-compatible UnixCrypt string, see man crypt(5),
|
||||
* but not fully implemented and insecure anyway.
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
protected static final String PROP_CRYPT = ".crypt";
|
||||
|
||||
/** stored as the b64 of the 16 byte salt + the 32 byte hash of the UTF-8 bytes */
|
||||
protected static final String PROP_SHASH = ".shash";
|
||||
|
||||
@@ -64,7 +73,10 @@ public class PasswordManager {
|
||||
String pfx = realm;
|
||||
if (user != null && user.length() > 0)
|
||||
pfx += '.' + user;
|
||||
return pw.equals(_context.getProperty(pfx + PROP_PW));
|
||||
String s = _context.getProperty(pfx + PROP_PW);
|
||||
if (s == null)
|
||||
return false;
|
||||
return DataHelper.eqCT(pw, s);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +92,7 @@ public class PasswordManager {
|
||||
String b64 = _context.getProperty(pfx + PROP_B64);
|
||||
if (b64 == null)
|
||||
return false;
|
||||
return b64.equals(Base64.encode(DataHelper.getUTF8(pw)));
|
||||
return DataHelper.eqCT(Base64.encode(DataHelper.getUTF8(pw)), b64);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user