Author: jflesch Date: 2007-04-14 22:08:49 +0000 (Sat, 14 Apr 2007) New Revision: 12730
Added: trunk/apps/Thaw/src/freenet/ trunk/apps/Thaw/src/freenet/crypt/ trunk/apps/Thaw/src/freenet/crypt/Digest.java trunk/apps/Thaw/src/freenet/crypt/SHA256.java trunk/apps/Thaw/src/freenet/support/ trunk/apps/Thaw/src/freenet/support/Base64.java trunk/apps/Thaw/src/freenet/support/HexUtil.java trunk/apps/Thaw/src/freenet/support/IllegalBase64Exception.java trunk/apps/Thaw/src/thaw/fcp/SHA256Computer.java Modified: trunk/apps/Thaw/src/thaw/core/Logger.java trunk/apps/Thaw/src/thaw/fcp/FCPClientHello.java trunk/apps/Thaw/src/thaw/fcp/FCPClientPut.java trunk/apps/Thaw/src/thaw/fcp/FCPConnection.java Log: Implement FileHash attribute in ClientPut request (need testing !) Copied: trunk/apps/Thaw/src/freenet/crypt/Digest.java (from rev 12729, trunk/freenet/src/freenet/crypt/Digest.java) =================================================================== --- trunk/apps/Thaw/src/freenet/crypt/Digest.java (rev 0) +++ trunk/apps/Thaw/src/freenet/crypt/Digest.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -0,0 +1,60 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ +package freenet.crypt; + +public interface Digest { + + /** + * retrieve the value of a hash, by filling the provided int[] with + * n elements of the hash (where n is the bitlength of the hash/32) + * @param digest int[] into which to place n elements + * @param offset index of first of the n elements + */ + public void extract(int [] digest, int offset); + + /** + * Add one byte to the digest. When this is implemented + * all of the abstract class methods end up calling + * this method for types other than bytes. + * @param b byte to add + */ + public void update(byte b); + + /** + * Add many bytes to the digest. + * @param data byte data to add + * @param offset start byte + * @param length number of bytes to hash + */ + public void update(byte[] data, int offset, int length); + + /** + * Adds the entire contents of the byte array to the digest. + */ + public void update(byte[] data); + + /** + * Returns the completed digest, reinitializing the hash function. + * @return the byte array result + */ + public byte[] digest(); + + /** + * Write completed digest into the given buffer. + * @param buffer the buffer to write into + * @param offset the byte offset at which to start writing + * @param reset If true, the hash function is reinitialized + * after writing to the buffer. + */ + public void digest(boolean reset, byte[] buffer, int offset); + + /** + * Return the hash size of this digest in bits + */ + public int digestSize(); +} + + + + Copied: trunk/apps/Thaw/src/freenet/crypt/SHA256.java (from rev 12729, trunk/freenet/src/freenet/crypt/SHA256.java) =================================================================== --- trunk/apps/Thaw/src/freenet/crypt/SHA256.java (rev 0) +++ trunk/apps/Thaw/src/freenet/crypt/SHA256.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -0,0 +1,413 @@ +/** + Cryptix General Licence + + Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 + The Cryptix Foundation Limited. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + + * + * Copyright (C) 2000 The Cryptix Foundation Limited. All rights reserved. + * + * Use, modification, copying and distribution of this software is subject to + * the terms and conditions of the Cryptix General Licence. You should have + * received a copy of the Cryptix General Licence along with this library; + * if not, you can download a copy from http://www.cryptix.org/ . + */ + +package freenet.crypt; + +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Vector; + +import freenet.support.HexUtil; + +/** + * @author Jeroen C. van Gelderen (gelderen at cryptix.org) + */ +public class SHA256 implements Digest { + +// Constants +//........................................................................... + + /** Size (in bytes) of this hash */ + private static final int HASH_SIZE = 32; + private static final int BLOCK_SIZE = 64; + + /** 64 byte buffer */ + private final byte[] buf; + + + /** Buffer offset */ + private int bufOff; + + /** Number of bytes hashed 'till now. */ + private long byteCount; + + /** Round constants */ + private static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + +// Instance variables +//........................................................................... + + /** 8 32-bit words (interim result) */ + private final int[] context; + + /** Expanded message block buffer */ + private final int[] buffer; + + + +// Constructors +//........................................................................... + + public SHA256() { + this.context = new int[8]; + this.buffer = new int[64]; + this.buf = new byte[64]; + coreReset(); + } + +// Concreteness +//........................................................................... + + protected void coreDigest(byte[] buf, int off) { + for( int i=0; i<context.length; i++ ) + for( int j=0; j<4 ; j++ ) + buf[off+(i * 4 + (3-j))] = (byte)(context[i] >>> (8 * j)); + } + + + protected void coreReset() { + this.bufOff = 0; + this.byteCount = 0; + + // initial values + context[0] = 0x6a09e667; + context[1] = 0xbb67ae85; + context[2] = 0x3c6ef372; + context[3] = 0xa54ff53a; + context[4] = 0x510e527f; + context[5] = 0x9b05688c; + context[6] = 0x1f83d9ab; + context[7] = 0x5be0cd19; + } + + + protected void coreUpdate(byte[] block, int offset) { + + int[] W = buffer; + + // extract the bytes into our working buffer + for( int i=0; i<16; i++ ) + W[i] = (block[offset++] ) << 24 | + (block[offset++] & 0xFF) << 16 | + (block[offset++] & 0xFF) << 8 | + (block[offset++] & 0xFF); + + // expand + for( int i=16; i<64; i++ ) + W[i] = sig1(W[i-2]) + W[i-7] + sig0(W[i-15]) + W[i-16]; + + int a = context[0]; + int b = context[1]; + int c = context[2]; + int d = context[3]; + int e = context[4]; + int f = context[5]; + int g = context[6]; + int h = context[7]; + + // run 64 rounds + for( int i=0; i<64; i++ ) { + int T1 = h + Sig1(e) + Ch(e, f, g) + K[i] + W[i]; + int T2 = Sig0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + // merge + context[0] += a; + context[1] += b; + context[2] += c; + context[3] += d; + context[4] += e; + context[5] += f; + context[6] += g; + context[7] += h; + } + + + private final int Ch(int x, int y, int z) { return (x&y)^(~x&z); } + + private final int Maj(int x, int y, int z) { return (x&y)^(x&z)^(y&z); } + + private final int Sig0(int x) { return S( 2, x) ^ S(13, x) ^ S(22, x); } + private final int Sig1(int x) { return S( 6, x) ^ S(11, x) ^ S(25, x); } + private final int sig0(int x) { return S( 7, x) ^ S(18, x) ^ R( 3, x); } + private final int sig1(int x) { return S(17, x) ^ S(19, x) ^ R(10, x); } + + private final int R(int off, int x) { return (x >>> off); } + private final int S(int off, int x) { return (x>>>off) | (x<<(32-off)); } + + private int privateDigest(byte[] buf, int offset, int len, boolean reset) + { + //#ASSERT(this.bufOff < BLOCK_SIZE); + + this.buf[this.bufOff++] = (byte)0x80; + + int lenOfBitLen = 8; + int C = BLOCK_SIZE - lenOfBitLen; + if(this.bufOff > C) { + while(this.bufOff < BLOCK_SIZE) + this.buf[this.bufOff++] = (byte)0x00; + + coreUpdate(this.buf, 0); + this.bufOff = 0; + } + + while(this.bufOff < C) + this.buf[this.bufOff++] = (byte)0x00; + + long bitCount = byteCount * 8; + + for(int i=56; i>=0; i-=8) + this.buf[this.bufOff++] = (byte)(bitCount >>> (i) ); + + coreUpdate(this.buf, 0); + coreDigest(buf, offset); + + if (reset) coreReset(); + return HASH_SIZE; + } + + + // Freenet Digest interface methods + + /** + * retrieve the value of a hash, by filling the provided int[] with + * n elements of the hash (where n is the bitlength of the hash/32) + * @param digest int[] into which to place n elements + * @param offset index of first of the n elements + **/ + public void extract(int [] digest, int offset) { + System.arraycopy(context, 0, digest, offset, context.length); + } + + /** + * Add one byte to the digest. When this is implemented + * all of the abstract class methods end up calling + * this method for types other than bytes. + * @param b byte to add + */ + public void update(byte b) { + + byteCount += 1; + buf[bufOff++] = b; + if( bufOff==BLOCK_SIZE ) { + coreUpdate(buf, 0); + bufOff = 0; + } + + } + + /** + * Add many bytes to the digest. + * @param input byte data to add + * @param offset start byte + * @param length number of bytes to hash + */ + public void update(byte[] input, int offset, int length) { + byteCount += length; + + int todo; + while( length >= (todo = BLOCK_SIZE - this.bufOff) ) { + System.arraycopy(input, offset, this.buf, this.bufOff, todo); + coreUpdate(this.buf, 0); + length -= todo; + offset += todo; + this.bufOff = 0; + } + + System.arraycopy(input, offset, this.buf, this.bufOff, length); + bufOff += length; + } + + public void update(byte[] data) { + update(data, 0, data.length); + } + + /** + * Returns the completed digest, reinitializing the hash function; + * @return the byte array result + */ + public byte[] digest() { + byte[] tmp = new byte[HASH_SIZE]; + privateDigest(tmp, 0, HASH_SIZE, true); + return tmp; + } + + /** + * It won't reset the Message Digest for you! + * @param InputStream + * @param MessageDigest + * @return + * @throws IOException + */ + public static void hash(InputStream is, MessageDigest md) throws IOException { + try { + byte[] buf = new byte[4096]; + int readBytes = is.read(buf); + while(readBytes > -1) { + md.update(buf, 0, readBytes); + readBytes = is.read(buf); + } + } finally { + if(is != null) is.close(); + } + } + + public static byte[] hash(InputStream is) throws IOException { + MessageDigest md = SHA256.getMessageDigest(); + md.reset(); + hash(is, md); + byte[] result = md.digest(); + SHA256.returnMessageDigest(md); + + return result; + } + + + /** + * Write the completed digest into the given buffer. + * @param reset If true, the hash function is reinitialized + */ + public void digest(boolean reset, byte[] buffer, int offset) { + privateDigest(buf, offset, buffer.length, true); + } + + /** + * Return the hash size of this digest in bits + */ + public final int digestSize() { + return HASH_SIZE<<3; + } + + public String doHash(String s) { + coreReset(); + for (int i=0; i<s.length(); i++) + { + this.update((byte) s.charAt(i)); + } + byte[] d=digest(); + return HexUtil.bytesToHex(d); + } + + static private final Vector digests = new Vector(); + + /** + * Create a new SHA-256 MessageDigest + * Either succeed or stop the node. + */ + public synchronized static MessageDigest getMessageDigest() { + try { + if(!digests.isEmpty()) return (MessageDigest) digests.remove(digests.size()-1); + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e2) { + //TODO: maybe we should point to a HOWTO for freejvms + System.err.println("Check your JVM settings especially the JCE!"+e2); + e2.printStackTrace(); + } + throw new RuntimeException(); + } + + /** + * Return a MessageDigest to the pool. + * Must be SHA-256 ! + */ + public synchronized static void returnMessageDigest(MessageDigest md256) { + if(md256 == null) return; + String algo = md256.getAlgorithm(); + if(!(algo.equals("SHA-256") || algo.equals("SHA256"))) + throw new IllegalArgumentException("Should be SHA-256 but is "+algo); + md256.reset(); + digests.add(md256); + } + + public static byte[] digest(byte[] data) { + MessageDigest md = getMessageDigest(); + byte[] hash = md.digest(data); + returnMessageDigest(md); + return hash; + } + + public static void main(String[] args) { + byte[] buffer=new byte[1024]; + SHA256 s=new SHA256(); + try { + while (true) { + int rc=System.in.read(buffer); + if (rc>0) + s.update(buffer, 0, rc); + else break; + } + } catch (java.io.IOException e) {} + byte[] rv=s.digest(); + System.out.println(HexUtil.bytesToHex(rv)); + } + + public static int getDigestLength() { + return 32; + } + + +} Copied: trunk/apps/Thaw/src/freenet/support/Base64.java (from rev 12729, trunk/freenet/src/freenet/support/Base64.java) =================================================================== --- trunk/apps/Thaw/src/freenet/support/Base64.java (rev 0) +++ trunk/apps/Thaw/src/freenet/support/Base64.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -0,0 +1,196 @@ +package freenet.support; + +import java.util.Random; + +/** + * This class provides encoding of byte arrays into Base64-encoded strings, + * and decoding the other way. + * + * <P>NOTE! This is modified Base64 with slightly different characters than + * usual, so it won't require escaping when used in URLs. + * + * <P>NOTE! This class only does the padding that's normal in Base64 + * if the 'true' flag is given to the encode() method. This is because + * Base64 requires that the length of the encoded text be a multiple + * of four characters, padded with '='. Without the 'true' flag, we don't + * add these '=' characters. + * + * @author Stephen Blackheath + */ +public class Base64 +{ + // Unit test + public static void main(String[] args) + throws IllegalBase64Exception + { + int iter; + Random r = new Random(); + for (iter = 0; iter < 1000; iter++) { + byte[] b = new byte[r.nextInt(64)]; + for (int i = 0; i < b.length; i++) + b[i] = (byte) (r.nextInt(256)); + String encoded = encode(b); + System.out.println(encoded); + byte[] decoded = decode(encoded); + if (decoded.length != b.length) { + System.out.println("length mismatch"); + return; + } + for (int i = 0; i < b.length; i++) + if (b[i] != decoded[i]) { + System.out.println("data mismatch: index "+i+" of "+b.length+" should be 0x"+Integer.toHexString(b[i] & 0xFF)+ + " was 0x"+Integer.toHexString(decoded[i] & 0xFF)); + return; + } + } + System.out.println("passed "+iter+" tests"); + } + + private static char[] base64Alphabet = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '~', '-'}; + + /** + * A reverse lookup table to convert base64 letters back into the + * a 6-bit sequence. + */ + private static byte[] base64Reverse; + // Populate the base64Reverse lookup table from the base64Alphabet table. + static { + base64Reverse = new byte[128]; + // Set all entries to 0xFF, which means that that particular letter + // is not a legal base64 letter. + for (int i = 0; i < base64Reverse.length; i++) + base64Reverse[i] = (byte) 0xFF; + for (int i = 0; i < base64Alphabet.length; i++) + base64Reverse[base64Alphabet[i]] = (byte) i; + } + + /** + * Encode to our shortened (non-standards-compliant) format. + */ + public static String encode(byte[] in) + { + return encode(in, false); + } + + /** + * Caller should specify equalsPad=true if they want a standards compliant encoding. + */ + public static String encode(byte[] in, boolean equalsPad) + { + char[] out = new char[((in.length+2)/3)*4]; + int rem = in.length%3; + int o = 0; + for (int i = 0; i < in.length;) { + int val = (in[i++] & 0xFF) << 16; + if (i < in.length) + val |= (in[i++] & 0xFF) << 8; + if (i < in.length) + val |= (in[i++] & 0xFF); + out[o++] = base64Alphabet[(val>>18) & 0x3F]; + out[o++] = base64Alphabet[(val>>12) & 0x3F]; + out[o++] = base64Alphabet[(val>>6) & 0x3F]; + out[o++] = base64Alphabet[val & 0x3F]; + } + int outLen = out.length; + switch (rem) { + case 1: outLen -= 2; break; + case 2: outLen -= 1; break; + } + // Pad with '=' signs up to a multiple of four if requested. + if (equalsPad) + while (outLen < out.length) + out[outLen++] = '='; + return new String(out, 0, outLen); + } + + /** + * Handles the standards-compliant (padded with '=' signs) as well as our + * shortened form. + */ + public static byte[] decode(String inStr) + throws IllegalBase64Exception + { + try { + char[] in = inStr.toCharArray(); + int inLength = in.length; + + // Strip trailing equals signs. + while ((inLength > 0) && (in[inLength-1] == '=')) + inLength--; + + int blocks = inLength/4; + int remainder = inLength & 3; + // wholeInLen and wholeOutLen are the the length of the input and output + // sequences respectively, not including any partial block at the end. + int wholeInLen = blocks*4; + int wholeOutLen = blocks*3; + int outLen = wholeOutLen; + switch (remainder) { + case 1: throw new IllegalBase64Exception("illegal Base64 length"); + case 2: outLen = wholeOutLen+1; break; + case 3: outLen = wholeOutLen+2; break; + default: outLen = wholeOutLen; + } + byte[] out = new byte[outLen]; + int o = 0; + int i; + for (i = 0; i < wholeInLen;) { + int in1 = base64Reverse[in[i]]; + int in2 = base64Reverse[in[i+1]]; + int in3 = base64Reverse[in[i+2]]; + int in4 = base64Reverse[in[i+3]]; + int orValue = in1|in2|in3|in4; + if ((orValue & 0x80) != 0) + throw new IllegalBase64Exception("illegal Base64 character"); + int outVal = (in1 << 18) | (in2 << 12) | (in3 << 6) | in4; + out[o] = (byte) (outVal>>16); + out[o+1] = (byte) (outVal>>8); + out[o+2] = (byte) outVal; + i += 4; + o += 3; + } + int orValue; + switch (remainder) { + case 2: + { + int in1 = base64Reverse[in[i]]; + int in2 = base64Reverse[in[i+1]]; + orValue = in1|in2; + int outVal = (in1 << 18) | (in2 << 12); + out[o] = (byte) (outVal>>16); + } + break; + case 3: + { + int in1 = base64Reverse[in[i]]; + int in2 = base64Reverse[in[i+1]]; + int in3 = base64Reverse[in[i+2]]; + orValue = in1|in2|in3; + int outVal = (in1 << 18) | (in2 << 12) | (in3 << 6); + out[o] = (byte) (outVal>>16); + out[o+1] = (byte) (outVal>>8); + } + break; + default: + // Keep compiler happy + orValue = 0; + } + if ((orValue & 0x80) != 0) + throw new IllegalBase64Exception("illegal Base64 character"); + return out; + } + // Illegal characters can cause an ArrayIndexOutOfBoundsException when + // looking up base64Reverse. + catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalBase64Exception("illegal Base64 character"); + } + } +} Copied: trunk/apps/Thaw/src/freenet/support/HexUtil.java (from rev 12729, trunk/freenet/src/freenet/support/HexUtil.java) =================================================================== --- trunk/apps/Thaw/src/freenet/support/HexUtil.java (rev 0) +++ trunk/apps/Thaw/src/freenet/support/HexUtil.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -0,0 +1,219 @@ +package freenet.support; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.util.BitSet; + +/** + * Number in hexadecimal format are used throughout Freenet. + * + * <p>Unless otherwise stated, the conventions follow the rules outlined in the + * Java Language Specification.</p> + * + * @author syoung + */ +public class HexUtil { + + private HexUtil() { + } + + + /** + * Converts a byte array into a string of upper case hex chars. + * + * @param bs + * A byte array + * @param off + * The index of the first byte to read + * @param length + * The number of bytes to read. + * @return the string of hex chars. + */ + public static final String bytesToHex(byte[] bs, int off, int length) { + StringBuffer sb = new StringBuffer(length * 2); + bytesToHexAppend(bs, off, length, sb); + return sb.toString(); + } + + public static final void bytesToHexAppend( + byte[] bs, + int off, + int length, + StringBuffer sb) { + sb.ensureCapacity(sb.length() + length * 2); + for (int i = off; (i < (off + length)) && (i < bs.length); i++) { + sb.append(Character.forDigit((bs[i] >>> 4) & 0xf, 16)); + sb.append(Character.forDigit(bs[i] & 0xf, 16)); + } + } + + public static final String bytesToHex(byte[] bs) { + return bytesToHex(bs, 0, bs.length); + } + + public static final byte[] hexToBytes(String s) { + return hexToBytes(s, 0); + } + + public static final byte[] hexToBytes(String s, int off) { + byte[] bs = new byte[off + (1 + s.length()) / 2]; + hexToBytes(s, bs, off); + return bs; + } + + /** + * Converts a String of hex characters into an array of bytes. + * + * @param s + * A string of hex characters (upper case or lower) of even + * length. + * @param out + * A byte array of length at least s.length()/2 + off + * @param off + * The first byte to write of the array + */ + public static final void hexToBytes(String s, byte[] out, int off) + throws NumberFormatException, IndexOutOfBoundsException { + int slen = s.length(); + if ((slen % 2) != 0) { + s = '0' + s; + } + + if (out.length < off + slen / 2) { + throw new IndexOutOfBoundsException( + "Output buffer too small for input (" + + out.length + + '<' + + off + + slen / 2 + + ')'); + } + + // Safe to assume the string is even length + byte b1, b2; + for (int i = 0; i < slen; i += 2) { + b1 = (byte) Character.digit(s.charAt(i), 16); + b2 = (byte) Character.digit(s.charAt(i + 1), 16); + if ((b1 < 0) || (b2 < 0)) { + throw new NumberFormatException(); + } + out[off + i / 2] = (byte) (b1 << 4 | b2); + } + } + + /** + * Pack the bits in ba into a byte[]. + */ + public final static byte[] bitsToBytes(BitSet ba, int size) { + int bytesAlloc = countBytesForBits(size); + byte[] b = new byte[bytesAlloc]; + + for(int i=0;i<b.length;i++) { + short s = 0; + for(int j=0;j<8;j++) { + int idx = i*8+j; + boolean val = + idx > size ? false : + ba.get(idx); + s |= val ? (1<<j) : 0; + + } + if(s > 255) throw new IllegalStateException("WTF? s = "+s); + b[i] = (byte)s; + } + + return b; + } + + /** + * Pack the bits in ba into a byte[] then convert that + * to a hex string and return it. + */ + public final static String bitsToHexString(BitSet ba, int size) { + return bytesToHex(bitsToBytes(ba, size)); + } + + + /** + * @return the number of bytes required to represent the + * bitset + */ + public static int countBytesForBits(int size) { + // Brackets matter here! == takes precedence over the rest + return (size/8) + ((size % 8) == 0 ? 0:1); + } + + + /** + * Read bits from a byte array into a bitset + * @param b the byte[] to read from + * @param ba the bitset to write to + */ + public static void bytesToBits(byte[] b, BitSet ba, int maxSize) { + + int x = 0; + for(int i=0;i<b.length;i++) { + for(int j=0;j<8;j++) { + if(x > maxSize) break; + int mask = 1 << j; + boolean value = (mask & b[i]) != 0; + ba.set(x, value); + x++; + } + } + } + + + /** + * Read a hex string of bits and write it into a bitset + * @param s hex string of the stored bits + * @param ba the bitset to store the bits in + * @param length the maximum number of bits to store + */ + public static void hexToBits(String s, BitSet ba, int length) { + byte[] b = hexToBytes(s); + bytesToBits(b, ba, length); + } + + /** + * Write a (reasonably short) BigInteger to a stream. + * @param integer the BigInteger to write + * @param out the stream to write it to + */ + public static void writeBigInteger(BigInteger integer, DataOutputStream out) throws IOException { + if(integer.signum() == -1) { + //dump("Negative BigInteger", Logger.ERROR, true); + throw new IllegalStateException("Negative BigInteger!"); + } + byte[] buf = integer.toByteArray(); + if(buf.length > Short.MAX_VALUE) + throw new IllegalStateException("Too long: "+buf.length); + out.writeShort((short)buf.length); + out.write(buf); + } + + /** + * Read a (reasonably short) BigInteger from a DataInputStream + * @param dis the stream to read from + * @return a BigInteger + */ + public static BigInteger readBigInteger(DataInputStream dis) throws IOException { + short i = dis.readShort(); + if(i < 0) throw new IOException("Invalid BigInteger length: "+i); + byte[] buf = new byte[i]; + dis.readFully(buf); + return new BigInteger(1,buf); + } + + + /** + * Turn a BigInteger into a hex string. + * BigInteger.toString(16) NPEs on Sun JDK 1.4.2_05. :< + * The bugs in their Big* are getting seriously irritating... + */ + public static String biToHex(BigInteger bi) { + return bytesToHex(bi.toByteArray()); + } +} Copied: trunk/apps/Thaw/src/freenet/support/IllegalBase64Exception.java (from rev 12729, trunk/freenet/src/freenet/support/IllegalBase64Exception.java) =================================================================== --- trunk/apps/Thaw/src/freenet/support/IllegalBase64Exception.java (rev 0) +++ trunk/apps/Thaw/src/freenet/support/IllegalBase64Exception.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -0,0 +1,16 @@ +package freenet.support; + +/** + * This exception is thrown if a Base64-encoded string is of an illegal length + * or contains an illegal character. + */ +public class IllegalBase64Exception + extends Exception +{ + + private static final long serialVersionUID = -1; + public IllegalBase64Exception(String descr) + { + super(descr); + } +} Modified: trunk/apps/Thaw/src/thaw/core/Logger.java =================================================================== --- trunk/apps/Thaw/src/thaw/core/Logger.java 2007-04-14 21:11:26 UTC (rev 12729) +++ trunk/apps/Thaw/src/thaw/core/Logger.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -22,7 +22,7 @@ * 2 or more is recommanded. * 4 or more is unhealthy */ - public final static int LOG_LEVEL = 3; + public final static int LOG_LEVEL = 5; private static Vector logListeners = null; Modified: trunk/apps/Thaw/src/thaw/fcp/FCPClientHello.java =================================================================== --- trunk/apps/Thaw/src/thaw/fcp/FCPClientHello.java 2007-04-14 21:11:26 UTC (rev 12729) +++ trunk/apps/Thaw/src/thaw/fcp/FCPClientHello.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -17,6 +17,7 @@ private final static String FCP_EXPECTED_VERSION = "2.0"; private String id; + private String connectionId; private String nodeFCPVersion; private String nodeVersion; private String nodeName = null; @@ -62,9 +63,12 @@ /** * Warning: This query is blocking (only this one) ! + * @param queueManager always null */ public boolean start(final FCPQueueManager queueManager) { + queryManager.getConnection().registerClientHello(this); + final FCPMessage message = new FCPMessage(); message.setMessageName("ClientHello"); @@ -113,6 +117,7 @@ if("NodeHello".equals( answer.getMessageName() )) { Logger.info(this, "Received a nodeHello"); + connectionId = answer.getValue("ConnectionIdentifier"); nodeFCPVersion = answer.getValue("FCPVersion"); nodeVersion = answer.getValue("Version"); nodeName = answer.getValue("Node"); @@ -155,5 +160,9 @@ return 0; } + + public String getConnectionId() { + return connectionId; + } } Modified: trunk/apps/Thaw/src/thaw/fcp/FCPClientPut.java =================================================================== --- trunk/apps/Thaw/src/thaw/fcp/FCPClientPut.java 2007-04-14 21:11:26 UTC (rev 12729) +++ trunk/apps/Thaw/src/thaw/fcp/FCPClientPut.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -50,6 +50,8 @@ private final static int PACKET_SIZE = 1024; + private SHA256Computer sha; + /** * To resume query from file. (see core.QueueKeeper) */ @@ -199,6 +201,26 @@ successful = false; running = false; + sha = null; + + if (queueManager.getQueryManager().getConnection().isLocalSocket()) { + sha = new SHA256Computer(queueManager.getQueryManager().getConnection().getClientHello().getConnectionId() + +"-"+ (localFile != null ? localFile.getPath() : "") + +"-", + localFile.getPath()); + sha.addObserver(this); + + Thread th = new Thread(sha); + th.start(); + } else { + return startProcess(); + } + + return true; + } + + + public boolean startProcess() { if((keyType == 2) && (privateKey == null)) { generateSSK(); } @@ -331,6 +353,9 @@ } else { msg.setValue("UploadFrom", "disk"); msg.setValue("Filename", localFile.getPath()); + + if (sha != null) + msg.setValue("FileHash", sha.getHash()); } Logger.info(this, "Sending "+Long.toString(fileSize)+" bytes on socket ..."); @@ -476,6 +501,11 @@ } public void update(final Observable o, final Object param) { + if (o == sha) { + startProcess(); + return; + } + if(o == sskGenerator) { privateKey = sskGenerator.getPrivateKey(); publicKey = sskGenerator.getPublicKey() + "/" + name; Modified: trunk/apps/Thaw/src/thaw/fcp/FCPConnection.java =================================================================== --- trunk/apps/Thaw/src/thaw/fcp/FCPConnection.java 2007-04-14 21:11:26 UTC (rev 12729) +++ trunk/apps/Thaw/src/thaw/fcp/FCPConnection.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -49,7 +49,9 @@ private boolean duplicationAllowed = true; private boolean localSocket = false; + private FCPClientHello clientHello; + /** * Don't connect. Call connect() for that. * @param maxUploadSpeed in KB: -1 means no limit @@ -469,4 +471,12 @@ return newConnection; } + + public void registerClientHello(FCPClientHello ch) { + clientHello = ch; + } + + public FCPClientHello getClientHello() { + return clientHello; + } } Added: trunk/apps/Thaw/src/thaw/fcp/SHA256Computer.java =================================================================== --- trunk/apps/Thaw/src/thaw/fcp/SHA256Computer.java (rev 0) +++ trunk/apps/Thaw/src/thaw/fcp/SHA256Computer.java 2007-04-14 22:08:49 UTC (rev 12730) @@ -0,0 +1,70 @@ +package thaw.fcp; + +import java.util.Observer; +import java.util.Observable; + +import java.io.FileInputStream; + +import freenet.crypt.SHA256; +import freenet.support.Base64; + +import thaw.core.Logger; + +public class SHA256Computer extends Observable implements Runnable { + private SHA256 sha; + + private String file; + + public SHA256Computer(String header, String fileToHash) { + file = fileToHash; + + sha = new SHA256(); + + sha.update(header.getBytes()); + } + + + public void run() { + try { + FileInputStream in = new FileInputStream(file); + + byte[] raw = new byte[32768]; /* 32 Ko */ + + while(in.available() > 0) { + in.read(raw); + sha.update(raw); + } + + in.close(); + } catch(java.io.FileNotFoundException e) { + Logger.error(this, "Can't hash file because: "+e.toString()); + sha = null; + } catch(java.io.IOException e) { + Logger.error(this, "Can't hash file because: "+e.toString()); + sha = null; + } + + setChanged(); + notifyObservers(); + } + + + /** + * In % + */ + public int getProgression() { + return 0; + } + + /** + * Returns the Base64Encode of the hash + */ + public String getHash() { + if (sha != null) + return Base64.encode(sha.digest()); + else { + Logger.warning(this, "No hash !"); + return null; + } + } +}
