2006-09-27 jrandom
* added HMAC-SHA256 * properly use CRLF with EepPost * suppress jbigi/jcpuid messages if jbigi.dontLog/jcpuid.dontLog is set * PBE session key generation (with 1000 rounds of SHA256) * misc SDK helper functions
This commit is contained in:
@ -32,7 +32,7 @@ public class CPUID {
|
||||
* initialization? this would otherwise use the Log component, but this makes
|
||||
* it easier for other systems to reuse this class
|
||||
*/
|
||||
private static final boolean _doLog = true;
|
||||
private static final boolean _doLog = System.getProperty("jcpuid.dontLog") == null;
|
||||
|
||||
//.matches() is a java 1.4+ addition, using a simplified version for 1.3+
|
||||
//private static final boolean isX86 = System.getProperty("os.arch").toLowerCase().matches("i?[x0-9]86(_64)?");
|
||||
|
@ -14,6 +14,7 @@ import net.i2p.crypto.DummyElGamalEngine;
|
||||
import net.i2p.crypto.DummyPooledRandomSource;
|
||||
import net.i2p.crypto.ElGamalAESEngine;
|
||||
import net.i2p.crypto.ElGamalEngine;
|
||||
import net.i2p.crypto.HMAC256Generator;
|
||||
import net.i2p.crypto.HMACGenerator;
|
||||
import net.i2p.crypto.KeyGenerator;
|
||||
import net.i2p.crypto.PersistentSessionKeyManager;
|
||||
@ -67,6 +68,7 @@ public class I2PAppContext {
|
||||
private AESEngine _AESEngine;
|
||||
private LogManager _logManager;
|
||||
private HMACGenerator _hmac;
|
||||
private HMAC256Generator _hmac256;
|
||||
private SHA256Generator _sha;
|
||||
protected Clock _clock; // overridden in RouterContext
|
||||
private DSAEngine _dsa;
|
||||
@ -82,6 +84,7 @@ public class I2PAppContext {
|
||||
private volatile boolean _AESEngineInitialized;
|
||||
private volatile boolean _logManagerInitialized;
|
||||
private volatile boolean _hmacInitialized;
|
||||
private volatile boolean _hmac256Initialized;
|
||||
private volatile boolean _shaInitialized;
|
||||
protected volatile boolean _clockInitialized; // used in RouterContext
|
||||
private volatile boolean _dsaInitialized;
|
||||
@ -353,6 +356,19 @@ public class I2PAppContext {
|
||||
_hmacInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public HMAC256Generator hmac256() {
|
||||
if (!_hmac256Initialized) initializeHMAC256();
|
||||
return _hmac256;
|
||||
}
|
||||
private void initializeHMAC256() {
|
||||
synchronized (this) {
|
||||
if (_hmac256 == null) {
|
||||
_hmac256 = new HMAC256Generator(this);
|
||||
}
|
||||
_hmac256Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our SHA256 instance (see the hmac discussion for why its context specific)
|
||||
|
51
core/java/src/net/i2p/crypto/HMAC256Generator.java
Normal file
51
core/java/src/net/i2p/crypto/HMAC256Generator.java
Normal file
@ -0,0 +1,51 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import gnu.crypto.hash.Sha256Standalone;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.crypto.macs.HMac} and
|
||||
* {@link org.bouncycastle.crypto.digests.MD5Digest}.
|
||||
*
|
||||
*/
|
||||
public class HMAC256Generator extends HMACGenerator {
|
||||
public HMAC256Generator(I2PAppContext context) { super(context); }
|
||||
|
||||
protected HMac acquire() {
|
||||
synchronized (_available) {
|
||||
if (_available.size() > 0)
|
||||
return (HMac)_available.remove(0);
|
||||
}
|
||||
// the HMAC is hardcoded to use SHA256 digest size
|
||||
// for backwards compatability. next time we have a backwards
|
||||
// incompatible change, we should update this by removing ", 32"
|
||||
return new HMac(new Sha256ForMAC());
|
||||
}
|
||||
|
||||
private class Sha256ForMAC extends Sha256Standalone implements Digest {
|
||||
public String getAlgorithmName() { return "sha256 for hmac"; }
|
||||
public int getDigestSize() { return 32; }
|
||||
public int doFinal(byte[] out, int outOff) {
|
||||
byte rv[] = digest();
|
||||
System.arraycopy(rv, 0, out, outOff, rv.length);
|
||||
reset();
|
||||
return rv.length;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte data[] = new byte[64];
|
||||
ctx.random().nextBytes(data);
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
Hash mac = ctx.hmac256().calculate(key, data);
|
||||
System.out.println(Base64.encode(mac.getData()));
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ import org.bouncycastle.crypto.macs.HMac;
|
||||
public class HMACGenerator {
|
||||
private I2PAppContext _context;
|
||||
/** set of available HMAC instances for calculate */
|
||||
private List _available;
|
||||
protected List _available;
|
||||
/** set of available byte[] buffers for verify */
|
||||
private List _availableTmp;
|
||||
|
||||
@ -85,7 +85,7 @@ public class HMACGenerator {
|
||||
return eq;
|
||||
}
|
||||
|
||||
private HMac acquire() {
|
||||
protected HMac acquire() {
|
||||
synchronized (_available) {
|
||||
if (_available.size() > 0)
|
||||
return (HMac)_available.remove(0);
|
||||
|
@ -9,10 +9,13 @@ package net.i2p.crypto;
|
||||
*
|
||||
*/
|
||||
|
||||
import gnu.crypto.hash.Sha256Standalone;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
@ -53,6 +56,18 @@ public class KeyGenerator {
|
||||
return key;
|
||||
}
|
||||
|
||||
private static final int PBE_ROUNDS = 1000;
|
||||
/** PBE the passphrase with the salt */
|
||||
public SessionKey generateSessionKey(byte salt[], byte passphrase[]) {
|
||||
byte salted[] = new byte[16+passphrase.length];
|
||||
System.arraycopy(salt, 0, salted, 0, Math.min(salt.length, 16));
|
||||
System.arraycopy(passphrase, 0, salted, 16, passphrase.length);
|
||||
byte h[] = _context.sha().calculateHash(salted).getData();
|
||||
for (int i = 1; i < PBE_ROUNDS; i++)
|
||||
_context.sha().calculateHash(h, 0, Hash.HASH_LENGTH, h, 0);
|
||||
return new SessionKey(h);
|
||||
}
|
||||
|
||||
/** standard exponent size */
|
||||
private static final int PUBKEY_EXPONENT_SIZE_FULL = 2048;
|
||||
/**
|
||||
@ -95,7 +110,7 @@ public class KeyGenerator {
|
||||
* @return the corresponding PublicKey object
|
||||
*/
|
||||
public static PublicKey getPublicKey(PrivateKey priv) {
|
||||
BigInteger a = new NativeBigInteger(priv.toByteArray());
|
||||
BigInteger a = new NativeBigInteger(1, priv.toByteArray());
|
||||
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
|
||||
PublicKey pub = new PublicKey();
|
||||
byte [] pubBytes = aalpha.toByteArray();
|
||||
@ -132,7 +147,7 @@ public class KeyGenerator {
|
||||
* @return a SigningPublicKey object
|
||||
*/
|
||||
public static SigningPublicKey getSigningPublicKey(SigningPrivateKey priv) {
|
||||
BigInteger x = new NativeBigInteger(priv.toByteArray());
|
||||
BigInteger x = new NativeBigInteger(1, priv.toByteArray());
|
||||
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
||||
SigningPublicKey pub = new SigningPublicKey();
|
||||
byte [] pubBytes = padBuffer(y.toByteArray(), SigningPublicKey.KEYSIZE_BYTES);
|
||||
|
@ -9,6 +9,7 @@ package net.i2p.data;
|
||||
*
|
||||
*/
|
||||
|
||||
import gnu.crypto.hash.Sha256Standalone;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -770,9 +771,11 @@ public class DataHelper {
|
||||
* Read a newline delimited line from the stream, returning the line (without
|
||||
* the newline), or null if EOF reached before the newline was found
|
||||
*/
|
||||
public static String readLine(InputStream in) throws IOException {
|
||||
public static String readLine(InputStream in) throws IOException { return readLine(in, (Sha256Standalone)null); }
|
||||
/** update the hash along the way */
|
||||
public static String readLine(InputStream in, Sha256Standalone hash) throws IOException {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
boolean ok = readLine(in, buf);
|
||||
boolean ok = readLine(in, buf, hash);
|
||||
if (ok)
|
||||
return buf.toString();
|
||||
else
|
||||
@ -785,8 +788,13 @@ public class DataHelper {
|
||||
* newline was found
|
||||
*/
|
||||
public static boolean readLine(InputStream in, StringBuffer buf) throws IOException {
|
||||
return readLine(in, buf, null);
|
||||
}
|
||||
/** update the hash along the way */
|
||||
public static boolean readLine(InputStream in, StringBuffer buf, Sha256Standalone hash) throws IOException {
|
||||
int c = -1;
|
||||
while ( (c = in.read()) != -1) {
|
||||
if (hash != null) hash.update((byte)c);
|
||||
if (c == '\n')
|
||||
break;
|
||||
buf.append((char)c);
|
||||
@ -797,6 +805,10 @@ public class DataHelper {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void write(OutputStream out, byte data[], Sha256Standalone hash) throws IOException {
|
||||
hash.update(data);
|
||||
out.write(data);
|
||||
}
|
||||
|
||||
public static List sortStructures(Collection dataStructures) {
|
||||
if (dataStructures == null) return new ArrayList();
|
||||
|
@ -32,6 +32,7 @@ public class PrivateKey extends DataStructureImpl {
|
||||
public PrivateKey() {
|
||||
setData(null);
|
||||
}
|
||||
public PrivateKey(byte data[]) { setData(data); }
|
||||
|
||||
/** constructs from base64
|
||||
* @param base64Data a string of base64 data (the output of .toBase64() called
|
||||
|
@ -31,6 +31,11 @@ public class PublicKey extends DataStructureImpl {
|
||||
public PublicKey() {
|
||||
setData(null);
|
||||
}
|
||||
public PublicKey(byte data[]) {
|
||||
if ( (data == null) || (data.length != KEYSIZE_BYTES) )
|
||||
throw new IllegalArgumentException("Data must be specified, and the correct size");
|
||||
setData(data);
|
||||
}
|
||||
|
||||
/** constructs from base64
|
||||
* @param base64Data a string of base64 data (the output of .toBase64() called
|
||||
|
@ -13,6 +13,7 @@ import net.i2p.util.Log;
|
||||
public class EepPost {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private static final String CRLF = "\r\n";
|
||||
|
||||
public EepPost() {
|
||||
this(I2PAppContext.getGlobalContext());
|
||||
@ -65,6 +66,7 @@ public class EepPost {
|
||||
_onCompletion = onCompletion;
|
||||
}
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Running the post task");
|
||||
Socket s = null;
|
||||
try {
|
||||
URL u = new URL(_url);
|
||||
@ -81,17 +83,20 @@ public class EepPost {
|
||||
_proxyPort = p;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Connecting to the server/proxy...");
|
||||
s = new Socket(_proxyHost, _proxyPort);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Connected");
|
||||
OutputStream out = s.getOutputStream();
|
||||
String sep = getSeparator();
|
||||
long length = calcContentLength(sep, _fields);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Separator: " + sep + " content length: " + length);
|
||||
String header = getHeader(isProxy, path, h, p, sep, length);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Header: \n" + header);
|
||||
out.write(header.getBytes());
|
||||
out.flush();
|
||||
if (false) {
|
||||
out.write(("--" + sep + "\ncontent-disposition: form-data; name=\"field1\"\n\nStuff goes here\n--" + sep + "--\n").getBytes());
|
||||
out.write(("--" + sep + CRLF + "content-disposition: form-data; name=\"field1\"" + CRLF + CRLF + "Stuff goes here" + CRLF + "--" + sep + "--" + CRLF).getBytes());
|
||||
} else {
|
||||
sendFields(out, sep, _fields);
|
||||
}
|
||||
@ -121,18 +126,18 @@ public class EepPost {
|
||||
Object val = fields.get(key);
|
||||
if (val instanceof File) {
|
||||
File f = (File)val;
|
||||
len += ("--" + sep + "\nContent-Disposition: form-data; name=\"" + key + "\"; filename=\"" + f.getName() + "\"\n").length();
|
||||
len += ("--" + sep + CRLF + "Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + f.getName() + "\"" + CRLF).length();
|
||||
//len += ("Content-length: " + f.length() + "\n").length();
|
||||
len += ("Content-Type: application/octet-stream\n\n").length();
|
||||
len += ("Content-Type: application/octet-stream" + CRLF + CRLF).length();
|
||||
len += f.length();
|
||||
len += 1; // nl
|
||||
len += CRLF.length(); // nl
|
||||
} else {
|
||||
len += ("--" + sep + "\nContent-Disposition: form-data; name=\"" + key + "\"\n\n").length();
|
||||
len += ("--" + sep + CRLF + "Content-Disposition: form-data; name=\"" + key + "\"" + CRLF + CRLF).length();
|
||||
len += val.toString().length();
|
||||
len += 1; // nl
|
||||
len += CRLF.length(); // nl
|
||||
}
|
||||
}
|
||||
len += 2 + sep.length() + 2;
|
||||
len += 2 + sep.length() + 2 + CRLF.length(); //2 + sep.length() + 2;
|
||||
//len += 2;
|
||||
return len;
|
||||
}
|
||||
@ -145,29 +150,29 @@ public class EepPost {
|
||||
else
|
||||
sendField(out, separator, field, val.toString());
|
||||
}
|
||||
out.write(("--" + separator + "--\n").getBytes());
|
||||
out.write(("--" + separator + "--" + CRLF).getBytes());
|
||||
}
|
||||
|
||||
private void sendFile(OutputStream out, String separator, String field, File file) throws IOException {
|
||||
long len = file.length();
|
||||
out.write(("--" + separator + "\n").getBytes());
|
||||
out.write(("Content-Disposition: form-data; name=\"" + field + "\"; filename=\"" + file.getName() + "\"\n").getBytes());
|
||||
out.write(("--" + separator + CRLF).getBytes());
|
||||
out.write(("Content-Disposition: form-data; name=\"" + field + "\"; filename=\"" + file.getName() + "\"" + CRLF).getBytes());
|
||||
//out.write(("Content-length: " + len + "\n").getBytes());
|
||||
out.write(("Content-Type: application/octet-stream\n\n").getBytes());
|
||||
out.write(("Content-Type: application/octet-stream" + CRLF + CRLF).getBytes());
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
byte buf[] = new byte[1024];
|
||||
int read = -1;
|
||||
while ( (read = in.read(buf)) != -1)
|
||||
out.write(buf, 0, read);
|
||||
out.write("\n".getBytes());
|
||||
out.write(CRLF.getBytes());
|
||||
in.close();
|
||||
}
|
||||
|
||||
private void sendField(OutputStream out, String separator, String field, String val) throws IOException {
|
||||
out.write(("--" + separator + "\n").getBytes());
|
||||
out.write(("Content-Disposition: form-data; name=\"" + field + "\"\n\n").getBytes());
|
||||
out.write(("--" + separator + CRLF).getBytes());
|
||||
out.write(("Content-Disposition: form-data; name=\"" + field + "\"" + CRLF + CRLF).getBytes());
|
||||
out.write(val.getBytes());
|
||||
out.write("\n".getBytes());
|
||||
out.write(CRLF.getBytes());
|
||||
}
|
||||
|
||||
private String getHeader(boolean isProxy, String path, String host, int port, String separator, long length) {
|
||||
@ -179,16 +184,16 @@ public class EepPost {
|
||||
buf.append(":").append(port);
|
||||
}
|
||||
buf.append(path);
|
||||
buf.append(" HTTP/1.1\n");
|
||||
buf.append(" HTTP/1.1" + CRLF);
|
||||
buf.append("Host: ").append(host);
|
||||
if (port != 80)
|
||||
buf.append(":").append(port);
|
||||
buf.append("\n");
|
||||
buf.append("Connection: close\n");
|
||||
buf.append("Content-length: ").append(length).append("\n");
|
||||
buf.append(CRLF);
|
||||
buf.append("Connection: close" + CRLF);
|
||||
buf.append("Content-length: ").append(length).append(CRLF);
|
||||
buf.append("Content-type: multipart/form-data, boundary=").append(separator);
|
||||
buf.append("\n");
|
||||
buf.append("\n");
|
||||
buf.append(CRLF);
|
||||
buf.append(CRLF);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ public class NativeBigInteger extends BigInteger {
|
||||
* initialization? this would otherwise use the Log component, but this makes
|
||||
* it easier for other systems to reuse this class
|
||||
*/
|
||||
private static final boolean _doLog = true;
|
||||
private static final boolean _doLog = System.getProperty("jbigi.dontLog") == null;
|
||||
|
||||
private final static String JBIGI_OPTIMIZATION_K6 = "k6";
|
||||
private final static String JBIGI_OPTIMIZATION_K6_2 = "k62";
|
||||
|
@ -1,4 +1,11 @@
|
||||
$Id: history.txt,v 1.527 2006-09-26 23:31:34 complication Exp $
|
||||
$Id: history.txt,v 1.528 2006-09-26 23:44:13 complication Exp $
|
||||
|
||||
2006-09-27 jrandom
|
||||
* added HMAC-SHA256
|
||||
* properly use CRLF with EepPost
|
||||
* suppress jbigi/jcpuid messages if jbigi.dontLog/jcpuid.dontLog is set
|
||||
* PBE session key generation (with 1000 rounds of SHA256)
|
||||
* misc SDK helper functions
|
||||
|
||||
2006-09-26 Complication
|
||||
* Take back another inadverent logging change in NTCPConnection
|
||||
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.462 $ $Date: 2006-09-25 22:11:39 $";
|
||||
public final static String ID = "$Revision: 1.463 $ $Date: 2006-09-26 23:02:15 $";
|
||||
public final static String VERSION = "0.6.1.25";
|
||||
public final static long BUILD = 9;
|
||||
public final static long BUILD = 10;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
Reference in New Issue
Block a user