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;