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;
+               }
+       }
+}


Reply via email to