Author: toad
Date: 2005-12-16 22:40:41 +0000 (Fri, 16 Dec 2005)
New Revision: 7724

Added:
   trunk/freenet/src/freenet/keys/ClientKeyBlock.java
   trunk/freenet/src/freenet/keys/ClientSSK.java
   trunk/freenet/src/freenet/keys/ClientSSKBlock.java
   trunk/freenet/src/freenet/keys/SSKDecodeException.java
Modified:
   trunk/freenet/src/freenet/client/ArchiveManager.java
   trunk/freenet/src/freenet/client/Fetcher.java
   trunk/freenet/src/freenet/keys/CHKBlock.java
   trunk/freenet/src/freenet/keys/ClientCHK.java
   trunk/freenet/src/freenet/keys/ClientCHKBlock.java
   trunk/freenet/src/freenet/keys/ClientKey.java
   trunk/freenet/src/freenet/keys/Key.java
   trunk/freenet/src/freenet/keys/KeyBlock.java
   trunk/freenet/src/freenet/keys/SSKBlock.java
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/QueuedDataRequest.java
   trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java
   trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
   trunk/freenet/src/freenet/node/RequestStarterClient.java
   trunk/freenet/src/freenet/node/SimpleLowLevelClient.java
Log:
SSKs: Mostly ready, but still waiting for advice on groups from Scott, and no 
encoding yet.

Modified: trunk/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveManager.java        2005-12-16 
19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/client/ArchiveManager.java        2005-12-16 
22:40:41 UTC (rev 7724)
@@ -111,9 +111,10 @@
         * @param archiveType The archive type, defined in Metadata.
         * @return An archive handler. 
         */
