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.