Author: toad
Date: 2006-01-07 23:17:48 +0000 (Sat, 07 Jan 2006)
New Revision: 7810

Added:
   trunk/freenet/src/freenet/keys/InsertableClientSSK.java
   trunk/freenet/src/freenet/keys/KeyEncodeException.java
   trunk/freenet/src/freenet/keys/SSKEncodeException.java
Modified:
   trunk/freenet/src/freenet/crypt/DSA.java
   trunk/freenet/src/freenet/keys/CHKEncodeException.java
   trunk/freenet/src/freenet/keys/ClientCHK.java
   trunk/freenet/src/freenet/keys/ClientCHKBlock.java
   trunk/freenet/src/freenet/keys/ClientSSKBlock.java
   trunk/freenet/src/freenet/keys/Key.java
   trunk/freenet/src/freenet/keys/SSKBlock.java
   trunk/freenet/src/freenet/node/TextModeClientInterface.java
   trunk/freenet/src/freenet/node/Version.java
Log:
333:
One more step towards working SSKs.

Modified: trunk/freenet/src/freenet/crypt/DSA.java
===================================================================
--- trunk/freenet/src/freenet/crypt/DSA.java    2006-01-07 22:12:10 UTC (rev 
7809)
+++ trunk/freenet/src/freenet/crypt/DSA.java    2006-01-07 23:17:48 UTC (rev 
7810)
@@ -28,7 +28,7 @@
                                    Random r) {
        BigInteger k;
        do {
-           k=new NativeBigInteger(160, r);
+           k=new NativeBigInteger(256, r);
        } while (k.compareTo(g.getQ())>-1 || k.compareTo(BigInteger.ZERO)==0);
        return sign(g, x, k, m);
     }

Modified: trunk/freenet/src/freenet/keys/CHKEncodeException.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKEncodeException.java      2006-01-07 
22:12:10 UTC (rev 7809)
+++ trunk/freenet/src/freenet/keys/CHKEncodeException.java      2006-01-07 
23:17:48 UTC (rev 7810)
@@ -6,7 +6,7 @@
  * Exception thrown when a CHK encoding fails.
  * Specifically, it is thrown when the data is too big to encode.
  */