-       public synchronized ArchiveHandler makeHandler(FreenetURI key, short 
archiveType) {
+       public synchronized ArchiveHandler makeHandler(FreenetURI key, short 
archiveType, boolean returnNullIfNotFound) {
                ArchiveHandler handler = getCached(key);
                if(handler != null) return handler;
+               if(returnNullIfNotFound) return null;
                handler = new ArchiveStoreContext(this, key, archiveType);
                putCached(key, handler);
                return handler;

Modified: trunk/freenet/src/freenet/client/Fetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/Fetcher.java       2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/client/Fetcher.java       2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -7,7 +7,9 @@
 import freenet.client.events.DecodedBlockEvent;
 import freenet.client.events.FetchedMetadataEvent;
 import freenet.client.events.GotBlockEvent;
+import freenet.keys.ClientCHK;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.FreenetURI;
 import freenet.keys.KeyBlock;
 import freenet.keys.KeyDecodeException;
@@ -107,7 +109,7 @@
                        throw new 
FetchException(FetchException.TOO_MUCH_RECURSION, ""+recursionLevel+" should be 
< "+ctx.maxRecursionLevel);

                // Do the fetch
-               KeyBlock block;
+               ClientKeyBlock block;
                try {
                        block = ctx.client.getKey(key, localOnly, 
ctx.starterClient, ctx.cacheLocalRequests);
                } catch (LowLevelGetException e) {
@@ -138,7 +140,7 @@

                Bucket data;
                try {
-                       data = block.decode(key, ctx.bucketFactory, (int) 
(Math.min(ctx.maxTempLength, Integer.MAX_VALUE)));
+                       data = block.decode(ctx.bucketFactory, (int) 
(Math.min(ctx.maxTempLength, Integer.MAX_VALUE)));
                } catch (KeyDecodeException e1) {
                        throw new 
FetchException(FetchException.BLOCK_DECODE_ERROR, e1.getMessage());
                } catch (IOException e) {
@@ -148,7 +150,7 @@

                ctx.eventProducer.produceEvent(new DecodedBlockEvent(key));

-               if(!key.isMetadata()) {
+               if(!block.isMetadata()) {
                        // Just return the data
                        return new FetchResult(dm, data);
                }
@@ -202,7 +204,7 @@
                        }
                        return runMetadata(dm, recursionLevel, key, 
metaStrings, metadata, container, thisKey, dontEnterImplicitArchives, 
localOnly);
                } else if(metadata.isArchiveManifest()) {
-                       container = ctx.archiveManager.makeHandler(thisKey, 
metadata.getArchiveType());
+                       container = ctx.archiveManager.makeHandler(thisKey, 
metadata.getArchiveType(), false);
                        Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                        try {
                                metadata = Metadata.construct(metadataBucket);
@@ -223,7 +225,7 @@
                                 */
                                if((!dontEnterImplicitArchives) && 
ArchiveManager.isUsableArchiveType(dm.getMIMEType()) && 
(!metaStrings.isEmpty())) {
                                        // Possible implicit archive inside 
archive?
-                                       container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()));
+                                       container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()), false);
                                        Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                                        try {
                                                metadata = 
Metadata.construct(metadataBucket);
@@ -250,17 +252,16 @@
                        FreenetURI uri = metadata.getSingleTarget();
                        dm.mergeNoOverwrite(metadata.getClientMetadata());
                        if((!dontEnterImplicitArchives) && 
ArchiveManager.isUsableArchiveType(dm.getMIMEType()) && 
(!metaStrings.isEmpty())) {
+                               // Is probably an implicit archive.
                                ClientKey target;
                                try {
                                        target = ClientKey.getBaseKey(uri);
                                } catch (MalformedURLException e1) {
                                        throw new 
FetchException(FetchException.INVALID_URI, "Invalid URI: "+uri);
                                }
-                               if(!(target.isMetadata())) {
-                                       // Target *is not* metadata.
-                                       // Therefore target is a usable archive.
-                                       // We might not have to fetch it.
-                                       container = 
ctx.archiveManager.makeHandler(uri, 
ArchiveManager.getArchiveType(dm.getMIMEType()));
+                               // Probably a usable archive as-is. We may not 
have to fetch it.
+                               container = ctx.archiveManager.makeHandler(uri, 
ArchiveManager.getArchiveType(dm.getMIMEType()), true);
+                               if(container != null) {
                                        Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                                        try {
                                                metadata = 
Metadata.construct(metadataBucket);
@@ -268,7 +269,7 @@
                                                throw new 
FetchException(FetchException.BUCKET_ERROR);
                                        }
                                        return runMetadata(dm, 
recursionLevel+1, key, metaStrings, metadata, container, thisKey, 
dontEnterImplicitArchives, localOnly);
-                               }
+                               } // else just fetch it, create context later
                        }
                        FetchResult fr = realRun(dm, recursionLevel, uri, 
dontEnterImplicitArchives, localOnly);
                        if(metadata.compressed) {
@@ -293,7 +294,7 @@
                        dm.mergeNoOverwrite(metadata.getClientMetadata()); // 
even splitfiles can have mime types!
                        if((!dontEnterImplicitArchives) && 
ArchiveManager.isUsableArchiveType(dm.getMIMEType()) && 
(!metaStrings.isEmpty())) {
                                // We know target is not metadata.
-                               container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()));
+                               container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()), false);
                                Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                                try {
                                        metadata = 
Metadata.construct(metadataBucket);

Modified: trunk/freenet/src/freenet/keys/CHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKBlock.java        2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/CHKBlock.java        2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -87,104 +87,6 @@
         // Otherwise it checks out
     }

-    /**
-     * Decode into RAM, if short.
-     * @throws CHKDecodeException 
-     */
-       public byte[] memoryDecode(ClientCHK chk) throws CHKDecodeException {
-               try {
-                       ArrayBucket a = (ArrayBucket) decode(chk, new 
ArrayBucketFactory(), 32*1024);
-                       return BucketTools.toByteArray(a); // FIXME
-               } catch (IOException e) {
-                       throw new Error(e);
-               }
-       }
-       
-    public Bucket decode(ClientKey key, BucketFactory bf, int maxLength) 
throws KeyDecodeException, IOException {
-       if(!(key instanceof ClientCHK))
-               throw new KeyDecodeException("Not a CHK!: "+key);
-       return decode((ClientCHK)key, bf, maxLength);
-    }
-    
-    /**
-     * Decode the CHK and recover the original data
-     * @return the original data
-     * @throws IOException If there is a bucket error.
-     */
-    public Bucket decode(ClientCHK key, 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)
-            throw new UnsupportedOperationException();
-        BlockCipher cipher;
-        try {
-            cipher = new Rijndael(256, 256);
-        } catch (UnsupportedCipherException e) {
-            // FIXME - log this properly
-            throw new Error(e);
-        }
-        byte[] cryptoKey = key.cryptoKey;
-        if(cryptoKey.length < Node.SYMMETRIC_KEY_LENGTH)
-            throw new CHKDecodeException("Crypto key too short");
-        cipher.initialize(key.cryptoKey);
-        PCFBMode pcfb = new PCFBMode(cipher);
-        byte[] hbuf = new byte[header.length-2];
-        System.arraycopy(header, 2, hbuf, 0, header.length-2);
-        byte[] dbuf = new byte[data.length];
-        System.arraycopy(data, 0, dbuf, 0, data.length);
-        // Decipher header first - functions as IV
-        pcfb.blockDecipher(hbuf, 0, hbuf.length);
-        pcfb.blockDecipher(dbuf, 0, dbuf.length);
-        // Check: Decryption key == hash of data (not including header)
-        MessageDigest md256;
-        try {
-            md256 = MessageDigest.getInstance("SHA-256");
-        } catch (NoSuchAlgorithmException e1) {
-            // FIXME: log this properly?
-            throw new Error(e1);
-        }
-        byte[] dkey = md256.digest(dbuf);
-        if(!java.util.Arrays.equals(dkey, key.cryptoKey)) {
-            throw new CHKDecodeException("Check failed: decrypt key == 
H(data)");
-        }
-        // Check: IV == hash of decryption key
-        byte[] predIV = md256.digest(dkey);
-        // Extract the IV
-        byte[] iv = new byte[32];
-        System.arraycopy(hbuf, 0, iv, 0, 32);
-        if(!Arrays.equals(iv, predIV))
-            throw new CHKDecodeException("Check failed: Decrypted IV == 
H(decryption key)");
-        // Checks complete
-        int size = ((hbuf[32] & 0xff) << 8) + (hbuf[33] & 0xff);
-        if(size > 32768 || size < 0)
-            throw new CHKDecodeException("Invalid size: "+size);
-        byte[] output = new byte[size];
-        // No particular reason to check the padding, is there?
-        System.arraycopy(dbuf, 0, output, 0, size);
-        return decompress(key, output, bf, maxLength);
-    }
-
-    private Bucket decompress(ClientCHK key, byte[] output, BucketFactory bf, 
int maxLength) throws CHKDecodeException, IOException {
-        if(key.isCompressed()) {
-               Logger.minor(this, "Decompressing in decode: "+key.getURI()+" 
with codec "+key.compressionAlgorithm);
-            if(output.length < 5) throw new CHKDecodeException("No bytes to 
decompress");
-            // Decompress
-            // First get the length
-            int len = ((((((output[0] & 0xff) << 8) + (output[1] & 0xff)) << 
8) + (output[2] & 0xff)) << 8) +
-               (output[3] & 0xff);
-            if(len > MAX_LENGTH_BEFORE_COMPRESSION)
-                throw new CHKDecodeException("Invalid precompressed size: 
"+len);
-            Compressor decompressor = 
Compressor.getCompressionAlgorithmByMetadataID(key.compressionAlgorithm);
-            Bucket inputBucket = new SimpleReadOnlyArrayBucket(output, 4, 
output.length-4);
-            try {
-                               return decompressor.decompress(inputBucket, bf, 
maxLength);
-                       } catch (CompressionOutputSizeException e) {
-                               throw new CHKDecodeException("Too big");
-                       }
-        } else {
-               return BucketTools.makeImmutableBucket(bf, output);
-        }
-       }
-
        public Key getKey() {
         return chk;
     }

Modified: trunk/freenet/src/freenet/keys/ClientCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHK.java       2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/ClientCHK.java       2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -119,18 +119,17 @@
                ","+cryptoAlgorithm;
     }

+       public Key getNodeKey() {
+               return getNodeCHK();
+       }

+       public NodeCHK getNodeCHK() {
+               if(nodeKey == null)
+               nodeKey = new NodeCHK(routingKey);
+           return nodeKey;
+       }
+       
     /**
-     * @return a NodeCHK corresponding to this key. Basically keep the 
-     * routingKey and lose everything else.
-     */
-    public NodeCHK getNodeCHK() {
-        if(nodeKey == null)
-            nodeKey = new NodeCHK(routingKey);
-        return nodeKey;
-    }
-
-    /**
      * @return URI form of this key.
      */
     public FreenetURI getURI() {

Modified: trunk/freenet/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHKBlock.java  2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/ClientCHKBlock.java  2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -3,6 +3,7 @@
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;

 import org.spaceroots.mantissa.random.MersenneTwister;

@@ -10,10 +11,14 @@
 import freenet.crypt.PCFBMode;
 import freenet.crypt.UnsupportedCipherException;
 import freenet.crypt.ciphers.Rijndael;
+import freenet.node.Node;
 import freenet.support.ArrayBucket;
 import freenet.support.ArrayBucketFactory;
 import freenet.support.Bucket;
+import freenet.support.BucketFactory;
 import freenet.support.BucketTools;
+import freenet.support.Logger;
+import freenet.support.SimpleReadOnlyArrayBucket;
 import freenet.support.compress.CompressionOutputSizeException;
 import freenet.support.compress.Compressor;
 import freenet.support.compress.DecompressException;
@@ -22,9 +27,9 @@
 /**
  * @author amphibian
  * 
- * Client CHKBlock - provides functions for decoding, holds a key.
+ * Client CHKBlock - provides functions for decoding, holds a client-key.
  */
-public class ClientCHKBlock extends CHKBlock {
+public class ClientCHKBlock extends CHKBlock implements ClientKeyBlock {

     public static final long MAX_COMPRESSED_DATA_LENGTH = NodeCHK.BLOCK_SIZE - 
4;
        final ClientCHK key;
@@ -54,6 +59,82 @@
     }

     /**
+     * Decode into RAM, if short.
+     * @throws CHKDecodeException 
+     */
+       public byte[] memoryDecode() throws CHKDecodeException {
+               try {
+                       ArrayBucket a = (ArrayBucket) decode(new 
ArrayBucketFactory(), 32*1024);
+                       return BucketTools.toByteArray(a); // FIXME
+               } catch (IOException e) {
+                       throw new Error(e);
+               }
+       }
+       
+    public Bucket decode(ClientKey key, BucketFactory bf, int maxLength) 
throws KeyDecodeException, IOException {
+       if(!(key instanceof ClientCHK))
+               throw new KeyDecodeException("Not a CHK!: "+key);
+       return decode((ClientCHK)key, bf, maxLength);
+    }
+    
+    /**
+     * Decode the CHK and recover the original data
+     * @return the original data
+     * @throws IOException If there is a bucket error.
+     */
+    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)
+            throw new UnsupportedOperationException();
+        BlockCipher cipher;
+        try {
+            cipher = new Rijndael(256, 256);
+        } catch (UnsupportedCipherException e) {
+            // FIXME - log this properly
+            throw new Error(e);
+        }
+        byte[] cryptoKey = key.cryptoKey;
+        if(cryptoKey.length < Node.SYMMETRIC_KEY_LENGTH)
+            throw new CHKDecodeException("Crypto key too short");
+        cipher.initialize(key.cryptoKey);
+        PCFBMode pcfb = new PCFBMode(cipher);
+        byte[] hbuf = new byte[header.length-2];
+        System.arraycopy(header, 2, hbuf, 0, header.length-2);
+        byte[] dbuf = new byte[data.length];
+        System.arraycopy(data, 0, dbuf, 0, data.length);
+        // Decipher header first - functions as IV
+        pcfb.blockDecipher(hbuf, 0, hbuf.length);
+        pcfb.blockDecipher(dbuf, 0, dbuf.length);
+        // Check: Decryption key == hash of data (not including header)
+        MessageDigest md256;
+        try {
+            md256 = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e1) {
+            // FIXME: log this properly?
+            throw new Error(e1);
+        }
+        byte[] dkey = md256.digest(dbuf);
+        if(!java.util.Arrays.equals(dkey, key.cryptoKey)) {
+            throw new CHKDecodeException("Check failed: decrypt key == 
H(data)");
+        }
+        // Check: IV == hash of decryption key
+        byte[] predIV = md256.digest(dkey);
+        // Extract the IV
+        byte[] iv = new byte[32];
+        System.arraycopy(hbuf, 0, iv, 0, 32);
+        if(!Arrays.equals(iv, predIV))
+            throw new CHKDecodeException("Check failed: Decrypted IV == 
H(decryption key)");
+        // Checks complete
+        int size = ((hbuf[32] & 0xff) << 8) + (hbuf[33] & 0xff);
+        if(size > 32768 || size < 0)
+            throw new CHKDecodeException("Invalid size: "+size);
+        byte[] output = new byte[size];
+        // No particular reason to check the padding, is there?
+        System.arraycopy(dbuf, 0, output, 0, size);
+        return Key.decompress(key, output, bf, maxLength, 
key.compressionAlgorithm, Math.min(maxLength, MAX_LENGTH_BEFORE_COMPRESSION));
+    }
+
+    /**
      * Encode a Bucket of data to a CHKBlock.
      * @param sourceData The bucket of data to encode. Can be arbitrarily 
large.
      * @param asMetadata Is this a metadata key?
@@ -223,4 +304,8 @@
         return key;
     }

+       public boolean isMetadata() {
+               return key.isMetadata();
+       }
+
 }

Modified: trunk/freenet/src/freenet/keys/ClientKey.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientKey.java       2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/ClientKey.java       2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -13,12 +13,13 @@
                        return new ClientCHK(origURI);
                throw new UnsupportedOperationException("Unknown keytype from 
"+origURI);
        }
+       
+       public abstract FreenetURI getURI();

        /**
-        * Does the key contain metadata? If not, it contains real data.
+        * @return a NodeCHK corresponding to this key. Basically keep the 
+        * routingKey and lose everything else.
         */
-       public abstract boolean isMetadata();
+       public abstract Key getNodeKey();

-       public abstract FreenetURI getURI();
-
 }

Added: trunk/freenet/src/freenet/keys/ClientKeyBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientKeyBlock.java  2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/ClientKeyBlock.java  2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -0,0 +1,21 @@
+package freenet.keys;
+
+import java.io.IOException;
+
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+
+public interface ClientKeyBlock {
+
+       /** Decode with the key
+        * @param factory The BucketFactory to use to create the Bucket to 
return the data in.
+        * @param maxLength The maximum size of the returned data in bytes.
+        */
+       Bucket decode(BucketFactory factory, int maxLength) throws 
KeyDecodeException, IOException;
+
+       /**
+        * Does the block contain metadata? If not, it contains real data.
+        */
+       boolean isMetadata();
+
+}

Added: trunk/freenet/src/freenet/keys/ClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSK.java       2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/ClientSSK.java       2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -0,0 +1,62 @@
+package freenet.keys;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import freenet.crypt.DSAPublicKey;
+import freenet.crypt.UnsupportedCipherException;
+import freenet.crypt.ciphers.Rijndael;
+
+public class ClientSSK extends ClientKey {
+
+       /** Document name */
+       public final String docName;
+       /** Public key */
+       public final DSAPublicKey pubKey;
+       /** Public key hash */
+       public final byte[] pubKeyHash;
+       /** Encryption key */
+       public final byte[] cryptoKey;
+       /** Encrypted hashed docname */
+       public final byte[] ehDocname;
+       
+       public ClientSSK(String docName, DSAPublicKey pubKey, byte[] cryptoKey) 
{
+               this.docName = docName;
+               this.pubKey = pubKey;
+               byte[] pubKeyAsBytes = pubKey.asBytes();
+               MessageDigest md;
+               try {
+                       md = MessageDigest.getInstance("SHA-256");
+                       md.update(pubKeyAsBytes);
+                       pubKeyHash = md.digest();
+               } catch (NoSuchAlgorithmException e) {
+                       throw new Error(e);
+               }
+               this.cryptoKey = cryptoKey;
+               try {
+                       md.update(docName.getBytes("UTF-8"));
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error(e);
+               }
+               byte[] buf = md.digest();
+               try {
+                       Rijndael aes = new Rijndael(256,256);
+                       aes.initialize(cryptoKey);
+                       aes.encipher(buf, buf);
+                       ehDocname = buf;
+               } catch (UnsupportedCipherException e) {
+                       throw new Error(e);
+               }
+               
+       }
+       
+       public FreenetURI getURI() {
+               return new FreenetURI("SSK", docName, pubKeyHash, cryptoKey, 
null);
+       }
+
+       public Key getNodeKey() {
+               return new NodeSSK(pubKeyHash, ehDocname);
+       }
+
+}

Added: trunk/freenet/src/freenet/keys/ClientSSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSKBlock.java  2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/ClientSSKBlock.java  2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -0,0 +1,85 @@
+package freenet.keys;
+
+import java.io.IOException;
+
+import freenet.crypt.PCFBMode;
+import freenet.crypt.UnsupportedCipherException;
+import freenet.crypt.ciphers.Rijndael;
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+
+public class ClientSSKBlock extends SSKBlock implements ClientKeyBlock {
+       
+       static final int DATA_DECRYPT_KEY_LENGTH = 32;
+       
+       /** Is metadata. Set on decode. */
+       private boolean isMetadata;
+       /** Has decoded? */
+       private boolean decoded;
+       /** 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());
+       }
+       
+       /**
+        * Decode the data.
+        */
+       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);
+               Rijndael aes;
+               try {
+                       aes = new Rijndael(256,256);
+               } catch (UnsupportedCipherException e) {
+                       throw new Error(e);
+               }
+               aes.initialize(key.cryptoKey);
+               PCFBMode pcfb = new PCFBMode(aes);
+               // ECB-encrypted E(H(docname)) serves as IV.
+               pcfb.reset(key.ehDocname);
+               pcfb.blockDecipher(decryptedHeaders, 0, 
decryptedHeaders.length);
+               // First 32 bytes are the key
+               byte[] dataDecryptKey = new byte[DATA_DECRYPT_KEY_LENGTH];
+               System.arraycopy(decryptedHeaders, 0, dataDecryptKey, 0, 
DATA_DECRYPT_KEY_LENGTH);
+               aes.initialize(dataDecryptKey);
+               byte[] dataOutput = new byte[data.length];
+               System.arraycopy(data, 0, dataOutput, 0, data.length);
+               // Data decrypt key should be unique, so use it as IV
+               pcfb.reset(dataDecryptKey);
+               pcfb.blockDecipher(dataOutput, 0, dataOutput.length);
+               // 2 bytes - data length
+               int dataLength = ((decryptedHeaders[DATA_DECRYPT_KEY_LENGTH] & 
0xff) << 8) +
+                       (decryptedHeaders[DATA_DECRYPT_KEY_LENGTH+1] & 0xff);
+               // Metadata flag is top bit
+               if((dataLength & 32768) != 0) {
+                       dataLength = dataLength & ~32768;
+                       isMetadata = true;
+               }
+               if(dataLength > data.length) {
+                       throw new SSKDecodeException("Data length: 
"+dataLength+" but data.length="+data.length);
+               }
+               
+               if(dataLength != data.length) {
+                       byte[] realDataOutput = new byte[dataLength];
+                       System.arraycopy(dataOutput, 0, realDataOutput, 0, 
dataLength);
+                       dataOutput = realDataOutput;
+               }
+        short compressionAlgorithm = 
(short)(((decryptedHeaders[DATA_DECRYPT_KEY_LENGTH+2] & 0xff) << 8) + 
(decryptedHeaders[DATA_DECRYPT_KEY_LENGTH+3] & 0xff));
+        
+               
+               decoded = true;
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       public boolean isMetadata() {
+               if(!decoded)
+                       throw new IllegalStateException("Cannot read isMetadata 
before decoded");
+               return isMetadata;
+       }
+
+}

Modified: trunk/freenet/src/freenet/keys/Key.java
===================================================================
--- trunk/freenet/src/freenet/keys/Key.java     2005-12-16 19:01:17 UTC (rev 
7723)
+++ trunk/freenet/src/freenet/keys/Key.java     2005-12-16 22:40:41 UTC (rev 
7724)
@@ -7,7 +7,14 @@
 import java.security.NoSuchAlgorithmException;

 import freenet.io.WritableToDataOutputStream;
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+import freenet.support.BucketTools;
 import freenet.support.Fields;
+import freenet.support.Logger;
+import freenet.support.SimpleReadOnlyArrayBucket;
+import freenet.support.compress.CompressionOutputSizeException;
+import freenet.support.compress.Compressor;

 /**
  * @author amphibian
@@ -82,4 +89,27 @@
     public int hashCode() {
         return hash;
     }
+    
+    static Bucket decompress(ClientCHK key, byte[] output, BucketFactory bf, 
int maxLength, short compressionAlgorithm, int maxDecompressedLength) throws 
CHKDecodeException, IOException {
+        if(key.isCompressed()) {
+               Logger.minor(key, "Decompressing in decode: "+key.getURI()+" 
with codec "+compressionAlgorithm);
+            if(output.length < 5) throw new CHKDecodeException("No bytes to 
decompress");
+            // Decompress
+            // First get the length
+            int len = ((((((output[0] & 0xff) << 8) + (output[1] & 0xff)) << 
8) + (output[2] & 0xff)) << 8) +
+               (output[3] & 0xff);
+            if(len > maxDecompressedLength)
+                throw new CHKDecodeException("Invalid precompressed size: 
"+len);
+            Compressor decompressor = 
Compressor.getCompressionAlgorithmByMetadataID(compressionAlgorithm);
+            Bucket inputBucket = new SimpleReadOnlyArrayBucket(output, 4, 
output.length-4);
+            try {
+                               return decompressor.decompress(inputBucket, bf, 
maxLength);
+                       } catch (CompressionOutputSizeException e) {
+                               throw new CHKDecodeException("Too big");
+                       }
+        } else {
+               return BucketTools.makeImmutableBucket(bf, output);
+        }
+       }
+
 }

Modified: trunk/freenet/src/freenet/keys/KeyBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/KeyBlock.java        2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/KeyBlock.java        2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -12,11 +12,4 @@

     final static int HASH_SHA256 = 1;

-       /** Decode with the key
-        * @param key The ClientKey to use to decode the block. 
-        * @param factory The BucketFactory to use to create the Bucket to 
return the data in.
-        * @param maxLength The maximum size of the returned data in bytes.
-        */
-       Bucket decode(ClientKey key, BucketFactory factory, int maxLength) 
throws KeyDecodeException, IOException;
-
 }

Modified: trunk/freenet/src/freenet/keys/SSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/SSKBlock.java        2005-12-16 19:01:17 UTC 
(rev 7723)
+++ trunk/freenet/src/freenet/keys/SSKBlock.java        2005-12-16 22:40:41 UTC 
(rev 7724)
@@ -3,6 +3,7 @@
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;

 import net.i2p.util.NativeBigInteger;

@@ -20,7 +21,7 @@

        final byte[] data;
        final byte[] headers;
-       /** The index of the first non-signature-related byte in the headers */
+       /** The index of the first byte of encrypted fields in the headers, 
after E(H(docname)) */
        final int headersOffset;
        /* HEADERS FORMAT:
         * 2 bytes - hash ID
@@ -43,11 +44,13 @@
        final NodeSSK nodeKey;
        final DSAPublicKey pubKey;
     final short hashIdentifier;
+    final short symCipherIdentifier;

     static final short DATA_LENGTH = 1024;

     static final short SIG_R_LENGTH = 20;
     static final short SIG_S_LENGTH = 20;
+    static final short E_H_DOCNAME_LENGTH = 32;

        /**
         * Initialize, and verify data, headers against key. Provided
@@ -95,13 +98,16 @@
                if(!DSA.verify(pubKey, new DSASignature(r, s), new 
NativeBigInteger(1, overallHash))) {
                        throw new SSKVerifyException("Signature verification 
failed for node-level SSK");
                }
-               headersOffset = x;
+               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??");
        }
-       
-       public Bucket decode(ClientKey key, BucketFactory factory, int 
maxLength) throws KeyDecodeException, IOException {
-               
-               // TODO Auto-generated method stub
-               return null;
-       }

 }

Added: trunk/freenet/src/freenet/keys/SSKDecodeException.java
===================================================================
--- trunk/freenet/src/freenet/keys/SSKDecodeException.java      2005-12-16 
19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/keys/SSKDecodeException.java      2005-12-16 
22:40:41 UTC (rev 7724)
@@ -0,0 +1,9 @@
+package freenet.keys;
+
+public class SSKDecodeException extends KeyDecodeException {
+
+       public SSKDecodeException(String string) {
+               super(string);
+       }
+
+}

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2005-12-16 19:01:17 UTC (rev 
7723)
+++ trunk/freenet/src/freenet/node/Node.java    2005-12-16 22:40:41 UTC (rev 
7724)
@@ -45,6 +45,7 @@
 import freenet.keys.ClientCHK;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;
 import freenet.keys.NodeCHK;
 import freenet.store.BaseFreenetStore;
@@ -429,14 +430,14 @@
         usm.start();
     }

-    public KeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException {
+    public ClientKeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException {
        if(localOnly)
                return realGetKey(key, localOnly, cache);
        else
                return client.getKey(key, localOnly, cache);
     }

-    public KeyBlock realGetKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException {
+    public ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException {
        if(key instanceof ClientCHK)
                return realGetCHK((ClientCHK)key, localOnly, cache);
        else

Modified: trunk/freenet/src/freenet/node/QueuedDataRequest.java
===================================================================
--- trunk/freenet/src/freenet/node/QueuedDataRequest.java       2005-12-16 
19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/node/QueuedDataRequest.java       2005-12-16 
22:40:41 UTC (rev 7724)
@@ -1,6 +1,7 @@
 package freenet.node;

 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;

 public class QueuedDataRequest extends QueuedRequest {
@@ -17,7 +18,7 @@
                this.cache = cache;
        }

-       public KeyBlock waitAndFetch() throws LowLevelGetException {
+       public ClientKeyBlock waitAndFetch() throws LowLevelGetException {
                waitForSendClearance();
                return client.realGetKey(key, localOnly, cache);
        }

Modified: trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java
===================================================================
--- trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java    
2005-12-16 19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java    
2005-12-16 22:40:41 UTC (rev 7724)
@@ -3,12 +3,13 @@
 import freenet.client.InsertBlock;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;

 interface QueueingSimpleLowLevelClient extends SimpleLowLevelClient {

        /** Unqueued version. Only call from QueuedDataRequest ! */
-       KeyBlock realGetKey(ClientKey key, boolean localOnly, boolean cache) 
throws LowLevelGetException;
+       ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException;

        /** Ditto */
        void realPutCHK(ClientCHKBlock block, boolean cache) throws 
LowLevelPutException;

Modified: trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
===================================================================
--- trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java       
2005-12-16 19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java       
2005-12-16 22:40:41 UTC (rev 7724)
@@ -179,7 +179,7 @@
                 byte[] encData = block.getData();
                 byte[] encHeaders = block.getHeader();
                 ClientCHKBlock newBlock = new ClientCHKBlock(encData, 
encHeaders, chk, true);
-                Logger.error(RealNodeRequestInsertTest.class, "Decoded: "+new 
String(newBlock.memoryDecode(chk)));
+                Logger.error(RealNodeRequestInsertTest.class, "Decoded: "+new 
String(newBlock.memoryDecode()));
                 Logger.error(RealNodeRequestInsertTest.class,"CHK: 
"+chk.getURI());
                 Logger.error(RealNodeRequestInsertTest.class,"Headers: 
"+HexUtil.bytesToHex(block.getHeader()));
                 randomNode.putCHK(block, starters[node1], true);
@@ -196,7 +196,7 @@
                     Logger.error(RealNodeRequestInsertTest.class, "Fetch 
FAILED from "+node2);
                     requestsAvg.report(0.0);
                 } else {
-                    byte[] results = block.memoryDecode(chk);
+                    byte[] results = block.memoryDecode();
                     requestsAvg.report(1.0);
                     if(Arrays.equals(results, data)) {
                         Logger.error(RealNodeRequestInsertTest.class, "Fetch 
succeeded: "+new String(results));

Modified: trunk/freenet/src/freenet/node/RequestStarterClient.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestStarterClient.java    2005-12-16 
19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/node/RequestStarterClient.java    2005-12-16 
22:40:41 UTC (rev 7724)
@@ -5,6 +5,7 @@
 import freenet.crypt.RandomSource;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;
 import freenet.support.DoublyLinkedList;
 import freenet.support.UpdatableSortedLinkedListItemImpl;
@@ -42,7 +43,7 @@
         * Blocking fetch of a key.
         * @throws LowLevelGetException If the fetch failed for some reason.
         */
-       public KeyBlock getKey(ClientKey key, boolean localOnly, boolean cache) 
throws LowLevelGetException {
+       public ClientKeyBlock getKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException {
                QueuedDataRequest qdr = new QueuedDataRequest(key, localOnly, 
cache, client);
                addRequest(qdr);
                return qdr.waitAndFetch();

Modified: trunk/freenet/src/freenet/node/SimpleLowLevelClient.java
===================================================================
--- trunk/freenet/src/freenet/node/SimpleLowLevelClient.java    2005-12-16 
19:01:17 UTC (rev 7723)
+++ trunk/freenet/src/freenet/node/SimpleLowLevelClient.java    2005-12-16 
22:40:41 UTC (rev 7724)
@@ -2,6 +2,7 @@

 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;

 /**
@@ -19,7 +20,7 @@
      * @param cache If false, don't cache the data. See the comments at the top
      * of Node.java.
      */
-    public KeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException;
+    public ClientKeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException;

     /**
      * Insert a key.


Reply via email to