2005-03-23 jrandom
* New /configupdate.jsp page for controlling the update / notification process, as well as various minor related updates. Note that not all options are exposed yet, and the update detection code isn't in place in this commit - it currently says there is always an update available. * New EepGet component for reliable downloading, with a CLI exposed in java -cp lib/i2p.jar net.i2p.util.EepGet url * Added a default signing key to the TrustedUpdate component to be used for verifying updates. This signing key can be authenticated via gpg --verify i2p/core/java/src/net/i2p/crypto/TrustedUpdate.java * New public domain SHA1 implementation for the DSA code so that we can handle signing streams of arbitrary size without excess memory usage (thanks P.Verdy!) * Added some helpers to the TrustedUpdate to work off streams and to offer a minimal CLI: TrustedUpdate keygen pubKeyFile privKeyFile TrustedUpdate sign origFile signedFile privKeyFile TrustedUpdate verify signedFile
This commit is contained in:
@ -91,7 +91,6 @@ public class I2PAppContext {
|
||||
public static I2PAppContext getGlobalContext() {
|
||||
synchronized (I2PAppContext.class) {
|
||||
if (_globalAppContext == null) {
|
||||
System.err.println("*** Building a seperate global context!");
|
||||
_globalAppContext = new I2PAppContext(false, null);
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,13 @@ package net.i2p.crypto;
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
@ -55,6 +58,12 @@ public class DSAEngine {
|
||||
return verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
|
||||
}
|
||||
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
|
||||
return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
|
||||
}
|
||||
public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) {
|
||||
return verifySignature(signature, calculateHash(in), verifyingKey);
|
||||
}
|
||||
public boolean verifySignature(Signature signature, Hash hash, SigningPublicKey verifyingKey) {
|
||||
long start = _context.clock().now();
|
||||
|
||||
try {
|
||||
@ -72,7 +81,7 @@ public class DSAEngine {
|
||||
BigInteger r = new NativeBigInteger(1, rbytes);
|
||||
BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
|
||||
BigInteger w = s.modInverse(CryptoConstants.dsaq);
|
||||
byte data[] = calculateHash(signedData, offset, size).getData();
|
||||
byte data[] = hash.getData();
|
||||
NativeBigInteger bi = new NativeBigInteger(1, data);
|
||||
BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq);
|
||||
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
|
||||
@ -99,6 +108,18 @@ public class DSAEngine {
|
||||
}
|
||||
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
|
||||
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
|
||||
Hash h = calculateHash(data, offset, length);
|
||||
return sign(h, signingKey);
|
||||
}
|
||||
|
||||
public Signature sign(InputStream in, SigningPrivateKey signingKey) {
|
||||
if ((signingKey == null) || (in == null) ) return null;
|
||||
Hash h = calculateHash(in);
|
||||
return sign(h, signingKey);
|
||||
}
|
||||
|
||||
public Signature sign(Hash hash, SigningPrivateKey signingKey) {
|
||||
if ((signingKey == null) || (hash == null)) return null;
|
||||
long start = _context.clock().now();
|
||||
|
||||
Signature sig = new Signature();
|
||||
@ -110,11 +131,8 @@ public class DSAEngine {
|
||||
|
||||
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
BigInteger kinv = k.modInverse(CryptoConstants.dsaq);
|
||||
Hash h = calculateHash(data, offset, length);
|
||||
|
||||
if (h == null) return null;
|
||||
|
||||
BigInteger M = new NativeBigInteger(1, h.getData());
|
||||
BigInteger M = new NativeBigInteger(1, hash.getData());
|
||||
BigInteger x = new NativeBigInteger(1, signingKey.getData());
|
||||
BigInteger s = (kinv.multiply(M.add(x.multiply(r)))).mod(CryptoConstants.dsaq);
|
||||
|
||||
@ -157,141 +175,27 @@ public class DSAEngine {
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
private int[] H0 = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
|
||||
|
||||
private Hash calculateHash(byte[] source, int offset, int len) {
|
||||
long length = len * 8;
|
||||
int k = 448 - (int) ((length + 1) % 512);
|
||||
if (k < 0) {
|
||||
k += 512;
|
||||
}
|
||||
int padbytes = k / 8;
|
||||
int wordlength = len / 4 + padbytes / 4 + 3;
|
||||
int[] M0 = new int[wordlength];
|
||||
int wordcount = 0;
|
||||
int x = 0;
|
||||
for (x = 0; x < (len / 4) * 4; x += 4) {
|
||||
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
|
||||
M0[wordcount] |= source[offset + x + 1] << 24 >>> 24 << 16;
|
||||
M0[wordcount] |= source[offset + x + 2] << 24 >>> 24 << 8;
|
||||
M0[wordcount] |= source[offset + x + 3] << 24 >>> 24 << 0;
|
||||
wordcount++;
|
||||
}
|
||||
|
||||
switch (len - (wordcount + 1) * 4 + 4) {
|
||||
case 0:
|
||||
M0[wordcount] |= 0x80000000;
|
||||
break;
|
||||
case 1:
|
||||
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
|
||||
M0[wordcount] |= 0x00800000;
|
||||
break;
|
||||
case 2:
|
||||
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
|
||||
M0[wordcount] |= source[offset + x + 1] << 24 >>> 24 << 16;
|
||||
M0[wordcount] |= 0x00008000;
|
||||
break;
|
||||
case 3:
|
||||
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
|
||||
M0[wordcount] |= source[offset + x + 1] << 24 >>> 24 << 16;
|
||||
M0[wordcount] |= source[offset + x + 2] << 24 >>> 24 << 8;
|
||||
M0[wordcount] |= 0x00000080;
|
||||
break;
|
||||
}
|
||||
M0[wordlength - 2] = (int) (length >>> 32);
|
||||
M0[wordlength - 1] = (int) (length);
|
||||
int[] H = new int[5];
|
||||
for (x = 0; x < 5; x++) {
|
||||
H[x] = H0[x];
|
||||
}
|
||||
int blocks = M0.length / 16;
|
||||
|
||||
int[] W = new int[80];
|
||||
for (int bl = 0; bl < blocks; bl++) {
|
||||
int a = H[0];
|
||||
int b = H[1];
|
||||
int c = H[2];
|
||||
int d = H[3];
|
||||
int e = H[4];
|
||||
|
||||
Arrays.fill(W, 0);
|
||||
|
||||
for (x = 0; x < 80; x++) {
|
||||
if (x < 16) {
|
||||
W[x] = M0[bl * 16 + x];
|
||||
} else {
|
||||
W[x] = ROTL(1, W[x - 3] ^ W[x - 8] ^ W[x - 14] ^ W[x - 16]);
|
||||
}
|
||||
|
||||
public Hash calculateHash(InputStream in) {
|
||||
SHA1 digest = new SHA1();
|
||||
byte buf[] = new byte[64];
|
||||
int read = 0;
|
||||
try {
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
digest.engineUpdate(buf, 0, read);
|
||||
}
|
||||
|
||||
for (x = 0; x < 80; x++) {
|
||||
int T = add(ROTL(5, a), add(f(x, b, c, d), add(e, add(k(x), W[x]))));
|
||||
e = d;
|
||||
d = c;
|
||||
c = ROTL(30, b);
|
||||
b = a;
|
||||
a = T;
|
||||
}
|
||||
|
||||
H[0] = add(a, H[0]);
|
||||
H[1] = add(b, H[1]);
|
||||
H[2] = add(c, H[2]);
|
||||
H[3] = add(d, H[3]);
|
||||
H[4] = add(e, H[4]);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to hash the stream", ioe);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] hashbytes = new byte[20];
|
||||
for (x = 0; x < 5; x++) {
|
||||
hashbytes[x * 4] = (byte) (H[x] << 0 >>> 24);
|
||||
hashbytes[x * 4 + 1] = (byte) (H[x] << 8 >>> 24);
|
||||
hashbytes[x * 4 + 2] = (byte) (H[x] << 16 >>> 24);
|
||||
hashbytes[x * 4 + 3] = (byte) (H[x] << 24 >>> 24);
|
||||
}
|
||||
Hash hash = new Hash();
|
||||
hash.setData(hashbytes);
|
||||
return hash;
|
||||
return new Hash(digest.engineDigest());
|
||||
}
|
||||
|
||||
private int k(int t) {
|
||||
if (t > -1 && t < 20) {
|
||||
return 0x5a827999;
|
||||
} else if (t > 19 && t < 40) {
|
||||
return 0x6ed9eba1;
|
||||
} else if (t > 39 && t < 60) {
|
||||
return 0x8f1bbcdc;
|
||||
} else if (t > 59 && t < 80) { return 0xca62c1d6; }
|
||||
return 0x00000000;
|
||||
}
|
||||
|
||||
private int f(int t, int x, int y, int z) {
|
||||
if (t > -1 && t < 20) {
|
||||
return Ch(x, y, z);
|
||||
} else if (t > 19 && t < 40) {
|
||||
return Parity(x, y, z);
|
||||
} else if (t > 39 && t < 60) {
|
||||
return Maj(x, y, z);
|
||||
} else if (t > 59 && t < 80) { return Parity(x, y, z); }
|
||||
return 0x00000000;
|
||||
}
|
||||
|
||||
private int Ch(int x, int y, int z) {
|
||||
return (x & y) ^ (~x & z);
|
||||
}
|
||||
|
||||
private int Parity(int x, int y, int z) {
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
private int Maj(int x, int y, int z) {
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
}
|
||||
|
||||
private int ROTL(int n, int x) {
|
||||
return (x << n) | (x >>> 32 - n);
|
||||
}
|
||||
|
||||
private int add(int x, int y) {
|
||||
return x + y;
|
||||
public static Hash calculateHash(byte[] source, int offset, int len) {
|
||||
SHA1 h = new SHA1();
|
||||
h.engineUpdate(source, offset, len);
|
||||
byte digested[] = h.digest();
|
||||
return new Hash(digested);
|
||||
}
|
||||
}
|
697
core/java/src/net/i2p/crypto/SHA1.java
Normal file
697
core/java/src/net/i2p/crypto/SHA1.java
Normal file
@ -0,0 +1,697 @@
|
||||
package net.i2p.crypto;
|
||||
/* @(#)SHA1.java 1.11 2004-04-26
|
||||
* This file was freely contributed to the LimeWire project and is covered
|
||||
* by its existing GPL licence, but it may be used individually as a public
|
||||
* domain implementation of a published algorithm (see below for references).
|
||||
* It was also freely contributed to the Bitzi public domain sources.
|
||||
* @author Philippe Verdy
|
||||
*/
|
||||
|
||||
/* Sun may wish to change the following package name, if integrating this
|
||||
* class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger).
|
||||
*
|
||||
* You can include it in your own Security Provider by inserting
|
||||
* this property in your Provider derived class:
|
||||
* put("MessageDigest.SHA-1", "com.bitzi.util.SHA1");
|
||||
*/
|
||||
//package com.bitzi.util;
|
||||
import java.security.*;
|
||||
//--+---+1--+---+--2+---+---+3--+---+--4+---+---+5--+---+--6+---+---+7--+---+--
|
||||
//34567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
|
||||
/**
|
||||
* <p>The FIPS PUB 180-2 standard specifies four secure hash algorithms (SHA-1,
|
||||
* SHA-256, SHA-384 and SHA-512) for computing a condensed representation of
|
||||
* electronic data (message). When a message of any length < 2^^64 bits (for
|
||||
* SHA-1 and SHA-256) or < 2^^128 bits (for SHA-384 and SHA-512) is input to
|
||||
* an algorithm, the result is an output called a message digest. The message
|
||||
* digests range in length from 160 to 512 bits, depending on the algorithm.
|
||||
* Secure hash algorithms are typically used with other cryptographic
|
||||
* algorithms, such as digital signature algorithms and keyed-hash message
|
||||
* authentication codes, or in the generation of random numbers (bits).</p>
|
||||
*
|
||||
* <p>The four hash algorithms specified in this "SHS" standard are called
|
||||
* secure because, for a given algorithm, it is computationally infeasible
|
||||
* 1) to find a message that corresponds to a given message digest, or 2)
|
||||
* to find two different messages that produce the same message digest. Any
|
||||
* change to a message will, with a very high probability, result in a
|
||||
* different message digest. This will result in a verification failure when
|
||||
* the secure hash algorithm is used with a digital signature algorithm or a
|
||||
* keyed-hash message authentication algorithm.</p>
|
||||
*
|
||||
* <p>A "SHS change notice" adds a SHA-224 algorithm for interoperability,
|
||||
* which, like SHA-1 and SHA-256, operates on 512-bit blocks and 32-bit words,
|
||||
* but truncates the final digest and uses distinct initialization values.</p>
|
||||
*
|
||||
* <p><b>References:</b></p>
|
||||
* <ol>
|
||||
* <li> NIST FIPS PUB 180-2, "Secure Hash Signature Standard (SHS) with
|
||||
* change notice", National Institute of Standards and Technology (NIST),
|
||||
* 2002 August 1, and U.S. Department of Commerce, August 26.<br>
|
||||
* <a href="http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html">
|
||||
* http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html</a>
|
||||
* <li> NIST FIPS PUB 180-1, "Secure Hash Standard",
|
||||
* U.S. Department of Commerce, May 1993.<br>
|
||||
* <a href="http://www.itl.nist.gov/div897/pubs/fip180-1.htm">
|
||||
* http://www.itl.nist.gov/div897/pubs/fip180-1.htm</a></li>
|
||||
* <li> Bruce Schneier, "Section 18.7 Secure Hash Algorithm (SHA)",
|
||||
* <cite>Applied Cryptography, 2nd edition</cite>, <br>
|
||||
* John Wiley & Sons, 1996</li>
|
||||
* </ol>
|
||||
*/
|
||||
public final class SHA1 extends MessageDigest implements Cloneable {
|
||||
|
||||
/**
|
||||
* This implementation returns a fixed-size digest.
|
||||
*/
|
||||
private static final int HASH_LENGTH = 20; // bytes == 160 bits
|
||||
|
||||
/**
|
||||
* Private context for incomplete blocks and padding bytes.
|
||||
* INVARIANT: padding must be in 0..63.
|
||||
* When the padding reaches 64, a new block is computed, and
|
||||
* the 56 last bytes are kept in the padding history.
|
||||
*/
|
||||
private byte[] pad;
|
||||
private int padding;
|
||||
|
||||
/**
|
||||
* Private contextual byte count, sent in the next block,
|
||||
* after the ending padding block.
|
||||
*/
|
||||
private long bytes;
|
||||
|
||||
/**
|
||||
* Private context that contains the current digest key.
|
||||
*/
|
||||
private int hA, hB, hC, hD, hE;
|
||||
|
||||
/**
|
||||
* Creates a SHA1 object with default initial state.
|
||||
*/
|
||||
public SHA1() {
|
||||
super("SHA-1");
|
||||
pad = new byte[64];
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones this object.
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
SHA1 that = (SHA1)super.clone();
|
||||
that.pad = (byte[])this.pad.clone();
|
||||
return that;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest length in bytes.
|
||||
*
|
||||
* Can be used to allocate your own output buffer when
|
||||
* computing multiple digests.
|
||||
*
|
||||
* Overrides the protected abstract method of
|
||||
* <code>java.security.MessageDigestSpi</code>.
|
||||
* @return the digest length in bytes.
|
||||
*/
|
||||
public int engineGetDigestLength() {
|
||||
return HASH_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset athen initialize the digest context.
|
||||
*
|
||||
* Overrides the protected abstract method of
|
||||
* <code>java.security.MessageDigestSpi</code>.
|
||||
*/
|
||||
protected void engineReset() {
|
||||
int i = 60;
|
||||
do {
|
||||
pad[i ] = (byte)0x00;
|
||||
pad[i + 1] = (byte)0x00;
|
||||
pad[i + 2] = (byte)0x00;
|
||||
pad[i + 3] = (byte)0x00;
|
||||
} while ((i -= 4) >= 0);
|
||||
padding = 0;
|
||||
bytes = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the digest context.
|
||||
*/
|
||||
protected void init() {
|
||||
hA = 0x67452301;
|
||||
hB = 0xefcdab89;
|
||||
hC = 0x98badcfe;
|
||||
hD = 0x10325476;
|
||||
hE = 0xc3d2e1f0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the digest using the specified byte.
|
||||
* Requires internal buffering, and may be slow.
|
||||
*
|
||||
* Overrides the protected abstract method of
|
||||
* java.security.MessageDigestSpi.
|
||||
* @param input the byte to use for the update.
|
||||
*/
|
||||
public void engineUpdate(byte input) {
|
||||
bytes++;
|
||||
if (padding < 63) {
|
||||
pad[padding++] = input;
|
||||
return;
|
||||
}
|
||||
pad[63] = input;
|
||||
computeBlock(pad, 0);
|
||||
padding = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the digest using the specified array of bytes,
|
||||
* starting at the specified offset.
|
||||
*
|
||||
* Input length can be any size. May require internal buffering,
|
||||
* if input blocks are not multiple of 64 bytes.
|
||||
*
|
||||
* Overrides the protected abstract method of
|
||||
* java.security.MessageDigestSpi.
|
||||
* @param input the array of bytes to use for the update.
|
||||
* @param offset the offset to start from in the array of bytes.
|
||||
* @param length the number of bytes to use, starting at offset.
|
||||
*/
|
||||
public void engineUpdate(byte[] input, int offset, int len) {
|
||||
if (offset >= 0 && len >= 0 && offset + len <= input.length) {
|
||||
bytes += len;
|
||||
/* Terminate the previous block. */
|
||||
int padlen = 64 - padding;
|
||||
if (padding > 0 && len >= padlen) {
|
||||
System.arraycopy(input, offset, pad, padding, padlen);
|
||||
computeBlock(pad, 0);
|
||||
padding = 0;
|
||||
offset += padlen;
|
||||
len -= padlen;
|
||||
}
|
||||
/* Loop on large sets of complete blocks. */
|
||||
while (len >= 512) {
|
||||
computeBlock(input, offset);
|
||||
computeBlock(input, offset + 64);
|
||||
computeBlock(input, offset + 128);
|
||||
computeBlock(input, offset + 192);
|
||||
computeBlock(input, offset + 256);
|
||||
computeBlock(input, offset + 320);
|
||||
computeBlock(input, offset + 384);
|
||||
computeBlock(input, offset + 448);
|
||||
offset += 512;
|
||||
len -= 512;
|
||||
}
|
||||
/* Loop on remaining complete blocks. */
|
||||
while (len >= 64) {
|
||||
computeBlock(input, offset);
|
||||
offset += 64;
|
||||
len -= 64;
|
||||
}
|
||||
/* remaining bytes kept for next block. */
|
||||
if (len > 0) {
|
||||
System.arraycopy(input, offset, pad, padding, len);
|
||||
padding += len;
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the hash computation by performing final operations
|
||||
* such as padding. Computes the final hash and returns the final
|
||||
* value as a byte[20] array. Once engineDigest has been called,
|
||||
* the engine will be automatically reset as specified in the
|
||||
* JavaSecurity MessageDigest specification.
|
||||
*
|
||||
* For faster operations with multiple digests, allocate your own
|
||||
* array and use engineDigest(byte[], int offset, int len).
|
||||
*
|
||||
* Overrides the protected abstract method of
|
||||
* java.security.MessageDigestSpi.
|
||||
* @return the length of the digest stored in the output buffer.
|
||||
*/
|
||||
public byte[] engineDigest() {
|
||||
try {
|
||||
final byte hashvalue[] = new byte[HASH_LENGTH];
|
||||
engineDigest(hashvalue, 0, HASH_LENGTH);
|
||||
return hashvalue;
|
||||
} catch (DigestException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the hash computation by performing final operations
|
||||
* such as padding. Once engineDigest has been called, the engine
|
||||
* will be automatically reset (see engineReset).
|
||||
*
|
||||
* Overrides the protected abstract method of
|
||||
* java.security.MessageDigestSpi.
|
||||
* @param hashvalue the output buffer in which to store the digest.
|
||||
* @param offset offset to start from in the output buffer
|
||||
* @param len number of bytes within buf allotted for the digest.
|
||||
* Both this default implementation and the SUN provider
|
||||
* do not return partial digests. The presence of this
|
||||
* parameter is solely for consistency in our API's.
|
||||
* If the value of this parameter is less than the
|
||||
* actual digest length, the method will throw a
|
||||
* DigestException. This parameter is ignored if its
|
||||
* value is greater than or equal to the actual digest
|
||||
* length.
|
||||
* @return the length of the digest stored in the output buffer.
|
||||
*/
|
||||
public int engineDigest(byte[] hashvalue, int offset, final int len)
|
||||
throws DigestException {
|
||||
if (len >= HASH_LENGTH) {
|
||||
if (hashvalue.length - offset >= HASH_LENGTH) {
|
||||
/* Flush the trailing bytes, adding padding bytes into last
|
||||
* blocks. */
|
||||
int i;
|
||||
/* Add padding null bytes but replace the last 8 padding bytes
|
||||
* by the little-endian 64-bit digested message bit-length. */
|
||||
pad[i = padding] = (byte)0x80; /* required 1st padding byte */
|
||||
/* Check if 8 bytes available in pad to store the total
|
||||
* message size */
|
||||
switch (i) { /* INVARIANT: i must be in [0..63] */
|
||||
case 52: pad[53] = (byte)0x00; /* no break; falls thru */
|
||||
case 53: pad[54] = (byte)0x00; /* no break; falls thru */
|
||||
case 54: pad[55] = (byte)0x00; /* no break; falls thru */
|
||||
case 55: break;
|
||||
case 56: pad[57] = (byte)0x00; /* no break; falls thru */
|
||||
case 57: pad[58] = (byte)0x00; /* no break; falls thru */
|
||||
case 58: pad[59] = (byte)0x00; /* no break; falls thru */
|
||||
case 59: pad[60] = (byte)0x00; /* no break; falls thru */
|
||||
case 60: pad[61] = (byte)0x00; /* no break; falls thru */
|
||||
case 61: pad[62] = (byte)0x00; /* no break; falls thru */
|
||||
case 62: pad[63] = (byte)0x00; /* no break; falls thru */
|
||||
case 63:
|
||||
computeBlock(pad, 0);
|
||||
/* Clear the 56 first bytes of pad[]. */
|
||||
i = 52;
|
||||
do {
|
||||
pad[i ] = (byte)0x00;
|
||||
pad[i + 1] = (byte)0x00;
|
||||
pad[i + 2] = (byte)0x00;
|
||||
pad[i + 3] = (byte)0x00;
|
||||
} while ((i -= 4) >= 0);
|
||||
break;
|
||||
default:
|
||||
/* Clear the rest of 56 first bytes of pad[]. */
|
||||
switch (i & 3) {
|
||||
case 3: i++;
|
||||
break;
|
||||
case 2: pad[(i += 2) - 1] = (byte)0x00;
|
||||
break;
|
||||
case 1: pad[(i += 3) - 2] = (byte)0x00;
|
||||
pad[ i - 1] = (byte)0x00;
|
||||
break;
|
||||
case 0: pad[(i += 4) - 3] = (byte)0x00;
|
||||
pad[ i - 2] = (byte)0x00;
|
||||
pad[ i - 1] = (byte)0x00;
|
||||
}
|
||||
do {
|
||||
pad[i ] = (byte)0x00;
|
||||
pad[i + 1] = (byte)0x00;
|
||||
pad[i + 2] = (byte)0x00;
|
||||
pad[i + 3] = (byte)0x00;
|
||||
} while ((i += 4) < 56);
|
||||
}
|
||||
/* Convert the message size from bytes to big-endian bits. */
|
||||
pad[56] = (byte)((i = (int)(bytes >>> 29)) >> 24);
|
||||
pad[57] = (byte)(i >>> 16);
|
||||
pad[58] = (byte)(i >>> 8);
|
||||
pad[59] = (byte)i;
|
||||
pad[60] = (byte)((i = (int)bytes << 3) >> 24);
|
||||
pad[61] = (byte)(i >>> 16);
|
||||
pad[62] = (byte)(i >>> 8);
|
||||
pad[63] = (byte)i;
|
||||
computeBlock(pad, 0);
|
||||
/* Return the computed digest in big-endian byte order. */
|
||||
hashvalue[offset ] = (byte)((i = hA) >>> 24);
|
||||
hashvalue[offset + 1] = (byte)(i >>> 16);
|
||||
hashvalue[offset + 2] = (byte)(i >>> 8);
|
||||
hashvalue[offset + 3] = (byte)i;
|
||||
hashvalue[offset + 4] = (byte)((i = hB) >>> 24);
|
||||
hashvalue[offset += 5] = (byte)(i >>> 16);
|
||||
hashvalue[offset + 1] = (byte)(i >>> 8);
|
||||
hashvalue[offset + 2] = (byte)i;
|
||||
hashvalue[offset + 3] = (byte)((i = hC) >>> 24);
|
||||
hashvalue[offset + 4] = (byte)(i >>> 16);
|
||||
hashvalue[offset += 5] = (byte)(i >>> 8);
|
||||
hashvalue[offset + 1] = (byte)i;
|
||||
hashvalue[offset + 2] = (byte)((i = hD) >>> 24);
|
||||
hashvalue[offset + 3] = (byte)(i >>> 16);
|
||||
hashvalue[offset + 4] = (byte)(i >>> 8);
|
||||
hashvalue[offset += 5] = (byte)i;
|
||||
hashvalue[offset + 1] = (byte)((i = hE) >>> 24);
|
||||
hashvalue[offset + 2] = (byte)(i >>> 16);
|
||||
hashvalue[offset + 3] = (byte)(i >>> 8);
|
||||
hashvalue[offset + 4] = (byte)i;
|
||||
engineReset(); /* clear the evidence */
|
||||
return HASH_LENGTH;
|
||||
}
|
||||
throw new DigestException(
|
||||
"insufficient space in output buffer to store the digest");
|
||||
}
|
||||
throw new DigestException("partial digests not returned");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the digest using the specified array of bytes,
|
||||
* starting at the specified offset, but an implied length
|
||||
* of exactly 64 bytes.
|
||||
*
|
||||
* Requires no internal buffering, but assumes a fixed input size,
|
||||
* in which the required padding bytes may have been added.
|
||||
*
|
||||
* @param input the array of bytes to use for the update.
|
||||
* @param offset the offset to start from in the array of bytes.
|
||||
*/
|
||||
private void computeBlock(final byte[] input, int offset) {
|
||||
/* Local temporary work variables for intermediate digests. */
|
||||
int a, b, c, d, e;
|
||||
/* Cache the input block into the local working set of 32-bit
|
||||
* values, in big-endian byte order. Be careful when
|
||||
* widening bytes or integers due to sign extension! */
|
||||
int i00, i01, i02, i03, i04, i05, i06, i07,
|
||||
i08, i09, i10, i11, i12, i13, i14, i15;
|
||||
/* Use hash schedule function Ch (rounds 0..19):
|
||||
* Ch(x,y,z) = (x & y) ^ (~x & z) = (x & (y ^ z)) ^ z,
|
||||
* and K00 = .... = K19 = 0x5a827999. */
|
||||
/* First pass, on big endian input (rounds 0..15). */
|
||||
e = hE
|
||||
+ (((a = hA) << 5) | (a >>> 27)) + 0x5a827999 // K00
|
||||
+ (((b = hB) & ((c = hC) ^ (d = hD))) ^ d) // Ch(b,c,d)
|
||||
+ (i00 = input[offset ] << 24
|
||||
| (input[offset + 1] & 0xff) << 16
|
||||
| (input[offset + 2] & 0xff) << 8
|
||||
| (input[offset + 3] & 0xff)); // W00
|
||||
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K01
|
||||
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
|
||||
+ (i01 = input[offset + 4] << 24
|
||||
| (input[offset += 5] & 0xff) << 16
|
||||
| (input[offset + 1] & 0xff) << 8
|
||||
| (input[offset + 2] & 0xff)); // W01
|
||||
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K02
|
||||
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
|
||||
+ (i02 = input[offset + 3] << 24
|
||||
| (input[offset + 4] & 0xff) << 16
|
||||
| (input[offset += 5] & 0xff) << 8
|
||||
| (input[offset + 1] & 0xff)); // W02
|
||||
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K03
|
||||
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
|
||||
+ (i03 = input[offset + 2] << 24
|
||||
| (input[offset + 3] & 0xff) << 16
|
||||
| (input[offset + 4] & 0xff) << 8
|
||||
| (input[offset += 5] & 0xff)); // W03
|
||||
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K04
|
||||
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
|
||||
+ (i04 = input[offset + 1] << 24
|
||||
| (input[offset + 2] & 0xff) << 16
|
||||
| (input[offset + 3] & 0xff) << 8
|
||||
| (input[offset + 4] & 0xff)); // W04
|
||||
e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K05
|
||||
+ ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
|
||||
+ (i05 = input[offset += 5] << 24
|
||||
| (input[offset + 1] & 0xff) << 16
|
||||
| (input[offset + 2] & 0xff) << 8
|
||||
| (input[offset + 3] & 0xff)); // W05
|
||||
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K06
|
||||
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
|
||||
+ (i06 = input[offset + 4] << 24
|
||||
| (input[offset += 5] & 0xff) << 16
|
||||
| (input[offset + 1] & 0xff) << 8
|
||||
| (input[offset + 2] & 0xff)); // W06
|
||||
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K07
|
||||
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
|
||||
+ (i07 = input[offset + 3] << 24
|
||||
| (input[offset + 4] & 0xff) << 16
|
||||
| (input[offset += 5] & 0xff) << 8
|
||||
| (input[offset + 1] & 0xff)); // W07
|
||||
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K08
|
||||
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
|
||||
+ (i08 = input[offset + 2] << 24
|
||||
| (input[offset + 3] & 0xff) << 16
|
||||
| (input[offset + 4] & 0xff) << 8
|
||||
| (input[offset += 5] & 0xff)); // W08
|
||||
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K09
|
||||
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
|
||||
+ (i09 = input[offset + 1] << 24
|
||||
| (input[offset + 2] & 0xff) << 16
|
||||
| (input[offset + 3] & 0xff) << 8
|
||||
| (input[offset + 4] & 0xff)); // W09
|
||||
e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K10
|
||||
+ ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
|
||||
+ (i10 = input[offset += 5] << 24
|
||||
| (input[offset + 1] & 0xff) << 16
|
||||
| (input[offset + 2] & 0xff) << 8
|
||||
| (input[offset + 3] & 0xff)); // W10
|
||||
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K11
|
||||
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
|
||||
+ (i11 = input[offset + 4] << 24
|
||||
| (input[offset += 5] & 0xff) << 16
|
||||
| (input[offset + 1] & 0xff) << 8
|
||||
| (input[offset + 2] & 0xff)); // W11
|
||||
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K12
|
||||
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
|
||||
+ (i12 = input[offset + 3] << 24
|
||||
| (input[offset + 4] & 0xff) << 16
|
||||
| (input[offset += 5] & 0xff) << 8
|
||||
| (input[offset + 1] & 0xff)); // W12
|
||||
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K13
|
||||
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
|
||||
+ (i13 = input[offset + 2] << 24
|
||||
| (input[offset + 3] & 0xff) << 16
|
||||
| (input[offset + 4] & 0xff) << 8
|
||||
| (input[offset += 5] & 0xff)); // W13
|
||||
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K14
|
||||
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
|
||||
+ (i14 = input[offset + 1] << 24
|
||||
| (input[offset + 2] & 0xff) << 16
|
||||
| (input[offset + 3] & 0xff) << 8
|
||||
| (input[offset + 4] & 0xff)); // W14
|
||||
e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K15
|
||||
+ ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
|
||||
+ (i15 = input[offset += 5] << 24
|
||||
| (input[offset + 1] & 0xff) << 16
|
||||
| (input[offset + 2] & 0xff) << 8
|
||||
| (input[offset + 3] & 0xff)); // W15
|
||||
/* Second pass, on scheduled input (rounds 16..31). */
|
||||
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K16
|
||||
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
|
||||
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W16
|
||||
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K17
|
||||
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
|
||||
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W17
|
||||
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K18
|
||||
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
|
||||
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W18
|
||||
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K19
|
||||
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
|
||||
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W19
|
||||
/* Use hash schedule function Parity (rounds 20..39):
|
||||
* Parity(x,y,z) = x ^ y ^ z,
|
||||
* and K20 = .... = K39 = 0x6ed9eba1. */
|
||||
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K20
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W20
|
||||
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K21
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W21
|
||||
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K22
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W22
|
||||
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K23
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W23
|
||||
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K24
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W24
|
||||
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K25
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W25
|
||||
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K26
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W26
|
||||
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K27
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W27
|
||||
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K28
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W28
|
||||
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K29
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W29
|
||||
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K30
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W30
|
||||
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K31
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W31
|
||||
/* Third pass, on scheduled input (rounds 32..47). */
|
||||
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K32
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W32
|
||||
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K33
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W33
|
||||
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K34
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W34
|
||||
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K35
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W35
|
||||
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K36
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W36
|
||||
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K37
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W37
|
||||
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K38
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W38
|
||||
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K39
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W39
|
||||
/* Use hash schedule function Maj (rounds 40..59):
|
||||
* Maj(x,y,z) = (x&y) ^ (x&z) ^ (y&z) = (x & y) | ((x | y) & z),
|
||||
* and K40 = .... = K59 = 0x8f1bbcdc. */
|
||||
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K40
|
||||
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
|
||||
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W40
|
||||
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K41
|
||||
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
|
||||
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W41
|
||||
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K42
|
||||
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
|
||||
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W42
|
||||
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K43
|
||||
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
|
||||
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W43
|
||||
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K44
|
||||
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
|
||||
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W44
|
||||
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K45
|
||||
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
|
||||
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W45
|
||||
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K46
|
||||
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
|
||||
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W46
|
||||
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K47
|
||||
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
|
||||
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W47
|
||||
/* Fourth pass, on scheduled input (rounds 48..63). */
|
||||
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K48
|
||||
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
|
||||
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W48
|
||||
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K49
|
||||
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
|
||||
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W49
|
||||
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K50
|
||||
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
|
||||
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W50
|
||||
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K51
|
||||
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
|
||||
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W51
|
||||
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K52
|
||||
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
|
||||
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W52
|
||||
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K53
|
||||
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
|
||||
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W53
|
||||
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K54
|
||||
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
|
||||
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W54
|
||||
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K55
|
||||
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
|
||||
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W55
|
||||
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K56
|
||||
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
|
||||
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W56
|
||||
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K57
|
||||
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
|
||||
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W57
|
||||
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K58
|
||||
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
|
||||
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W58
|
||||
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K59
|
||||
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
|
||||
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W59
|
||||
/* Use hash schedule function Parity (rounds 60..79):
|
||||
* Parity(x,y,z) = x ^ y ^ z,
|
||||
* and K60 = .... = K79 = 0xca62c1d6. */
|
||||
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K60
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W60
|
||||
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K61
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W61
|
||||
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K62
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W62
|
||||
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K63
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W63
|
||||
/* Fifth pass, on scheduled input (rounds 64..79). */
|
||||
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K64
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W64
|
||||
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K65
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W65
|
||||
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K66
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W66
|
||||
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K67
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W67
|
||||
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K68
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W68
|
||||
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K69
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W69
|
||||
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K70
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W70
|
||||
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K71
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W71
|
||||
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K72
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W72
|
||||
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K73
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W73
|
||||
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K74
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W74
|
||||
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K75
|
||||
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
|
||||
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W75
|
||||
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K76
|
||||
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
|
||||
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W76
|
||||
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K77
|
||||
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
|
||||
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W77
|
||||
/* Terminate the last two rounds of fifth pass,
|
||||
* feeding the final digest on the fly. */
|
||||
hB +=
|
||||
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K78
|
||||
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
|
||||
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W78
|
||||
hA +=
|
||||
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K79
|
||||
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
|
||||
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W79
|
||||
hE += e;
|
||||
hD += d;
|
||||
hC += /* c= */ (c << 30) | (c >>> 2);
|
||||
}
|
||||
}
|
191
core/java/src/net/i2p/crypto/SHA1Test.java
Normal file
191
core/java/src/net/i2p/crypto/SHA1Test.java
Normal file
@ -0,0 +1,191 @@
|
||||
package net.i2p.crypto;
|
||||
/* @(#)SHA1Test.java 1.10 2004-04-24
|
||||
* This file was freely contributed to the LimeWire project and is covered
|
||||
* by its existing GPL licence, but it may be used individually as a public
|
||||
* domain implementation of a published algorithm (see below for references).
|
||||
* It was also freely contributed to the Bitzi public domain sources.
|
||||
* @author Philippe Verdy
|
||||
*/
|
||||
|
||||
/* Sun may wish to change the following package name, if integrating this
|
||||
* class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger).
|
||||
*/
|
||||
//package com.bitzi.util;
|
||||
|
||||
import java.security.*;
|
||||
|
||||
public class SHA1Test {
|
||||
|
||||
private static final SHA1 hash = new SHA1();
|
||||
|
||||
public static void main(String args[]) {
|
||||
// http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
|
||||
System.out.println("****************************************");
|
||||
System.out.println("* Basic FIPS PUB 180-1 test vectors... *");
|
||||
System.out.println("****************************************");
|
||||
tst(1, 1,
|
||||
"abc",
|
||||
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D");
|
||||
tst(1, 2,
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||
"84983E44 1C3BD26e BAAE4AA1 F95129E5 E54670F1");
|
||||
tst(1, 3, /* one million bytes */
|
||||
1000000, "a",
|
||||
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F");
|
||||
System.out.println();
|
||||
|
||||
// http://csrc.ncsl.nist.gov/cryptval/shs/SHAVS.pdf
|
||||
System.out.println("********************************************************");
|
||||
System.out.println("* SHSV Examples of the selected short messages test... *");
|
||||
System.out.println("********************************************************");
|
||||
tst(2, 2, new byte[] {/* 8 bits, i.e. 1 byte */
|
||||
(byte)0x5e},
|
||||
"5e6f80a3 4a9798ca fc6a5db9 6cc57ba4 c4db59c2");
|
||||
tst(2, 4, new byte[] {/* 128 bits, i.e. 16 bytes */
|
||||
(byte)0x9a,(byte)0x7d,(byte)0xfd,(byte)0xf1,(byte)0xec,(byte)0xea,(byte)0xd0,(byte)0x6e,
|
||||
(byte)0xd6,(byte)0x46,(byte)0xaa,(byte)0x55,(byte)0xfe,(byte)0x75,(byte)0x71,(byte)0x46},
|
||||
"82abff66 05dbe1c1 7def12a3 94fa22a8 2b544a35");
|
||||
System.out.println();
|
||||
|
||||
System.out.println("*******************************************************");
|
||||
System.out.println("* SHSV Examples of the selected long messages test... *");
|
||||
System.out.println("*******************************************************");
|
||||
tst(3, 2, new byte[] {/* 1304 bits, i.e. 163 bytes */
|
||||
(byte)0xf7,(byte)0x8f,(byte)0x92,(byte)0x14,(byte)0x1b,(byte)0xcd,(byte)0x17,(byte)0x0a,
|
||||
(byte)0xe8,(byte)0x9b,(byte)0x4f,(byte)0xba,(byte)0x15,(byte)0xa1,(byte)0xd5,(byte)0x9f,
|
||||
(byte)0x3f,(byte)0xd8,(byte)0x4d,(byte)0x22,(byte)0x3c,(byte)0x92,(byte)0x51,(byte)0xbd,
|
||||
(byte)0xac,(byte)0xbb,(byte)0xae,(byte)0x61,(byte)0xd0,(byte)0x5e,(byte)0xd1,(byte)0x15,
|
||||
(byte)0xa0,(byte)0x6a,(byte)0x7c,(byte)0xe1,(byte)0x17,(byte)0xb7,(byte)0xbe,(byte)0xea,
|
||||
(byte)0xd2,(byte)0x44,(byte)0x21,(byte)0xde,(byte)0xd9,(byte)0xc3,(byte)0x25,(byte)0x92,
|
||||
(byte)0xbd,(byte)0x57,(byte)0xed,(byte)0xea,(byte)0xe3,(byte)0x9c,(byte)0x39,(byte)0xfa,
|
||||
(byte)0x1f,(byte)0xe8,(byte)0x94,(byte)0x6a,(byte)0x84,(byte)0xd0,(byte)0xcf,(byte)0x1f,
|
||||
(byte)0x7b,(byte)0xee,(byte)0xad,(byte)0x17,(byte)0x13,(byte)0xe2,(byte)0xe0,(byte)0x95,
|
||||
(byte)0x98,(byte)0x97,(byte)0x34,(byte)0x7f,(byte)0x67,(byte)0xc8,(byte)0x0b,(byte)0x04,
|
||||
(byte)0x00,(byte)0xc2,(byte)0x09,(byte)0x81,(byte)0x5d,(byte)0x6b,(byte)0x10,(byte)0xa6,
|
||||
(byte)0x83,(byte)0x83,(byte)0x6f,(byte)0xd5,(byte)0x56,(byte)0x2a,(byte)0x56,(byte)0xca,
|
||||
(byte)0xb1,(byte)0xa2,(byte)0x8e,(byte)0x81,(byte)0xb6,(byte)0x57,(byte)0x66,(byte)0x54,
|
||||
(byte)0x63,(byte)0x1c,(byte)0xf1,(byte)0x65,(byte)0x66,(byte)0xb8,(byte)0x6e,(byte)0x3b,
|
||||
(byte)0x33,(byte)0xa1,(byte)0x08,(byte)0xb0,(byte)0x53,(byte)0x07,(byte)0xc0,(byte)0x0a,
|
||||
(byte)0xff,(byte)0x14,(byte)0xa7,(byte)0x68,(byte)0xed,(byte)0x73,(byte)0x50,(byte)0x60,
|
||||
(byte)0x6a,(byte)0x0f,(byte)0x85,(byte)0xe6,(byte)0xa9,(byte)0x1d,(byte)0x39,(byte)0x6f,
|
||||
(byte)0x5b,(byte)0x5c,(byte)0xbe,(byte)0x57,(byte)0x7f,(byte)0x9b,(byte)0x38,(byte)0x80,
|
||||
(byte)0x7c,(byte)0x7d,(byte)0x52,(byte)0x3d,(byte)0x6d,(byte)0x79,(byte)0x2f,(byte)0x6e,
|
||||
(byte)0xbc,(byte)0x24,(byte)0xa4,(byte)0xec,(byte)0xf2,(byte)0xb3,(byte)0xa4,(byte)0x27,
|
||||
(byte)0xcd,(byte)0xbb,(byte)0xfb},
|
||||
"cb0082c8 f197d260 991ba6a4 60e76e20 2bad27b3");
|
||||
System.out.println();
|
||||
|
||||
// See also http://csrc.ncsl.nist.gov/cryptval/shs/sha1-vectors.zip
|
||||
|
||||
{
|
||||
final int RETRIES = 10;
|
||||
final int ITERATIONS = 2000;
|
||||
final int BLOCKSIZE = 65536;
|
||||
byte[] input = new byte[BLOCKSIZE];
|
||||
for (int i = BLOCKSIZE; --i >= 0; )
|
||||
input[i] = (byte)i;
|
||||
long best = 0;
|
||||
for (int i = 0; i < 1000; i++) // training for stable measure
|
||||
System.currentTimeMillis();
|
||||
|
||||
for (int retry = 0; retry < RETRIES; retry++) {
|
||||
long t0 = System.currentTimeMillis();
|
||||
for (int i = ITERATIONS; --i >= 0; );
|
||||
long t1 = System.currentTimeMillis();
|
||||
for (int i = ITERATIONS; --i >= 0; )
|
||||
hash.engineUpdate(input, 0, BLOCKSIZE);
|
||||
long t2 = System.currentTimeMillis();
|
||||
long time = (t2 - t1) - (t1 - t0);
|
||||
if (retry == 0 || time < best)
|
||||
best = time;
|
||||
}
|
||||
hash.engineReset();
|
||||
double rate = 1000.0 * ITERATIONS * BLOCKSIZE / best;
|
||||
System.out.println("Our rate = " +
|
||||
(float)(rate * 8) + " bits/s = " +
|
||||
(float)(rate / (1024 * 1024)) + " Megabytes/s");
|
||||
// Java 1.5 beta-b32c, on Athlon XP 1800+:
|
||||
// with java -client: 48.21 Megabytes/s.
|
||||
// with java -server: 68.23 Megabytes/s.
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA");
|
||||
for (int retry = 0; retry < RETRIES; retry++) {
|
||||
long t0 = System.currentTimeMillis();
|
||||
for (int i = ITERATIONS; --i >= 0; );
|
||||
long t1 = System.currentTimeMillis();
|
||||
for (int i = ITERATIONS; --i >= 0; )
|
||||
md.update(input, 0, BLOCKSIZE);
|
||||
long t2 = System.currentTimeMillis();
|
||||
long time = (t2 - t1) - (t1 - t0);
|
||||
if (retry == 0 || time < best)
|
||||
best = time;
|
||||
}
|
||||
md.reset();
|
||||
rate = 1000.0 * ITERATIONS * BLOCKSIZE / best;
|
||||
System.out.println("JCE rate = " +
|
||||
(float)(rate * 8) + " bits/s = " +
|
||||
(float)(rate / (1024 * 1024)) + " Megabytes/s");
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
System.out.println("No SHA algorithm in local JCE Security Providers");
|
||||
}
|
||||
// Java 1.5 beta-b32c, on Athlon XP 1800+:
|
||||
// with java -client: 23.20 Megabytes/s.
|
||||
// with java -server: 45.72 Megabytes/s.
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean tst(final int set, final int vector,
|
||||
final String source,
|
||||
final String expect) {
|
||||
byte[] input = new byte[source.length()];
|
||||
for (int i = 0; i < input.length; i++)
|
||||
input[i] = (byte)source.charAt(i);
|
||||
return tst(set, vector, input, expect);
|
||||
}
|
||||
|
||||
private static final boolean tst(final int set, final int vector,
|
||||
final byte[] input,
|
||||
final String expect) {
|
||||
System.out.print("Set " + set + ", vector# " + vector + ": ");
|
||||
hash.engineUpdate(input, 0, input.length);
|
||||
return tstResult(expect);
|
||||
}
|
||||
|
||||
private static final boolean tst(final int set, final int vector,
|
||||
final int times, final String source,
|
||||
final String expect) {
|
||||
byte[] input = new byte[source.length()];
|
||||
for (int i = 0; i < input.length; i++)
|
||||
input[i] = (byte)source.charAt(i);
|
||||
System.out.print("Set " + set + ", vector# " + vector + ": ");
|
||||
for (int i = 0; i < times; i++)
|
||||
hash.engineUpdate(input, 0, input.length);
|
||||
return tstResult(expect);
|
||||
}
|
||||
|
||||
private static final boolean tstResult(String expect) {
|
||||
final String result = toHex(hash.engineDigest());
|
||||
expect = expect.toUpperCase();
|
||||
if (!expect.equals(result)) {
|
||||
System.out.println("**************** WRONG ***************");
|
||||
System.out.println(" expect: " + expect);
|
||||
System.out.println(" result: " + result);
|
||||
return false;
|
||||
}
|
||||
System.out.println("OK");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final String toHex(final byte[] bytes) {
|
||||
StringBuffer buf = new StringBuffer(bytes.length * 2);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
if ((i & 3) == 0 && i != 0)
|
||||
buf.append(' ');
|
||||
buf.append(HEX.charAt((bytes[i] >> 4) & 0xF))
|
||||
.append(HEX.charAt( bytes[i] & 0xF));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
private static final String HEX = "0123456789ABCDEF";
|
||||
}
|
@ -6,8 +6,12 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
@ -19,21 +23,122 @@ import net.i2p.util.Log;
|
||||
* @author smeghead
|
||||
*/
|
||||
public class TrustedUpdate {
|
||||
/**
|
||||
* default trusted key, generated by jrandom. This can be authenticated
|
||||
* via gpg without modification (gpg --verify TrustedUpdate.java)
|
||||
*
|
||||
*/
|
||||
/*
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
private static byte[] I2P_PUBLICKEY = { 'p', 'k' };
|
||||
*/
|
||||
private static final String DEFAULT_TRUSTED_KEY =
|
||||
"W4kJbnv9KSVwbnapV7SaNW2kMIZKs~hwL0ro9pZXFo1xTwqz45nykCp1H" +
|
||||
"M7sAKYDZay5z1HvYYOl9CNVz00xF03KPU9RUCVxhDZ1YXhZIskPKjUPUs" +
|
||||
"CIpE~Z1C~N9KSEV6~2stDlBNH10VZ4T0X1TrcXwb3IBXliWo2y2GAx~Ow=";
|
||||
/*
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.2.4 (GNU/Linux)
|
||||
|
||||
iD8DBQFCQXoJGnFL2th344YRAtsIAKCUy/sOIsxahUnT2hKLXFL9lXsAmACfUHa5
|
||||
CPah6TDXYJCWmR0n3oPtrvo=
|
||||
=mD0t
|
||||
-----END PGP SIGNATURE-----
|
||||
*/
|
||||
|
||||
private ArrayList _trustedKeys;
|
||||
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
|
||||
private static final int VERSION_BYTES = 16;
|
||||
private static final int HEADER_BYTES = VERSION_BYTES + Signature.SIGNATURE_BYTES;
|
||||
|
||||
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
|
||||
|
||||
public TrustedUpdate() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
this(I2PAppContext.getGlobalContext());
|
||||
}
|
||||
public TrustedUpdate(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(TrustedUpdate.class);
|
||||
_trustedKeys = new ArrayList(1);
|
||||
String keys = ctx.getProperty(PROP_TRUSTED_KEYS);
|
||||
if ( (keys != null) && (keys.length() > 0) ) {
|
||||
StringTokenizer tok = new StringTokenizer(keys, ", ");
|
||||
while (tok.hasMoreTokens())
|
||||
_trustedKeys.add(tok.nextToken());
|
||||
} else {
|
||||
_trustedKeys.add(DEFAULT_TRUSTED_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList getTrustedKeys() { return _trustedKeys; }
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length <= 0) {
|
||||
usage();
|
||||
} else if ("keygen".equals(args[0])) {
|
||||
genKeysCLI(args[1], args[2]);
|
||||
} else if ("sign".equals(args[0])) {
|
||||
signCLI(args[1], args[2], args[3], args[4]);
|
||||
} else if ("verify".equals(args[0])) {
|
||||
verifyCLI(args[1]);
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
private static final void usage() {
|
||||
System.err.println("Usage: TrustedUpdate keygen publicKeyFile privateKeyFile");
|
||||
System.err.println(" TrustedUpdate sign origFile signedFile privateKeyFile version");
|
||||
System.err.println(" TrustedUpdate verify signedFile");
|
||||
}
|
||||
|
||||
private static final void genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
|
||||
SigningPublicKey pub = (SigningPublicKey)keys[0];
|
||||
SigningPrivateKey priv = (SigningPrivateKey)keys[1];
|
||||
|
||||
out = new FileOutputStream(publicKeyFile);
|
||||
pub.writeBytes(out);
|
||||
out.close();
|
||||
|
||||
out = new FileOutputStream(privateKeyFile);
|
||||
priv.writeBytes(out);
|
||||
out.close();
|
||||
out = null;
|
||||
System.out.println("Private keys writen to " + privateKeyFile + " and public to " + publicKeyFile);
|
||||
System.out.println("Public: " + pub.toBase64());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Error writing out the keys");
|
||||
} finally {
|
||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// If no context is defined, don't define one.
|
||||
// Expose verify(inputFile, publicKeyFile) via cli param
|
||||
}
|
||||
private static final void signCLI(String origFile, String outFile, String privKeyFile, String version) {
|
||||
TrustedUpdate up = new TrustedUpdate();
|
||||
Signature sig = up.sign(origFile, outFile, privKeyFile, version);
|
||||
if (sig != null)
|
||||
System.out.println("Signed and written to " + outFile);
|
||||
else
|
||||
System.out.println("Error signing");
|
||||
}
|
||||
|
||||
private static final void verifyCLI(String signedFile) {
|
||||
TrustedUpdate up = new TrustedUpdate();
|
||||
boolean ok = up.verify(signedFile);
|
||||
if (ok)
|
||||
System.out.println("Signature VALID");
|
||||
else
|
||||
System.out.println("Signature INVALID");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the version string from a signed I2P update file.
|
||||
@ -44,14 +149,25 @@ public class TrustedUpdate {
|
||||
* string is present.
|
||||
*/
|
||||
public String getUpdateVersion(String inputFile) {
|
||||
String updateVersion = null;
|
||||
byte[] data = readFileBytes(inputFile, 0, 16);
|
||||
try {
|
||||
updateVersion = new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(inputFile);
|
||||
byte data[] = new byte[VERSION_BYTES];
|
||||
int read = DataHelper.read(in, data);
|
||||
if (read != VERSION_BYTES)
|
||||
return null;
|
||||
for (int i = 0; i < VERSION_BYTES; i++)
|
||||
if (data[i] == 0x00)
|
||||
return new String(data, 0, i, "UTF-8");
|
||||
return new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
// If this ever gets called, you need a new JVM.
|
||||
}
|
||||
return updateVersion;
|
||||
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + uee.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
return "";
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,37 +185,80 @@ public class TrustedUpdate {
|
||||
* string is longer than 16 characters it will be
|
||||
* truncated.
|
||||
*
|
||||
* @return An instance of {@link net.i2p.data.Signature}.
|
||||
* @return An instance of {@link net.i2p.data.Signature}, or null if there was an error
|
||||
*/
|
||||
public Signature sign(String inputFile, String outputFile, String privateKeyFile, String updateVersion) {
|
||||
SigningPrivateKey key = new SigningPrivateKey();
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(privateKeyFile);
|
||||
key.readBytes(in);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the signing key", ioe);
|
||||
return null;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the signing key", dfe);
|
||||
return null;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
return sign(inputFile, outputFile, key, updateVersion);
|
||||
}
|
||||
|
||||
public Signature sign(String inputFile, String outputFile, SigningPrivateKey privKey, String updateVersion) {
|
||||
byte[] headerUpdateVersion = {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00 };
|
||||
byte[] updateVersionBytes = {};
|
||||
if (updateVersion.length() > 16)
|
||||
updateVersion = updateVersion.substring(0, 16);
|
||||
byte[] updateVersionBytes = null;
|
||||
if (updateVersion.length() > VERSION_BYTES)
|
||||
updateVersion = updateVersion.substring(0, VERSION_BYTES);
|
||||
try {
|
||||
updateVersionBytes = updateVersion.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If this ever gets called, you need a new JVM.
|
||||
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + e.getMessage());
|
||||
}
|
||||
for (int i = 0; i < updateVersionBytes.length; i++)
|
||||
headerUpdateVersion[i] = updateVersionBytes[i];
|
||||
byte[] data = readFileBytes(inputFile, 0, (int) new File(inputFile).length());
|
||||
Signature signature = DSAEngine.getInstance().sign(data, new SigningPrivateKey(readFileBytes(privateKeyFile, 0, (int) new File(privateKeyFile).length()-1)));
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
System.arraycopy(updateVersionBytes, 0, headerUpdateVersion, 0, updateVersionBytes.length);
|
||||
|
||||
Signature signature = null;
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(inputFile);
|
||||
signature = _context.dsa().sign(in, privKey);
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error signing", e);
|
||||
return null;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
in = null;
|
||||
}
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(outputFile);
|
||||
fileOutputStream.write(headerUpdateVersion);
|
||||
fileOutputStream.write(signature.getData());
|
||||
fileOutputStream.write(data);
|
||||
|
||||
in = new FileInputStream(inputFile);
|
||||
byte buf[] = new byte[1024];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1)
|
||||
fileOutputStream.write(buf, 0, read);
|
||||
fileOutputStream.close();
|
||||
fileOutputStream = null;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.log(Log.WARN, "Error writing signed I2P update file " + outputFile, ioe);
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ioe) {}
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
@ -112,10 +271,50 @@ public class TrustedUpdate {
|
||||
* @return <code>true</code> if the file has a valid signature.
|
||||
*/
|
||||
public boolean verify(String inputFile) {
|
||||
DSAEngine.getInstance().verifySignature(new Signature(readFileBytes(inputFile, 16, 55)),
|
||||
readFileBytes(inputFile, 56, (int) new File(inputFile).length()-57),
|
||||
new SigningPublicKey(I2P_PUBLICKEY));
|
||||
return false;
|
||||
for (int i = 0; i < _trustedKeys.size(); i++) {
|
||||
SigningPublicKey key = new SigningPublicKey();
|
||||
try {
|
||||
key.fromBase64((String)_trustedKeys.get(i));
|
||||
boolean ok = verify(inputFile, key);
|
||||
if (ok) return true;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.log(Log.CRIT, "Trusted key " + i + " is not valid");
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("None of the keys match");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the DSA signature of a signed I2P update.
|
||||
*
|
||||
* @param inputFile The signed update file to check.
|
||||
* @param key public key to verify against
|
||||
*
|
||||
* @return <code>true</code> if the file has a valid signature.
|
||||
*/
|
||||
public boolean verify(String inputFile, SigningPublicKey key) {
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(inputFile);
|
||||
byte version[] = new byte[VERSION_BYTES];
|
||||
Signature sig = new Signature();
|
||||
if (VERSION_BYTES != DataHelper.read(in, version))
|
||||
throw new IOException("Not enough data for the version bytes");
|
||||
sig.readBytes(in);
|
||||
return _context.dsa().verifySignature(sig, in, key);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error reading " + inputFile + " to verify", ioe);
|
||||
return false;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error reading the signature", dfe);
|
||||
return false;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,28 +326,55 @@ public class TrustedUpdate {
|
||||
* @return <code>true</code> if the file has a valid signature.
|
||||
*/
|
||||
public boolean verify(String inputFile, String publicKeyFile) {
|
||||
DSAEngine.getInstance().verifySignature(new Signature(readFileBytes(inputFile, 16, 55)),
|
||||
readFileBytes(inputFile, 56, (int) new File(inputFile).length()-57),
|
||||
new SigningPublicKey(readFileBytes(publicKeyFile, 0, (int) new File(publicKeyFile).length()-1)));
|
||||
return false;
|
||||
}
|
||||
|
||||
private byte[] readFileBytes(String inputFile, int offset, int length) {
|
||||
byte[] bytes = new byte[length];
|
||||
FileInputStream fileInputStream = null;
|
||||
|
||||
try {
|
||||
fileInputStream = new FileInputStream(inputFile);
|
||||
fileInputStream.read(bytes, offset, length);
|
||||
fileInputStream.close();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.log(Log.WARN, "File " + inputFile + " not found", fnfe);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.log(Log.WARN, "Error reading file " + inputFile, ioe);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
SigningPublicKey pub = new SigningPublicKey();
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(inputFile);
|
||||
pub.readBytes(in);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the signature", ioe);
|
||||
return false;
|
||||
} catch (DataFormatException dfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the signature", dfe);
|
||||
return false;
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
return verify(inputFile, pub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the signature on the signed inputFile, and if it is valid, migrate
|
||||
* the raw data out of it and into the outputFile
|
||||
*
|
||||
* @return true if the signature was valid and the data moved, false otherwise.
|
||||
*/
|
||||
public boolean migrateVerified(String inputFile, String outputFile) {
|
||||
boolean ok = verify(inputFile);
|
||||
if (!ok) return false;
|
||||
FileOutputStream out = null;
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
out = new FileOutputStream(outputFile);
|
||||
in = new FileInputStream(inputFile);
|
||||
long skipped = 0;
|
||||
while (skipped < HEADER_BYTES) {
|
||||
skipped += in.skip(HEADER_BYTES - skipped);
|
||||
}
|
||||
|
||||
byte buf[] = new byte[1024];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1)
|
||||
out.write(buf, 0, read);
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
} finally {
|
||||
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
513
core/java/src/net/i2p/util/EepGet.java
Normal file
513
core/java/src/net/i2p/util/EepGet.java
Normal file
@ -0,0 +1,513 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* EepGet [-p localhost:4444]
|
||||
* [-n #retries]
|
||||
* [-o outputFile]
|
||||
* [-m markSize lineLen]
|
||||
* url
|
||||
*/
|
||||
public class EepGet {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private boolean _shouldProxy;
|
||||
private String _proxyHost;
|
||||
private int _proxyPort;
|
||||
private int _numRetries;
|
||||
private String _outputFile;
|
||||
private String _url;
|
||||
private List _listeners;
|
||||
|
||||
private boolean _keepFetching;
|
||||
private Socket _proxy;
|
||||
private OutputStream _proxyOut;
|
||||
private InputStream _proxyIn;
|
||||
private OutputStream _out;
|
||||
private long _alreadyTransferred;
|
||||
private long _bytesTransferred;
|
||||
private long _bytesRemaining;
|
||||
private int _currentAttempt;
|
||||
private String _etag;
|
||||
|
||||
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
|
||||
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url) {
|
||||
this(ctx, false, null, -1, numRetries, outputFile, url);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(EepGet.class);
|
||||
_shouldProxy = shouldProxy;
|
||||
_proxyHost = proxyHost;
|
||||
_proxyPort = proxyPort;
|
||||
_numRetries = numRetries;
|
||||
_outputFile = outputFile;
|
||||
_url = url;
|
||||
_alreadyTransferred = 0;
|
||||
_bytesTransferred = 0;
|
||||
_bytesRemaining = -1;
|
||||
_currentAttempt = 0;
|
||||
_listeners = new ArrayList(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* EepGet [-p localhost:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] url
|
||||
*
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
String proxyHost = "localhost";
|
||||
int proxyPort = 4444;
|
||||
int numRetries = 5;
|
||||
int markSize = 1024;
|
||||
int lineLen = 40;
|
||||
String saveAs = null;
|
||||
String url = null;
|
||||
try {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (args[i].equals("-p")) {
|
||||
proxyHost = args[i+1].substring(0, args[i+1].indexOf(':'));
|
||||
String port = args[i+1].substring(args[i+1].indexOf(':')+1);
|
||||
proxyPort = Integer.parseInt(port);
|
||||
i++;
|
||||
} else if (args[i].equals("-n")) {
|
||||
numRetries = Integer.parseInt(args[i+1]);
|
||||
i++;
|
||||
} else if (args[i].equals("-o")) {
|
||||
saveAs = args[i+1];
|
||||
i++;
|
||||
} else if (args[i].equals("-m")) {
|
||||
markSize = Integer.parseInt(args[i+1]);
|
||||
lineLen = Integer.parseInt(args[i+2]);
|
||||
i += 2;
|
||||
} else {
|
||||
url = args[i];
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
if (saveAs == null)
|
||||
saveAs = suggestName(url);
|
||||
|
||||
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), proxyHost, proxyPort, numRetries, saveAs, url);
|
||||
get.addStatusListener(get.new CLIStatusListener(markSize, lineLen));
|
||||
get.fetch();
|
||||
}
|
||||
|
||||
public static String suggestName(String url) {
|
||||
String name = null;
|
||||
if (url.lastIndexOf('/') >= 0)
|
||||
name = sanitize(url.substring(url.lastIndexOf('/')+1));
|
||||
if (name != null)
|
||||
return name;
|
||||
else
|
||||
return sanitize(url);
|
||||
}
|
||||
|
||||
private static final String _safeChars = "abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"01234567890.,_=@#:";
|
||||
private static String sanitize(String name) {
|
||||
name = name.replace('/', '_');
|
||||
StringBuffer buf = new StringBuffer(name);
|
||||
for (int i = 0; i < name.length(); i++)
|
||||
if (_safeChars.indexOf(buf.charAt(i)) == -1)
|
||||
buf.setCharAt(i, '_');
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void usage() {
|
||||
System.err.println("EepGet [-p localhost:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] url");
|
||||
}
|
||||
|
||||
public static interface StatusListener {
|
||||
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url);
|
||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile);
|
||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause);
|
||||
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt);
|
||||
}
|
||||
private class CLIStatusListener implements StatusListener {
|
||||
private int _markSize;
|
||||
private int _lineSize;
|
||||
private long _startedOn;
|
||||
private long _written;
|
||||
private long _lastComplete;
|
||||
private DecimalFormat _pct = new DecimalFormat("00.0%");
|
||||
private DecimalFormat _kbps = new DecimalFormat("###,000.00");
|
||||
public CLIStatusListener() {
|
||||
this(1024, 40);
|
||||
}
|
||||
public CLIStatusListener(int markSize, int lineSize) {
|
||||
_markSize = markSize;
|
||||
_lineSize = lineSize;
|
||||
_written = 0;
|
||||
_lastComplete = _context.clock().now();
|
||||
_startedOn = _lastComplete;
|
||||
}
|
||||
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
||||
for (int i = 0; i < currentWrite; i++) {
|
||||
_written++;
|
||||
if ( (_markSize > 0) && (_written % _markSize == 0) ) {
|
||||
System.out.print("#");
|
||||
|
||||
if ( (_lineSize > 0) && (_written % ((long)_markSize*(long)_lineSize) == 0l) ) {
|
||||
long now = _context.clock().now();
|
||||
long timeToSend = now - _lastComplete;
|
||||
if (timeToSend > 0) {
|
||||
StringBuffer buf = new StringBuffer(50);
|
||||
buf.append(" ");
|
||||
double pct = ((double)alreadyTransferred + (double)_written) / ((double)alreadyTransferred + (double)bytesRemaining);
|
||||
synchronized (_pct) {
|
||||
buf.append(_pct.format(pct));
|
||||
}
|
||||
buf.append(": ");
|
||||
buf.append(_written+alreadyTransferred);
|
||||
buf.append(" @ ");
|
||||
double lineKBytes = ((double)_markSize * (double)_lineSize)/1024.0d;
|
||||
double kbps = lineKBytes/((double)timeToSend/1000.0d);
|
||||
synchronized (_kbps) {
|
||||
buf.append(_kbps.format(kbps));
|
||||
}
|
||||
buf.append("KBps");
|
||||
|
||||
buf.append(" / ");
|
||||
long lifetime = _context.clock().now() - _startedOn;
|
||||
double lifetimeKBps = (1000.0d*(double)(_written+alreadyTransferred)/((double)lifetime*1024.0d));
|
||||
synchronized (_kbps) {
|
||||
buf.append(_kbps.format(lifetimeKBps));
|
||||
}
|
||||
buf.append("KBps");
|
||||
System.out.println(buf.toString());
|
||||
}
|
||||
_lastComplete = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
|
||||
System.out.println();
|
||||
System.out.println("== " + new Date());
|
||||
System.out.println("== Transfer of " + url + " completed with " + (alreadyTransferred+bytesTransferred)
|
||||
+ " and " + (bytesRemaining - bytesTransferred) + " remaining");
|
||||
System.out.println("== Output saved to " + outputFile);
|
||||
long timeToSend = _context.clock().now() - _startedOn;
|
||||
System.out.println("== Transfer time: " + DataHelper.formatDuration(timeToSend));
|
||||
StringBuffer buf = new StringBuffer(50);
|
||||
buf.append("== Transfer rate: ");
|
||||
double kbps = (1000.0d*(double)(_written)/((double)timeToSend*1024.0d));
|
||||
synchronized (_kbps) {
|
||||
buf.append(_kbps.format(kbps));
|
||||
}
|
||||
buf.append("KBps");
|
||||
System.out.println(buf.toString());
|
||||
}
|
||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
|
||||
System.out.println();
|
||||
System.out.println("** " + new Date());
|
||||
System.out.println("** Attempt " + currentAttempt + " of " + url + " failed");
|
||||
System.out.println("** Transfered " + bytesTransferred
|
||||
+ " with " + (bytesRemaining < 0 ? "unknown" : ""+bytesRemaining) + " remaining");
|
||||
System.out.println("** " + cause.getMessage());
|
||||
_written = 0;
|
||||
}
|
||||
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
|
||||
System.out.println("== " + new Date());
|
||||
System.out.println("== Transfer of " + url + " failed after " + currentAttempt + " attempts");
|
||||
System.out.println("== Transfer size: " + bytesTransferred + " with "
|
||||
+ (bytesRemaining < 0 ? "unknown" : ""+bytesRemaining) + " remaining");
|
||||
long timeToSend = _context.clock().now() - _startedOn;
|
||||
System.out.println("== Transfer time: " + DataHelper.formatDuration(timeToSend));
|
||||
double kbps = (timeToSend > 0 ? (1000.0d*(double)(bytesTransferred)/((double)timeToSend*1024.0d)) : 0);
|
||||
StringBuffer buf = new StringBuffer(50);
|
||||
buf.append("== Transfer rate: ");
|
||||
synchronized (_kbps) {
|
||||
buf.append(_kbps.format(kbps));
|
||||
}
|
||||
buf.append("KBps");
|
||||
System.out.println(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void addStatusListener(StatusListener lsnr) {
|
||||
synchronized (_listeners) { _listeners.add(lsnr); }
|
||||
}
|
||||
|
||||
public void stopFetching() { _keepFetching = false; }
|
||||
public void fetch() {
|
||||
_keepFetching = true;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _url);
|
||||
while (_keepFetching) {
|
||||
try {
|
||||
sendRequest();
|
||||
doFetch();
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe);
|
||||
} finally {
|
||||
if (_out != null) {
|
||||
try {
|
||||
_out.close();
|
||||
} catch (IOException cioe) {}
|
||||
_out = null;
|
||||
}
|
||||
if (_proxy != null) {
|
||||
try {
|
||||
_proxy.close();
|
||||
_proxy = null;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
_currentAttempt++;
|
||||
if (_currentAttempt > _numRetries)
|
||||
break;
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).transferFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt);
|
||||
}
|
||||
|
||||
/** return true if the URL was completely retrieved */
|
||||
private void doFetch() throws IOException {
|
||||
readHeaders();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Headers read completely, reading " + _bytesRemaining);
|
||||
|
||||
byte buf[] = new byte[1024];
|
||||
while (_keepFetching) {
|
||||
int read = _proxyIn.read(buf);
|
||||
if (read == -1)
|
||||
break;
|
||||
_out.write(buf, 0, read);
|
||||
_bytesTransferred += read;
|
||||
if (read > 0)
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).bytesTransferred(_alreadyTransferred, read, _bytesTransferred, _bytesRemaining, _url);
|
||||
}
|
||||
|
||||
if (_out != null)
|
||||
_out.close();
|
||||
_out = null;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Done transferring " + _bytesTransferred);
|
||||
|
||||
if (_bytesRemaining == _bytesTransferred) {
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).transferComplete(_alreadyTransferred, _bytesTransferred, _bytesRemaining, _url, _outputFile);
|
||||
} else {
|
||||
throw new IOException("Disconnection on attempt " + _currentAttempt + " after " + _bytesTransferred);
|
||||
}
|
||||
}
|
||||
|
||||
private void readHeaders() throws IOException {
|
||||
String key = null;
|
||||
StringBuffer buf = new StringBuffer(32);
|
||||
|
||||
boolean read = DataHelper.readLine(_proxyIn, buf);
|
||||
if (!read) throw new IOException("Unable to read the first line");
|
||||
int responseCode = handleStatus(buf.toString());
|
||||
|
||||
boolean rcOk = false;
|
||||
switch (responseCode) {
|
||||
case 200: // full
|
||||
_out = new FileOutputStream(_outputFile, false);
|
||||
rcOk = true;
|
||||
break;
|
||||
case 206: // partial
|
||||
_out = new FileOutputStream(_outputFile, true);
|
||||
rcOk = true;
|
||||
break;
|
||||
case 416: // completed (or range out of reach)
|
||||
_bytesRemaining = 0;
|
||||
_keepFetching = false;
|
||||
return;
|
||||
default:
|
||||
rcOk = false;
|
||||
}
|
||||
|
||||
byte lookahead[] = new byte[3];
|
||||
while (true) {
|
||||
int cur = _proxyIn.read();
|
||||
switch (cur) {
|
||||
case -1:
|
||||
throw new IOException("Headers ended too soon");
|
||||
case ':':
|
||||
if (key == null) {
|
||||
key = buf.toString();
|
||||
buf.setLength(0);
|
||||
increment(lookahead, cur);
|
||||
break;
|
||||
} else {
|
||||
buf.append((char)cur);
|
||||
increment(lookahead, cur);
|
||||
break;
|
||||
}
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (key != null)
|
||||
handle(key, buf.toString());
|
||||
|
||||
buf.setLength(0);
|
||||
key = null;
|
||||
increment(lookahead, cur);
|
||||
if (isEndOfHeaders(lookahead)) {
|
||||
if (!rcOk)
|
||||
throw new IOException("Invalid HTTP response code: " + responseCode);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
buf.append((char)cur);
|
||||
increment(lookahead, cur);
|
||||
}
|
||||
|
||||
if (buf.length() > 1024)
|
||||
throw new IOException("Header line too long: " + buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the first status line and grab the response code.
|
||||
* e.g. "HTTP/1.1 206 OK" vs "HTTP/1.1 200 OK" vs
|
||||
* "HTTP/1.1 404 NOT FOUND", etc.
|
||||
*
|
||||
* @return HTTP response code (200, 206, other)
|
||||
*/
|
||||
private int handleStatus(String line) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Status line: [" + line + "]");
|
||||
StringTokenizer tok = new StringTokenizer(line, " ");
|
||||
if (!tok.hasMoreTokens()) {
|
||||
System.err.println("ERR: status "+ line);
|
||||
return -1;
|
||||
}
|
||||
String protocol = tok.nextToken(); // ignored
|
||||
if (!tok.hasMoreTokens()) {
|
||||
System.err.println("ERR: status "+ line);
|
||||
return -1;
|
||||
}
|
||||
String rc = tok.nextToken();
|
||||
try {
|
||||
return Integer.parseInt(rc);
|
||||
} catch (NumberFormatException nfe) {
|
||||
nfe.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(String key, String val) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Header line: [" + key + "] = [" + val + "]");
|
||||
if (key.equalsIgnoreCase("Content-length")) {
|
||||
try {
|
||||
_bytesRemaining = Long.parseLong(val.trim());
|
||||
} catch (NumberFormatException nfe) {
|
||||
nfe.printStackTrace();
|
||||
}
|
||||
} else if (key.equalsIgnoreCase("ETag")) {
|
||||
_etag = val.trim();
|
||||
} else {
|
||||
// ignore the rest
|
||||
}
|
||||
}
|
||||
|
||||
private void increment(byte[] lookahead, int cur) {
|
||||
lookahead[0] = lookahead[1];
|
||||
lookahead[1] = lookahead[2];
|
||||
lookahead[2] = (byte)cur;
|
||||
}
|
||||
private boolean isEndOfHeaders(byte lookahead[]) {
|
||||
byte first = lookahead[0];
|
||||
byte second = lookahead[1];
|
||||
byte third = lookahead[2];
|
||||
return (isNL(second) && isNL(third)) || // \n\n
|
||||
(isNL(first) && isNL(third)); // \n\r\n
|
||||
}
|
||||
|
||||
/** we ignore any potential \r, since we trim it on write anyway */
|
||||
private static final byte NL = '\n';
|
||||
private boolean isNL(byte b) { return (b == NL); }
|
||||
|
||||
private void sendRequest() throws IOException {
|
||||
File outFile = new File(_outputFile);
|
||||
if (outFile.exists())
|
||||
_alreadyTransferred = outFile.length();
|
||||
|
||||
String req = getRequest();
|
||||
|
||||
if (_shouldProxy) {
|
||||
_proxy = new Socket(_proxyHost, _proxyPort);
|
||||
} else {
|
||||
try {
|
||||
URL url = new URL(_url);
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
if (port == -1)
|
||||
port = 80;
|
||||
_proxy = new Socket(host, port);
|
||||
} catch (MalformedURLException mue) {
|
||||
throw new IOException("Request URL is invalid");
|
||||
}
|
||||
}
|
||||
_proxyIn = _proxy.getInputStream();
|
||||
_proxyOut = _proxy.getOutputStream();
|
||||
|
||||
_proxyOut.write(req.toString().getBytes());
|
||||
_proxyOut.flush();
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Request flushed");
|
||||
}
|
||||
|
||||
private String getRequest() {
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("GET ").append(_url).append(" HTTP/1.1\n");
|
||||
try {
|
||||
URL url = new URL(_url);
|
||||
buf.append("Host: ").append(url.getHost()).append("\n");
|
||||
} catch (MalformedURLException mue) {
|
||||
mue.printStackTrace();
|
||||
}
|
||||
if (_alreadyTransferred > 0) {
|
||||
buf.append("Range: bytes=");
|
||||
buf.append(_alreadyTransferred);
|
||||
buf.append("-\n");
|
||||
}
|
||||
buf.append("Connection: close\n\n");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Request: [" + buf.toString() + "]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -245,7 +245,7 @@ public class LogManager {
|
||||
if (!_alreadyNoticedMissingConfig) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Log file " + _location + " does not exist");
|
||||
System.err.println("Log file " + _location + " does not exist");
|
||||
//System.err.println("Log file " + _location + " does not exist");
|
||||
_alreadyNoticedMissingConfig = true;
|
||||
}
|
||||
parseConfig(new Properties());
|
||||
@ -644,7 +644,7 @@ public class LogManager {
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
_log.log(Log.CRIT, "Shutting down logger");
|
||||
_log.log(Log.WARN, "Shutting down logger");
|
||||
_writer.flushRecords();
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ package net.i2p.crypto;
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
@ -57,9 +58,13 @@ public class DSABench {
|
||||
long endkeys = System.currentTimeMillis();
|
||||
long startsign = System.currentTimeMillis();
|
||||
Signature s = DSAEngine.getInstance().sign(message, privkey);
|
||||
Signature s1 = DSAEngine.getInstance().sign(new ByteArrayInputStream(message), privkey);
|
||||
long endsignstartverify = System.currentTimeMillis();
|
||||
boolean v = DSAEngine.getInstance().verifySignature(s, message, pubkey);
|
||||
long endverify = System.currentTimeMillis();
|
||||
boolean v1 = DSAEngine.getInstance().verifySignature(s1, new ByteArrayInputStream(message), pubkey);
|
||||
boolean v2 = DSAEngine.getInstance().verifySignature(s1, message, pubkey);
|
||||
boolean v3 = DSAEngine.getInstance().verifySignature(s, new ByteArrayInputStream(message), pubkey);
|
||||
long endverify = System.currentTimeMillis();
|
||||
System.out.print(".");
|
||||
keygentime += endkeys - startkeys;
|
||||
signtime += endsignstartverify - startsign;
|
||||
@ -67,6 +72,8 @@ public class DSABench {
|
||||
if (!v) {
|
||||
throw new RuntimeException("Holy crap, did not verify");
|
||||
}
|
||||
if (!(v1 && v2 && v3))
|
||||
throw new RuntimeException("Stream did not verify");
|
||||
if ( (minKey == 0) && (minS == 0) && (minV == 0) ) {
|
||||
minKey = endkeys - startkeys;
|
||||
maxKey = endkeys - startkeys;
|
||||
|
Reference in New Issue
Block a user