-public class CHKEncodeException extends Exception {
+public class CHKEncodeException extends KeyEncodeException {
        static final long serialVersionUID = -1;
     public CHKEncodeException() {
         super();

Modified: trunk/freenet/src/freenet/keys/ClientCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHK.java       2006-01-07 22:12:10 UTC 
(rev 7809)
+++ trunk/freenet/src/freenet/keys/ClientCHK.java       2006-01-07 23:17:48 UTC 
(rev 7810)
@@ -32,8 +32,6 @@
     static final short EXTRA_LENGTH = 5;
     /** The length of the decryption key */
     static final short CRYPTO_KEY_LENGTH = 32;
-    /** Code for 256-bit AES with PCFB */
-    static final short ALGO_AES_PCFB_256 = 1;

     /**
      * @param routingKey The routing key. This is the overall hash of the
@@ -68,7 +66,7 @@
         if(extra == null || extra.length < 5)
             throw new MalformedURLException();
         cryptoAlgorithm = (short)(((extra[0] & 0xff) << 8) + (extra[1] & 
0xff));
-               if(cryptoAlgorithm != ALGO_AES_PCFB_256)
+               if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256)
                        throw new MalformedURLException("Invalid crypto 
algorithm");
         controlDocument = (extra[2] & 0x02) != 0;
         compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] & 
0xff));
@@ -83,7 +81,7 @@
                byte[] extra = new byte[EXTRA_LENGTH];
                dis.readFully(extra);
         cryptoAlgorithm = (short)(((extra[0] & 0xff) << 8) + (extra[1] & 
0xff));
-               if(cryptoAlgorithm != ALGO_AES_PCFB_256)
+               if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256)
                        throw new MalformedURLException("Invalid crypto 
algorithm");
         compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] & 
0xff));
         controlDocument = (extra[2] & 0x02) != 0;

Modified: trunk/freenet/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHKBlock.java  2006-01-07 22:12:10 UTC 
(rev 7809)
+++ trunk/freenet/src/freenet/keys/ClientCHKBlock.java  2006-01-07 23:17:48 UTC 
(rev 7810)
@@ -11,6 +11,7 @@
 import freenet.crypt.PCFBMode;
 import freenet.crypt.UnsupportedCipherException;
 import freenet.crypt.ciphers.Rijndael;
+import freenet.keys.Key.Compressed;
 import freenet.node.Node;
 import freenet.support.ArrayBucket;
 import freenet.support.ArrayBucketFactory;
@@ -84,7 +85,7 @@
      */
     public Bucket decode(BucketFactory bf, int maxLength) throws 
CHKDecodeException, IOException {
         // Overall hash already verified, so first job is to decrypt.
-        if(key.cryptoAlgorithm != ClientCHK.ALGO_AES_PCFB_256)
+        if(key.cryptoAlgorithm != Key.ALGO_AES_PCFB_256)
             throw new UnsupportedOperationException();
         BlockCipher cipher;
         try {
@@ -150,69 +151,13 @@
         byte[] header;
         ClientCHK key;
         short compressionAlgorithm = -1;
-        // Try to compress it - even if it fits into the block,
-        // because compressing it improves its entropy.
-        boolean compressed = false;
-        if(sourceData.size() > MAX_LENGTH_BEFORE_COMPRESSION)
-            throw new CHKEncodeException("Too big");
-        if(!dontCompress) {
-               byte[] cbuf = null;
-               if(alreadyCompressedCodec >= 0) {
-                       if(sourceData.size() > MAX_COMPRESSED_DATA_LENGTH)
-                               throw new CHKEncodeException("Too big 
(precompressed)");
-                       compressionAlgorithm = alreadyCompressedCodec;
-                       cbuf = BucketTools.toByteArray(sourceData);
-                       if(sourceLength > MAX_LENGTH_BEFORE_COMPRESSION)
-                               throw new CHKEncodeException("Too big");
-               } else {
-                       if (sourceData.size() > NodeCHK.BLOCK_SIZE) {
-                                       // Determine the best algorithm
-                                       for (int i = 0; i < 
Compressor.countCompressAlgorithms(); i++) {
-                                               Compressor comp = Compressor
-                                                               
.getCompressionAlgorithmByDifficulty(i);
-                                               ArrayBucket compressedData;
-                                               try {
-                                                       compressedData = 
(ArrayBucket) comp.compress(
-                                                                       
sourceData, new ArrayBucketFactory(), NodeCHK.BLOCK_SIZE);
-                                               } catch (IOException e) {
-                                                       throw new Error(e);
-                                               } catch 
(CompressionOutputSizeException e) {
-                                                       continue;
-                                               }
-                                               if (compressedData.size() <= 
MAX_COMPRESSED_DATA_LENGTH) {
-                                                       compressionAlgorithm = 
comp
-                                                                       
.codecNumberForMetadata();
-                                                       sourceLength = 
sourceData.size();
-                                                       try {
-                                                               cbuf = 
BucketTools.toByteArray(compressedData);
-                                                               // FIXME 
provide a method in ArrayBucket
-                                                       } catch (IOException e) 
{
-                                                               throw new 
Error(e);
-                                                       }
-                                                       break;
-                                               }
-                                       }
-                               }
-                       
-               }
-               if(cbuf != null) {
-                       // Use it
-                       int compressedLength = cbuf.length;
-                finalData = new byte[compressedLength+4];
-                System.arraycopy(cbuf, 0, finalData, 4, compressedLength);
-                finalData[0] = (byte) ((sourceLength >> 24) & 0xff);
-                finalData[1] = (byte) ((sourceLength >> 16) & 0xff);
-                finalData[2] = (byte) ((sourceLength >> 8) & 0xff);
-                finalData[3] = (byte) ((sourceLength) & 0xff);
-                compressed = true;
-               }
-        }
-        if(finalData == null) {
-            if(sourceData.size() > NodeCHK.BLOCK_SIZE) {
-                throw new CHKEncodeException("Too big");
-            }
-               finalData = BucketTools.toByteArray(sourceData);
-        }
+        try {
+                       Compressed comp = Key.compress(sourceData, 
dontCompress, alreadyCompressedCodec, sourceLength, 
MAX_LENGTH_BEFORE_COMPRESSION, MAX_COMPRESSED_DATA_LENGTH);
+                       finalData = comp.compressedData;
+                       compressionAlgorithm = comp.compressionAlgorithm;
+               } catch (KeyEncodeException e2) {
+                       throw new CHKEncodeException(e2.getMessage(), e2);
+               }

         // Now do the actual encode

@@ -270,7 +215,7 @@
         byte[] finalHash = md256.digest(data);

         // Now convert it into a ClientCHK
-        key = new ClientCHK(finalHash, encKey, asMetadata, 
ClientCHK.ALGO_AES_PCFB_256, compressionAlgorithm);
+        key = new ClientCHK(finalHash, encKey, asMetadata, 
Key.ALGO_AES_PCFB_256, compressionAlgorithm);

         try {
             return new ClientCHKBlock(data, header, key, false);

Modified: trunk/freenet/src/freenet/keys/ClientSSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSKBlock.java  2006-01-07 22:12:10 UTC 
(rev 7809)
+++ trunk/freenet/src/freenet/keys/ClientSSKBlock.java  2006-01-07 23:17:48 UTC 
(rev 7810)
@@ -21,8 +21,8 @@
        /** Client-key. This contains the decryption key etc. */
        private ClientSSK key;

-       public ClientSSKBlock(byte[] data, byte[] headers, ClientSSK key) 
throws SSKVerifyException {
-               super(data, headers, (NodeSSK) key.getNodeKey(), false);
+       public ClientSSKBlock(byte[] data, byte[] headers, ClientSSK key, 
boolean dontVerify) throws SSKVerifyException {
+               super(data, headers, (NodeSSK) key.getNodeKey(), dontVerify);
        }

        /**
@@ -31,8 +31,8 @@
        public Bucket decode(BucketFactory factory, int maxLength) throws 
KeyDecodeException, IOException {
                /* We know the signature is valid because it is checked in the 
constructor. */
                /* We also know e(h(docname)) is valid */
-               byte[] decryptedHeaders = new byte[headers.length - 
headersOffset];
-               System.arraycopy(headers, headersOffset, decryptedHeaders, 0, 
headers.length - headersOffset);
+               byte[] decryptedHeaders = new byte[ENCRYPTED_HEADERS_LENGTH];
+               System.arraycopy(headers, headersOffset, decryptedHeaders, 0, 
ENCRYPTED_HEADERS_LENGTH);
                Rijndael aes;
                try {
                        aes = new Rijndael(256,256);

Added: trunk/freenet/src/freenet/keys/InsertableClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableClientSSK.java     2006-01-07 
22:12:10 UTC (rev 7809)
+++ trunk/freenet/src/freenet/keys/InsertableClientSSK.java     2006-01-07 
23:17:48 UTC (rev 7810)
@@ -0,0 +1,161 @@
+package freenet.keys;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import net.i2p.util.NativeBigInteger;
+
+import org.spaceroots.mantissa.random.MersenneTwister;
+
+import freenet.crypt.DSA;
+import freenet.crypt.DSAPrivateKey;
+import freenet.crypt.DSAPublicKey;
+import freenet.crypt.DSASignature;
+import freenet.crypt.PCFBMode;
+import freenet.crypt.RandomSource;
+import freenet.crypt.UnsupportedCipherException;
+import freenet.crypt.ciphers.Rijndael;
+import freenet.keys.Key.Compressed;
+import freenet.support.Bucket;
+
+public class InsertableClientSSK extends ClientSSK {
+
+       public final DSAPrivateKey privKey;
+       
+       public InsertableClientSSK(String docName, DSAPublicKey pubKey, 
DSAPrivateKey privKey, byte[] cryptoKey) {
+               super(docName, pubKey, cryptoKey);
+               this.privKey = privKey;
+       }
+       
+       public ClientSSKBlock encode(Bucket sourceData, boolean asMetadata, 
boolean dontCompress, short alreadyCompressedCodec, long sourceLength, 
RandomSource r) throws SSKEncodeException, IOException {
+               byte[] compressedData;
+               short compressionAlgo;
+               try {
+                       Compressed comp = Key.compress(sourceData, 
dontCompress, alreadyCompressedCodec, sourceLength, 
ClientSSKBlock.MAX_DECOMPRESSED_DATA_LENGTH, ClientSSKBlock.DATA_LENGTH);
+                       compressedData = comp.compressedData;
+                       compressionAlgo = comp.compressionAlgorithm;
+               } catch (KeyEncodeException e) {
+                       throw new SSKEncodeException(e.getMessage(), e);
+               }
+               // Pad it
+        MessageDigest md256;
+        try {
+            md256 = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e1) {
+            // FIXME: log this properly?
+            throw new Error(e1);
+        }
+        byte[] data;
+        // First pad it
+        if(compressedData.length != ClientSSKBlock.DATA_LENGTH) {
+            // Hash the data
+            if(compressedData.length != 0)
+               md256.update(compressedData);
+            byte[] digest = md256.digest();
+            MersenneTwister mt = new MersenneTwister(digest);
+            data = new byte[ClientSSKBlock.DATA_LENGTH];
+            System.arraycopy(compressedData, 0, data, 0, 
compressedData.length);
+            byte[] randomBytes = new 
byte[ClientSSKBlock.DATA_LENGTH-compressedData.length];
+            mt.nextBytes(randomBytes);
+            System.arraycopy(randomBytes, 0, data, compressedData.length, 
ClientSSKBlock.DATA_LENGTH-compressedData.length);
+        } else {
+               data = compressedData;
+        }
+        
+        // Implicit hash of data
+        byte[] origDataHash = md256.digest(data);
+
+        Rijndael aes;
+        try {
+                       aes = new Rijndael(256, 256);
+               } catch (UnsupportedCipherException e) {
+                       throw new Error("256/256 Rijndael not supported!");
+               }
+
+               // Encrypt data. Data encryption key = H(plaintext data).
+               
+               aes.initialize(origDataHash);
+               PCFBMode pcfb = new PCFBMode(aes);
+               
+               pcfb.blockEncipher(data, 0, data.length);
+               
+               byte[] encryptedDataHash = md256.digest(data);
+               
+        // Create headers
+        
+        byte[] headers = new byte[SSKBlock.TOTAL_HEADERS_LENGTH];
+        // First two bytes = hash ID
+        int x = 0;
+        headers[x++] = (byte) (ClientSSKBlock.HASH_SHA256 >> 8);
+        headers[x++] = (byte) (ClientSSKBlock.HASH_SHA256);
+        // Then crypto ID
+        headers[x++] = (byte) (Key.ALGO_AES_PCFB_256 >> 8);
+        headers[x++] = (byte) (Key.ALGO_AES_PCFB_256);
+        // Then E(H(docname))
+        byte[] hDocname = md256.digest(docName.getBytes("UTF-8"));
+               aes.initialize(cryptoKey);
+               aes.encipher(hDocname, hDocname);
+               // Copy to headers
+               System.arraycopy(hDocname, 0, headers, x, hDocname.length);
+               x += hDocname.length;
+               // Now the encrypted headers
+               byte[] encryptedHeaders = new 
byte[SSKBlock.ENCRYPTED_HEADERS_LENGTH];
+               System.arraycopy(origDataHash, 0, encryptedHeaders, 0, 
origDataHash.length);
+               int y = origDataHash.length;
+               short len = (short) compressedData.length;
+               if(asMetadata) len |= 32768;
+               encryptedHeaders[y++] = (byte)(len >> 8);
+               encryptedHeaders[y++] = (byte)len;
+               encryptedHeaders[y++] = (byte)(compressionAlgo >> 8);
+               encryptedHeaders[y++] = (byte)compressionAlgo;
+               if(encryptedHeaders.length != y)
+                       throw new IllegalStateException("Have more bytes to 
generate encoding SSK");
+               aes.initialize(cryptoKey);
+               pcfb.reset(hDocname);
+               pcfb.blockEncipher(encryptedHeaders, 0, 
encryptedHeaders.length);
+               System.arraycopy(encryptedHeaders, 0, headers, x, 
encryptedHeaders.length);
+               x+=encryptedHeaders.length;
+               // Generate implicit overall hash.
+               md256.update(headers, 0, x);
+               md256.update(encryptedDataHash);
+               byte[] overallHash = md256.digest();
+               // Now sign it
+               DSASignature sig = DSA.sign(pubKey.getGroup(), privKey, new 
NativeBigInteger(1, overallHash), r);
+               // Pack R and S into 32 bytes each, and copy to headers.
+               
+               // Then create and return the ClientSSKBlock.
+               byte[] rBuf = truncate(sig.getR().toByteArray(), 
ClientSSKBlock.SIG_R_LENGTH);
+               byte[] sBuf = truncate(sig.getS().toByteArray(), 
ClientSSKBlock.SIG_S_LENGTH);
+               System.arraycopy(rBuf, 0, headers, x, rBuf.length);
+               x+=rBuf.length;
+               System.arraycopy(sBuf, 0, headers, x, sBuf.length);
+               x+=sBuf.length;
+               if(x != SSKBlock.TOTAL_HEADERS_LENGTH)
+                       throw new IllegalStateException("Too long");
+               try {
+                       return new ClientSSKBlock(data, headers, this, false); 
// FIXME set last arg to true to not verify
+               } catch (SSKVerifyException e) {
+                       throw new IllegalStateException("Impossible encoding 
error: "+e.getMessage(), e);
+               }
+       }
+
+       private byte[] truncate(byte[] bs, int len) {
+               if(bs.length == len)
+                       return bs;
+               else if (bs.length < len) {
+                       byte[] buf = new byte[len];
+                       System.arraycopy(bs, 0, buf, len - bs.length, 
bs.length);
+                       return buf;
+               } else { // if (bs.length > len) {
+                       for(int i=0;i<(bs.length-len);i++) {
+                               if(bs[i] != 0)
+                                       throw new IllegalStateException("Cannot 
truncate");
+                       }
+                       byte[] buf = new byte[len];
+                       System.arraycopy(bs, 0, buf, (bs.length-len), 
buf.length);
+                       return buf;
+               }
+       }
+       
+}

Modified: trunk/freenet/src/freenet/keys/Key.java
===================================================================
--- trunk/freenet/src/freenet/keys/Key.java     2006-01-07 22:12:10 UTC (rev 
7809)
+++ trunk/freenet/src/freenet/keys/Key.java     2006-01-07 23:17:48 UTC (rev 
7810)
@@ -7,6 +7,8 @@
 import java.security.NoSuchAlgorithmException;

 import freenet.io.WritableToDataOutputStream;
+import freenet.support.ArrayBucket;
+import freenet.support.ArrayBucketFactory;
 import freenet.support.Bucket;
 import freenet.support.BucketFactory;
 import freenet.support.BucketTools;
@@ -30,6 +32,8 @@

     /** 32 bytes for hash, 2 bytes for type */
     public static final short KEY_SIZE_ON_DISK = 34;
+    /** Code for 256-bit AES with PCFB */
+    static final short ALGO_AES_PCFB_256 = 1;

     protected Key(byte[] routingKey) {
        this.routingKey = routingKey;
@@ -116,6 +120,85 @@
         }
        }

+    static class Compressed {
+       public Compressed(byte[] finalData, short compressionAlgorithm2) {
+               this.compressedData = finalData;
+               this.compressionAlgorithm = compressionAlgorithm2;
+               }
+               byte[] compressedData;
+       short compressionAlgorithm;
+    }
+    
+    static Compressed compress(Bucket sourceData, boolean dontCompress, short 
alreadyCompressedCodec, long sourceLength, long MAX_LENGTH_BEFORE_COMPRESSION, 
long MAX_COMPRESSED_DATA_LENGTH) throws KeyEncodeException, IOException {
+       byte[] finalData = null;
+        short compressionAlgorithm = -1;
+        // Try to compress it - even if it fits into the block,
+        // because compressing it improves its entropy.
+        boolean compressed = false;
+        if(sourceData.size() > MAX_LENGTH_BEFORE_COMPRESSION)
+            throw new KeyEncodeException("Too big");
+        if(!dontCompress) {
+               byte[] cbuf = null;
+               if(alreadyCompressedCodec >= 0) {
+                       if(sourceData.size() > MAX_COMPRESSED_DATA_LENGTH)
+                               throw new KeyEncodeException("Too big 
(precompressed)");
+                       compressionAlgorithm = alreadyCompressedCodec;
+                       cbuf = BucketTools.toByteArray(sourceData);
+                       if(sourceLength > MAX_LENGTH_BEFORE_COMPRESSION)
+                               throw new CHKEncodeException("Too big");
+               } else {
+                       if (sourceData.size() > NodeCHK.BLOCK_SIZE) {
+                                       // Determine the best algorithm
+                                       for (int i = 0; i < 
Compressor.countCompressAlgorithms(); i++) {
+                                               Compressor comp = Compressor
+                                                               
.getCompressionAlgorithmByDifficulty(i);
+                                               ArrayBucket compressedData;
+                                               try {
+                                                       compressedData = 
(ArrayBucket) comp.compress(
+                                                                       
sourceData, new ArrayBucketFactory(), NodeCHK.BLOCK_SIZE);
+                                               } catch (IOException e) {
+                                                       throw new Error(e);
+                                               } catch 
(CompressionOutputSizeException e) {
+                                                       continue;
+                                               }
+                                               if (compressedData.size() <= 
MAX_COMPRESSED_DATA_LENGTH) {
+                                                       compressionAlgorithm = 
comp
+                                                                       
.codecNumberForMetadata();
+                                                       sourceLength = 
sourceData.size();
+                                                       try {
+                                                               cbuf = 
BucketTools.toByteArray(compressedData);
+                                                               // FIXME 
provide a method in ArrayBucket
+                                                       } catch (IOException e) 
{
+                                                               throw new 
Error(e);
+                                                       }
+                                                       break;
+                                               }
+                                       }
+                               }
+                       
+               }
+               if(cbuf != null) {
+                       // Use it
+                       int compressedLength = cbuf.length;
+                finalData = new byte[compressedLength+4];
+                System.arraycopy(cbuf, 0, finalData, 4, compressedLength);
+                finalData[0] = (byte) ((sourceLength >> 24) & 0xff);
+                finalData[1] = (byte) ((sourceLength >> 16) & 0xff);
+                finalData[2] = (byte) ((sourceLength >> 8) & 0xff);
+                finalData[3] = (byte) ((sourceLength) & 0xff);
+                compressed = true;
+               }
+        }
+        if(finalData == null) {
+            if(sourceData.size() > NodeCHK.BLOCK_SIZE) {
+                throw new CHKEncodeException("Too big");
+            }
+               finalData = BucketTools.toByteArray(sourceData);
+        }
+
+        return new Compressed(finalData, compressionAlgorithm);
+    }
+    
     public byte[] getRoutingKey() {
        return routingKey;
     }

Added: trunk/freenet/src/freenet/keys/KeyEncodeException.java
===================================================================
--- trunk/freenet/src/freenet/keys/KeyEncodeException.java      2006-01-07 
22:12:10 UTC (rev 7809)
+++ trunk/freenet/src/freenet/keys/KeyEncodeException.java      2006-01-07 
23:17:48 UTC (rev 7810)
@@ -0,0 +1,21 @@
+package freenet.keys;
+
+public class KeyEncodeException extends Exception {
+
+       public KeyEncodeException(String string) {
+               super(string);
+       }
+
+       public KeyEncodeException() {
+               super();
+       }
+
+       public KeyEncodeException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       public KeyEncodeException(Throwable cause) {
+               super(cause);
+       }
+
+}

Modified: trunk/freenet/src/freenet/keys/SSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/SSKBlock.java        2006-01-07 22:12:10 UTC 
(rev 7809)
+++ trunk/freenet/src/freenet/keys/SSKBlock.java        2006-01-07 23:17:48 UTC 
(rev 7810)
@@ -21,18 +21,19 @@
        final int headersOffset;
        /* HEADERS FORMAT:
         * 2 bytes - hash ID
-        * SIGNATURE ON THE BELOW HASH:
-        *  32 bytes - signature: R (unsigned bytes)
-        *  32 bytes - signature: S (unsigned bytes)
-        * IMPLICIT - hash of remaining fields, including the implicit hash of 
data
-        * IMPLICIT - hash of data
         * 2 bytes - symmetric cipher ID
         * 32 bytes - E(H(docname))
         * ENCRYPTED WITH E(H(docname)) AS IV:
         *  32 bytes - H(decrypted data), = data decryption key
         *  2 bytes - data length + metadata flag
         *  2 bytes - data compression algorithm or -1
+        * IMPLICIT - hash of remaining fields, including the implicit hash of 
data
+        * IMPLICIT - hash of data
         * 
+        * SIGNATURE ON THE ABOVE HASH:
+        *  32 bytes - signature: R (unsigned bytes)
+        *  32 bytes - signature: S (unsigned bytes)
+        * 
         * PLUS THE PUBKEY:
         *  Pubkey
         *  Group
@@ -50,6 +51,8 @@
     static public final short TOTAL_HEADERS_LENGTH = 2 + SIG_R_LENGTH + 
SIG_S_LENGTH + 2 + 
        E_H_DOCNAME_LENGTH + ClientSSKBlock.DATA_DECRYPT_KEY_LENGTH + 2 + 2;

+    static final short ENCRYPTED_HEADERS_LENGTH = 36;
+    
        /**
         * Initialize, and verify data, headers against key. Provided
         * key must have a pubkey, or we throw.
@@ -65,9 +68,6 @@
                        throw new SSKVerifyException("Data length wrong: 
"+data.length+" should be "+DATA_LENGTH);
                if(pubKey == null)
                        throw new SSKVerifyException("PubKey was null from 
"+nodeKey);
-        hashIdentifier = (short)(((headers[0] & 0xff) << 8) + (headers[1] & 
0xff));
-        if(hashIdentifier != HASH_SHA256)
-            throw new SSKVerifyException("Hash not SHA-256");
         MessageDigest md;
         try {
             md = MessageDigest.getInstance("SHA-256");
@@ -75,10 +75,20 @@
             throw new Error(e);
         }
         // Now verify it
+        hashIdentifier = (short)(((headers[0] & 0xff) << 8) + (headers[1] & 
0xff));
+        if(hashIdentifier != HASH_SHA256)
+            throw new SSKVerifyException("Hash not SHA-256");
+        int x = 2;
+               symCipherIdentifier = (short)(((headers[x] & 0xff) << 8) + 
(headers[x+1] & 0xff));
+               x+=2;
+               // Then E(H(docname))
+               byte[] ehDocname = new byte[E_H_DOCNAME_LENGTH];
+               System.arraycopy(headers, x, ehDocname, 0, ehDocname.length);
+               x+=E_H_DOCNAME_LENGTH;
+               headersOffset = x; // is index to start of encrypted headers
                // Extract the signature
                byte[] bufR = new byte[SIG_R_LENGTH];
                byte[] bufS = new byte[SIG_S_LENGTH];
-               int x = 2;
                if(x+SIG_R_LENGTH+SIG_S_LENGTH > headers.length)
                        throw new SSKVerifyException("Headers too short: 
"+headers.length+" should be at least "+x+SIG_R_LENGTH+SIG_S_LENGTH);
                if(!dontVerify)
@@ -91,8 +101,11 @@
                if(!dontVerify) {
                        md.update(data);
                        byte[] dataHash = md.digest();
+                       // All headers up to and not including the signature
+                       md.update(headers, 0, headersOffset);
+                       // Then the implicit data hash
                        md.update(dataHash);
-                       md.update(headers, x, headers.length - x);
+                       // Makes the implicit overall hash
                        byte[] overallHash = md.digest();
                        // Now verify it
                        NativeBigInteger r = new NativeBigInteger(1, bufR);
@@ -103,12 +116,6 @@
                        if(headers.length < x+2+E_H_DOCNAME_LENGTH)
                                throw new SSKVerifyException("Headers too short 
after sig verification: "+headers.length+" should be "+x+2+E_H_DOCNAME_LENGTH);
                }
-               symCipherIdentifier = (short)(((headers[x] & 0xff) << 8) + 
(headers[x+1] & 0xff));
-               x+=2;
-               byte[] ehDocname = new byte[E_H_DOCNAME_LENGTH];
-               System.arraycopy(headers, x, ehDocname, 0, ehDocname.length);
-               x+=E_H_DOCNAME_LENGTH;
-               headersOffset = x; // is index to start of e(h(docname))
                if(!Arrays.equals(ehDocname, nodeKey.encryptedHashedDocname))
                        throw new SSKVerifyException("E(H(docname)) wrong - 
wrong key??");
        }

Added: trunk/freenet/src/freenet/keys/SSKEncodeException.java
===================================================================
--- trunk/freenet/src/freenet/keys/SSKEncodeException.java      2006-01-07 
22:12:10 UTC (rev 7809)
+++ trunk/freenet/src/freenet/keys/SSKEncodeException.java      2006-01-07 
23:17:48 UTC (rev 7810)
@@ -0,0 +1,9 @@
+package freenet.keys;
+
+public class SSKEncodeException extends KeyEncodeException {
+
+       public SSKEncodeException(String message, KeyEncodeException e) {
+               super(message, e);
+       }
+
+}

Modified: trunk/freenet/src/freenet/node/TextModeClientInterface.java
===================================================================
--- trunk/freenet/src/freenet/node/TextModeClientInterface.java 2006-01-07 
22:12:10 UTC (rev 7809)
+++ trunk/freenet/src/freenet/node/TextModeClientInterface.java 2006-01-07 
23:17:48 UTC (rev 7810)
@@ -99,6 +99,8 @@
         System.out.println("GETCHKFILE:<filename> - Get the key that would be 
returned if we inserted the file.");
         System.out.println("PUTDIR:<path>[#<defaultfile>] - Put the entire 
directory from disk.");
         System.out.println("GETCHKDIR:<path>[#<defaultfile>] - Get the key 
that would be returned if we'd put the entire directory from disk.");
+        System.out.println("MAKESSK - Create an SSK keypair.");
+        System.out.println("PUTSSK:<insert uri>:<url to redirect to> - Insert 
an SSK redirect to a file already inserted.");
 //        System.out.println("PUBLISH:<name> - create a publish/subscribe 
stream called <name>");
 //        System.out.println("PUSH:<name>:<text> - publish a single line of 
text to the stream named");
 //        System.out.println("SUBSCRIBE:<key> - subscribe to a 
publish/subscribe stream by key");

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-01-07 22:12:10 UTC (rev 
7809)
+++ trunk/freenet/src/freenet/node/Version.java 2006-01-07 23:17:48 UTC (rev 
7810)
@@ -20,7 +20,7 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       public static final int buildNumber = 332;
+       public static final int buildNumber = 333;

        /** Oldest build of Fred we will talk to */
        public static final int lastGoodBuild = 332;


Reply via email to