tor-android/tor-android-binary/src/main/java/net/freehaven/tor/control/PasswordDigest.java

99 lines
3.1 KiB
Java

// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* A hashed digest of a secret password (used to set control connection
* security.)
*
* For the actual hashing algorithm, see RFC2440's secret-to-key conversion.
*/
public class PasswordDigest {
private final byte[] secret;
private final String hashedKey;
/** Return a new password digest with a random secret and salt. */
public static PasswordDigest generateDigest() {
byte[] secret = new byte[20];
SecureRandom rng = new SecureRandom();
rng.nextBytes(secret);
return new PasswordDigest(secret);
}
/** Construct a new password digest with a given secret and random salt */
public PasswordDigest(byte[] secret) {
this(secret, null);
}
/** Construct a new password digest with a given secret and random salt.
* Note that the 9th byte of the specifier determines the number of hash
* iterations as in RFC2440.
*/
public PasswordDigest(byte[] secret, byte[] specifier) {
this.secret = secret.clone();
if (specifier == null) {
specifier = new byte[9];
SecureRandom rng = new SecureRandom();
rng.nextBytes(specifier);
specifier[8] = 96;
}
hashedKey = "16:"+encodeBytes(secretToKey(secret, specifier));
}
/** Return the secret used to generate this password hash.
*/
public byte[] getSecret() {
return secret.clone();
}
/** Return the hashed password in the format used by Tor. */
public String getHashedPassword() {
return hashedKey;
}
/** Parameter used by RFC2440's s2k algorithm. */
private static final int EXPBIAS = 6;
/** Implement rfc2440 s2k */
public static byte[] secretToKey(byte[] secret, byte[] specifier) {
MessageDigest d;
try {
d = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("Can't run without sha-1.");
}
int c = (specifier[8])&0xff;
int count = (16 + (c&15)) << ((c>>4) + EXPBIAS);
byte[] tmp = new byte[8+secret.length];
System.arraycopy(specifier, 0, tmp, 0, 8);
System.arraycopy(secret, 0, tmp, 8, secret.length);
while (count > 0) {
if (count >= tmp.length) {
d.update(tmp);
count -= tmp.length;
} else {
d.update(tmp, 0, count);
count = 0;
}
}
byte[] key = new byte[20+9];
System.arraycopy(d.digest(), 0, key, 9, 20);
System.arraycopy(specifier, 0, key, 0, 9);
return key;
}
/** Return a hexadecimal encoding of a byte array. */
// XXX There must be a better way to do this in Java.
private static final String encodeBytes(byte[] ba) {
return Bytes.hex(ba);
}
}