[freenet-cvs] r11586 - in trunk/freenet/src/freenet: crypt crypt/ciphers keys node support/io

toad at freenetproject.org toad at freenetproject.org
Tue Jan 9 04:38:14 UTC 2007


Author: toad
Date: 2007-01-09 04:38:13 +0000 (Tue, 09 Jan 2007)
New Revision: 11586

Modified:
   trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java
   trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java
   trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.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/ClientKSK.java
   trunk/freenet/src/freenet/keys/ClientSSK.java
   trunk/freenet/src/freenet/keys/ClientSSKBlock.java
   trunk/freenet/src/freenet/keys/InsertableClientSSK.java
   trunk/freenet/src/freenet/keys/InsertableUSK.java
   trunk/freenet/src/freenet/keys/Key.java
   trunk/freenet/src/freenet/keys/NodeCHK.java
   trunk/freenet/src/freenet/keys/NodeSSK.java
   trunk/freenet/src/freenet/keys/USK.java
   trunk/freenet/src/freenet/node/PeerNode.java
   trunk/freenet/src/freenet/node/Version.java
   trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
   trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
Log:
1010: MANDATORY SECURITY FIX
Fix a serious crypto bug. Mandatory immediately (sorry folks).
All old KSKs are no longer retrievable, and you will need to generate a new SSK for your inserts to be properly encrypted.
Otherwise is backwards compatible with prior builds (in terms of content), so no content reset.

Modified: trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java
===================================================================
--- trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -27,7 +27,7 @@
         if(cipher != null) return cipher;
         getKey();
         try {
-            cipher = new Rijndael(256, 256);
+            cipher = new Rijndael(256, 256, false);
         } catch (UnsupportedCipherException e1) {
             throw new Error(e1);
         }

Modified: trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java
===================================================================
--- trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -19,7 +19,10 @@
  */
 public class Rijndael implements BlockCipher {
     private Object sessionKey;
-    private int keysize, blocksize;
+    private final int keysize, blocksize;
+    // FIXME remove. This is used to simulate an old security threatening bug in order
+    // to have backwards compatibility with old keys.
+    private final int cryptBlockSize;
 
     // for Util.getCipherByName..  and yes, screw you too, java
     public Rijndael(Integer keysize) throws UnsupportedCipherException {
@@ -27,10 +30,19 @@
     }
 
     public Rijndael(int keysize) throws UnsupportedCipherException {
-	this(keysize, 128);
+	this(keysize, 128, false);
     }
 
-    public Rijndael(int keysize, int blocksize) throws UnsupportedCipherException {
+    /**
+     * Create a Rijndael instance.
+     * @param keysize The key size.
+     * @param blocksize The block size.
+     * @param fakeInsecure If true, only encrypt the first 128 bits of any block. This
+     * is insecure! It is used for backwards compatibility with old data encrypted with
+     * the old code.
+     * @throws UnsupportedCipherException
+     */
+    public Rijndael(int keysize, int blocksize, boolean fakeInsecure) throws UnsupportedCipherException {
 	if (! ((keysize == 128) ||
 	       (keysize == 192) ||
 	       (keysize == 256)))
@@ -41,11 +53,18 @@
 	    throw new UnsupportedCipherException("Invalid blocksize");
 	this.keysize=keysize;
 	this.blocksize=blocksize;
+	// FIXME This is deliberate insecurity! It is used for backwards compatibility *ONLY*!
+	// FIXME IT MUST BE REMOVED SOON!
+	if(fakeInsecure)
+		this.cryptBlockSize = 128;
+	else
+		this.cryptBlockSize = blocksize;
     }
 
     public Rijndael() {
         this.keysize   = 128;
         this.blocksize = 128;
+        this.cryptBlockSize = 128;
     }
 
     public final int getBlockSize() {
@@ -60,7 +79,7 @@
 	try {
 	    byte[] nkey=new byte[keysize>>3];
 	    System.arraycopy(key, 0, nkey, 0, nkey.length);
-	    sessionKey=Rijndael_Algorithm.makeKey(nkey);
+	    sessionKey=Rijndael_Algorithm.makeKey(nkey, cryptBlockSize/8);
 	} catch (InvalidKeyException e) {
 	    e.printStackTrace();
 	    Logger.error(this,"Invalid key");
@@ -70,13 +89,13 @@
     public synchronized final void encipher(byte[] block, byte[] result) {
         if(block.length != blocksize/8)
             throw new IllegalArgumentException();
-	Rijndael_Algorithm.blockEncrypt(block, result, 0, sessionKey);
+	Rijndael_Algorithm.blockEncrypt(block, result, 0, sessionKey, cryptBlockSize/8);
     }
 
     public synchronized final void decipher(byte[] block, byte[] result) {
         if(block.length != blocksize/8)
             throw new IllegalArgumentException();
-	Rijndael_Algorithm.blockDecrypt(block, result, 0, sessionKey);
+	Rijndael_Algorithm.blockDecrypt(block, result, 0, sessionKey, cryptBlockSize/8);
     }
 
     public static void main(String[] args) throws UnsupportedCipherException {

Modified: trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
===================================================================
--- trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -300,7 +300,7 @@
      * @param k The 128/192/256-bit user-key to use.
      * @exception  InvalidKeyException  If the key is invalid.
      */
-    public static final Object makeKey (byte[] k) throws InvalidKeyException {
+    private static final Object makeKey (byte[] k) throws InvalidKeyException {
         return makeKey(k, BLOCK_SIZE);
     }
 
@@ -313,7 +313,7 @@
      * @param  inOffset   Index of in from which to start considering data.
      * @param  sessionKey The session key to use for encryption.
      */
-    public static final void
+    private static final void
     blockEncrypt (byte[] in, byte[] result, int inOffset, Object sessionKey) {
 if (RDEBUG) trace(IN, "blockEncrypt("+in+", "+inOffset+", "+sessionKey+ ')');
         int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
@@ -402,7 +402,7 @@
      * @param  inOffset   Index of in from which to start considering data.
      * @param  sessionKey The session key to use for decryption.
      */
-    public static final void
+    private static final void
     blockDecrypt (byte[] in, byte[] result, int inOffset, Object sessionKey) {
 if (RDEBUG) trace(IN, "blockDecrypt("+in+", "+inOffset+", "+sessionKey+ ')');
         int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys

Modified: trunk/freenet/src/freenet/keys/CHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKBlock.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/CHKBlock.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -42,12 +42,16 @@
     public byte[] getData() {
         return data;
     }
-
+    
     public CHKBlock(byte[] data2, byte[] header2, NodeCHK key) throws CHKVerifyException {
-        this(data2, header2, key, true);
+    	this(data2, header2, key, key.cryptoAlgorithm);
     }
+
+    public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, byte cryptoAlgorithm) throws CHKVerifyException {
+        this(data2, header2, key, true, cryptoAlgorithm);
+    }
     
-    public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, boolean verify) throws CHKVerifyException {
+    public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, boolean verify, byte cryptoAlgorithm) throws CHKVerifyException {
         data = data2;
         headers = header2;
         if(headers.length != TOTAL_HEADERS_LENGTH)
@@ -69,7 +73,7 @@
         md.update(data);
         byte[] hash = md.digest();
         if(key == null) {
-        	chk = new NodeCHK(hash);
+        	chk = new NodeCHK(hash, cryptoAlgorithm);
         } else {
         	chk = key;
             byte[] check = chk.routingKey;

Modified: trunk/freenet/src/freenet/keys/ClientCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientCHK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -25,7 +25,7 @@
     /** Is the data a control document? */
     final boolean controlDocument;
     /** Encryption algorithm */
-    final short cryptoAlgorithm;
+    final byte cryptoAlgorithm;
     /** Compression algorithm, negative means uncompressed */
     final short compressionAlgorithm;
     
@@ -49,7 +49,7 @@
      * values.
      */
     public ClientCHK(byte[] routingKey, byte[] encKey,  
-            boolean isControlDocument, short algo, short compressionAlgorithm) {
+            boolean isControlDocument, byte algo, short compressionAlgorithm) {
         this.routingKey = routingKey;
         this.cryptoKey = encKey;
         this.controlDocument = isControlDocument;
@@ -68,8 +68,10 @@
         byte[] extra = uri.getExtra();
         if((extra == null) || (extra.length < 5))
             throw new MalformedURLException();
-        cryptoAlgorithm = (short)(((extra[0] & 0xff) << 8) + (extra[1] & 0xff));
-		if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
+        // byte 0 is reserved, for now
+        cryptoAlgorithm = extra[1];
+		if((!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+				cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
 			throw new MalformedURLException("Invalid crypto algorithm");
         controlDocument = (extra[2] & 0x02) != 0;
         compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] & 0xff));
@@ -83,8 +85,10 @@
 	private ClientCHK(DataInputStream dis) throws IOException {
 		byte[] extra = new byte[EXTRA_LENGTH];
 		dis.readFully(extra);
-        cryptoAlgorithm = (short)(((extra[0] & 0xff) << 8) + (extra[1] & 0xff));
-		if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
+		// byte 0 is reserved, for now
+        cryptoAlgorithm = extra[1];
+		if((!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+				cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
 			throw new MalformedURLException("Invalid crypto algorithm");
         compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] & 0xff));
         controlDocument = (extra[2] & 0x02) != 0;
@@ -126,7 +130,7 @@
 
 	public NodeCHK getNodeCHK() {
 		if(nodeKey == null)
-	        nodeKey = new NodeCHK(routingKey);
+	        nodeKey = new NodeCHK(routingKey, cryptoAlgorithm);
 	    return nodeKey;
 	}
 	

Modified: trunk/freenet/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHKBlock.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientCHKBlock.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -44,7 +44,7 @@
      * @param data The data.
      */
     public ClientCHKBlock(byte[] data, byte[] header, ClientCHK key2, boolean verify) throws CHKVerifyException {
-        super(data, header, key2.getNodeCHK(), verify);
+        super(data, header, key2.getNodeCHK(), verify, key2.cryptoAlgorithm);
         this.key = key2;
     }
 
@@ -75,11 +75,12 @@
      */
     public Bucket decode(BucketFactory bf, int maxLength, boolean dontCompress) throws CHKDecodeException, IOException {
         // Overall hash already verified, so first job is to decrypt.
-        if(key.cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
+		if((!(key.cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+				key.cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
             throw new UnsupportedOperationException();
         BlockCipher cipher;
         try {
-            cipher = new Rijndael(256, 256);
+            cipher = new Rijndael(256, 256, key.cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
         } catch (UnsupportedCipherException e) {
             // FIXME - log this properly
             throw new Error(e);
@@ -178,7 +179,7 @@
         // Now encrypt the header, then the data, using the same PCFB instance
         BlockCipher cipher;
         try {
-            cipher = new Rijndael(256, 256);
+            cipher = new Rijndael(256, 256, false);
         } catch (UnsupportedCipherException e) {
             // FIXME - log this properly
             throw new Error(e);

Modified: trunk/freenet/src/freenet/keys/ClientKSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientKSK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientKSK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -19,7 +19,7 @@
 	final String keyword;
 	
 	private ClientKSK(String keyword, byte[] pubKeyHash, DSAPublicKey pubKey, DSAPrivateKey privKey, byte[] keywordHash) throws MalformedURLException {
-		super(keyword, pubKeyHash, pubKey, privKey, keywordHash);
+		super(keyword, pubKeyHash, pubKey, privKey, keywordHash, Key.ALGO_AES_PCFB_256_SHA256);
 		this.keyword = keyword;
 	}
 

Modified: trunk/freenet/src/freenet/keys/ClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientSSK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -16,6 +16,8 @@
 
 public class ClientSSK extends ClientKey {
 
+	/** Crypto type */
+	public final byte cryptoAlgorithm;
 	/** Document name */
 	public final String docName;
 	/** Public key */
@@ -34,6 +36,12 @@
 		this.docName = docName;
 		this.pubKey = pubKey;
 		this.pubKeyHash = pubKeyHash;
+		if(extras.length < 5)
+			throw new MalformedURLException("Extra bytes too short: "+extras.length+" bytes");
+		this.cryptoAlgorithm = extras[2];
+		if(!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+				cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256))
+			throw new MalformedURLException("Unknown encryption algorithm "+cryptoAlgorithm);
 		if(!Arrays.equals(extras, getExtraBytes()))
 			throw new MalformedURLException("Wrong extra bytes");
 		if(pubKeyHash.length != NodeSSK.PUBKEY_HASH_SIZE)
@@ -56,7 +64,7 @@
 		}
 		byte[] buf = md.digest();
 		try {
-			Rijndael aes = new Rijndael(256,256);
+			Rijndael aes = new Rijndael(256,256,cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
 			aes.initialize(cryptoKey);
 			aes.encipher(buf, buf);
 			ehDocname = buf;
@@ -80,15 +88,17 @@
 	public FreenetURI getURI() {
 		return new FreenetURI("SSK", docName, pubKeyHash, cryptoKey, getExtraBytes());
 	}
+
+	protected final byte[] getExtraBytes() {
+		return getExtraBytes(cryptoAlgorithm);
+	}
 	
-	protected static final byte[] getExtraBytes() {
+	protected static byte[] getExtraBytes(byte cryptoAlgorithm) {
 		// 5 bytes.
 		byte[] extra = new byte[5];
 
-		short cryptoAlgorithm = Key.ALGO_AES_PCFB_256_SHA256;
-		
 		extra[0] = NodeSSK.SSK_VERSION;
-		extra[1] = (byte) (cryptoAlgorithm >> 8);
+		extra[1] = 0; // 0 = fetch (public) URI; 1 = insert (private) URI
 		extra[2] = (byte) cryptoAlgorithm;
 		extra[3] = (byte) (KeyBlock.HASH_SHA256 >> 8);
 		extra[4] = (byte) KeyBlock.HASH_SHA256;
@@ -97,7 +107,7 @@
 
 	public Key getNodeKey() {
 		try {
-			return new NodeSSK(pubKeyHash, ehDocname, pubKey);
+			return new NodeSSK(pubKeyHash, ehDocname, pubKey, cryptoAlgorithm);
 		} catch (SSKVerifyException e) {
 			IllegalStateException x = new IllegalStateException("Have already verified and yet it fails!: "+e);
 			Logger.error(this, "Have already verified and yet it fails!: "+e);

Modified: trunk/freenet/src/freenet/keys/ClientSSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSKBlock.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientSSKBlock.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -8,6 +8,7 @@
 import freenet.crypt.PCFBMode;
 import freenet.crypt.UnsupportedCipherException;
 import freenet.crypt.ciphers.Rijndael;
+import freenet.support.Logger;
 import freenet.support.api.Bucket;
 import freenet.support.api.BucketFactory;
 import freenet.support.io.BucketTools;
@@ -47,7 +48,8 @@
 		System.arraycopy(headers, headersOffset, decryptedHeaders, 0, ENCRYPTED_HEADERS_LENGTH);
 		Rijndael aes;
 		try {
-			aes = new Rijndael(256,256);
+			Logger.minor(this, "cryptoAlgorithm="+key.cryptoAlgorithm+" for "+getClientKey().getURI());
+			aes = new Rijndael(256,256,key.cryptoAlgorithm==Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
 		} catch (UnsupportedCipherException e) {
 			throw new Error(e);
 		}

Modified: trunk/freenet/src/freenet/keys/InsertableClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableClientSSK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/InsertableClientSSK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -29,8 +29,8 @@
 
 	public final DSAPrivateKey privKey;
 	
-	public InsertableClientSSK(String docName, byte[] pubKeyHash, DSAPublicKey pubKey, DSAPrivateKey privKey, byte[] cryptoKey) throws MalformedURLException {
-		super(docName, pubKeyHash, getExtraBytes(), pubKey, cryptoKey);
+	public InsertableClientSSK(String docName, byte[] pubKeyHash, DSAPublicKey pubKey, DSAPrivateKey privKey, byte[] cryptoKey, byte cryptoAlgorithm) throws MalformedURLException {
+		super(docName, pubKeyHash, getExtraBytes(cryptoAlgorithm), pubKey, cryptoKey);
 		if(pubKey == null) throw new NullPointerException();
 		this.privKey = privKey;
 	}
@@ -38,18 +38,38 @@
 	public static InsertableClientSSK create(FreenetURI uri) throws MalformedURLException {
 		if(uri.getKeyType().equalsIgnoreCase("KSK"))
 			return ClientKSK.create(uri);
-		if(!uri.getKeyType().equalsIgnoreCase("SSK"))
-			throw new MalformedURLException();
+		
+		byte keyType;
+
+		byte[] extra = uri.getExtra();
+		if(uri.getKeyType().equals("SSK")) {
+			// FIXME remove once everyone is using PSKs
+			if(extra == null) {
+				keyType = Key.ALGO_INSECURE_AES_PCFB_256_SHA256;
+			} else {
+				// Formatted exactly as ,extra on fetching
+				if(extra.length < 5)
+					throw new MalformedURLException("SSK private key ,extra too short");
+				if(extra[1] != 1) {
+					throw new MalformedURLException("SSK not a private key");
+				}
+				keyType = extra[2];
+				if(!(keyType == Key.ALGO_AES_PCFB_256_SHA256 ||
+						keyType == Key.ALGO_INSECURE_AES_PCFB_256_SHA256))
+					throw new MalformedURLException("Unrecognized crypto type in SSK private key");
+			}
+		} else {
+			throw new MalformedURLException("Not a valid SSK insert URI type: "+uri.getKeyType());
+		}
+		
 		if((uri.getDocName() == null) || (uri.getDocName().length() == 0))
 			throw new MalformedURLException("SSK URIs must have a document name (to avoid ambiguity)");
-		if(uri.getExtra() != null)
-			throw new MalformedURLException("Insertable SSK URIs must NOT have ,extra - inserting from a pubkey rather than the privkey perhaps?");
 		DSAGroup g = Global.DSAgroupBigA;
 		DSAPrivateKey privKey = new DSAPrivateKey(new NativeBigInteger(1, uri.getKeyVal()));
 		DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
 		MessageDigest md = SHA256.getMessageDigest();
 		md.update(pubKey.asBytes());
-		return new InsertableClientSSK(uri.getDocName(), md.digest(), pubKey, privKey, uri.getCryptoKey());
+		return new InsertableClientSSK(uri.getDocName(), md.digest(), pubKey, privKey, uri.getCryptoKey(), keyType);
 	}
 	
 	public ClientSSKBlock encode(Bucket sourceData, boolean asMetadata, boolean dontCompress, short alreadyCompressedCodec, long sourceLength, RandomSource r) throws SSKEncodeException, IOException {
@@ -89,7 +109,7 @@
 
         Rijndael aes;
         try {
-			aes = new Rijndael(256, 256);
+			aes = new Rijndael(256, 256, cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
 		} catch (UnsupportedCipherException e) {
 			throw new Error("256/256 Rijndael not supported!");
 		}
@@ -188,14 +208,24 @@
 		DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
 		MessageDigest md = SHA256.getMessageDigest();
 		try {
-			return new InsertableClientSSK(docName, md.digest(pubKey.asBytes()), pubKey, privKey, ckey);
+			return new InsertableClientSSK(docName, md.digest(pubKey.asBytes()), pubKey, privKey, ckey, Key.ALGO_AES_PCFB_256_SHA256);
 		} catch (MalformedURLException e) {
 			throw new Error(e);
 		}
 	}
 
 	public FreenetURI getInsertURI() {
-		return new FreenetURI("SSK", docName, privKey.getX().toByteArray(), cryptoKey, null);
+		return new FreenetURI("SSK", docName, privKey.getX().toByteArray(), cryptoKey, getInsertExtraBytes());
 	}
+
+	private byte[] getInsertExtraBytes() {
+		byte[] extra = getExtraBytes();
+		extra[1] = 1; // insert
+		return extra;
+	}
+
+	public DSAGroup getCryptoGroup() {
+		return Global.DSAgroupBigA;
+	}
 	
 }

Modified: trunk/freenet/src/freenet/keys/InsertableUSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableUSK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/InsertableUSK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -32,20 +32,14 @@
 	public static InsertableUSK createInsertable(FreenetURI uri) throws MalformedURLException {
 		if(!uri.getKeyType().equalsIgnoreCase("USK"))
 			throw new MalformedURLException();
-		if((uri.getDocName() == null) || (uri.getDocName().length() == 0))
-			throw new MalformedURLException("USK URIs must have a document name (to avoid ambiguity)");
-		if(uri.getExtra() != null)
-			throw new MalformedURLException("Insertable SSK URIs must NOT have ,extra - inserting from a pubkey rather than the privkey perhaps?");
-		DSAGroup g = Global.DSAgroupBigA;
-		DSAPrivateKey privKey = new DSAPrivateKey(new NativeBigInteger(1, uri.getKeyVal()));
-		DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
-		MessageDigest md = SHA256.getMessageDigest();
-		md.update(pubKey.asBytes());
-		return new InsertableUSK(uri.getDocName(), md.digest(), uri.getCryptoKey(), privKey, g, uri.getSuggestedEdition());
+		uri = uri.setKeyType("SSK");
+		InsertableClientSSK ssk =
+			InsertableClientSSK.create(uri);
+		return new InsertableUSK(ssk.docName, ssk.pubKeyHash, ssk.cryptoKey, ssk.privKey, ssk.getCryptoGroup(), uri.getSuggestedEdition(), ssk.cryptoAlgorithm);
 	}
 	
-	InsertableUSK(String docName, byte[] pubKeyHash, byte[] cryptoKey, DSAPrivateKey key, DSAGroup group, long suggestedEdition) throws MalformedURLException {
-		super(pubKeyHash, cryptoKey, docName, suggestedEdition);
+	InsertableUSK(String docName, byte[] pubKeyHash, byte[] cryptoKey, DSAPrivateKey key, DSAGroup group, long suggestedEdition, byte cryptoAlgorithm) throws MalformedURLException {
+		super(pubKeyHash, cryptoKey, docName, suggestedEdition, cryptoAlgorithm);
 		if(cryptoKey.length != ClientSSK.CRYPTO_KEY_LENGTH)
 			throw new MalformedURLException("Decryption key wrong length: "+cryptoKey.length+" should be "+ClientSSK.CRYPTO_KEY_LENGTH);
 		this.privKey = key;
@@ -57,13 +51,13 @@
 	}
 
 	public USK getUSK() {
-		return new USK(pubKeyHash, cryptoKey, siteName, suggestedEdition);
+		return new USK(pubKeyHash, cryptoKey, siteName, suggestedEdition, cryptoAlgorithm);
 	}
 
 	public InsertableClientSSK getInsertableSSK(long ver) {
 		try {
 			return new InsertableClientSSK(siteName + SEPARATOR + ver, pubKeyHash, 
-					new DSAPublicKey(group, privKey), privKey, cryptoKey);
+					new DSAPublicKey(group, privKey), privKey, cryptoKey, cryptoAlgorithm);
 		} catch (MalformedURLException e) {
 			Logger.error(this, "Caught "+e+" should not be possible in USK.getSSK", e);
 			throw new Error(e);
@@ -73,11 +67,10 @@
 	public InsertableUSK privCopy(long edition) {
 		if(edition == suggestedEdition) return this;
 		try {
-			return new InsertableUSK(siteName, pubKeyHash, cryptoKey, privKey, group, edition);
+			return new InsertableUSK(siteName, pubKeyHash, cryptoKey, privKey, group, edition, cryptoAlgorithm);
 		} catch (MalformedURLException e) {
 			throw new Error(e);
 		}
 	}
 
-
 }

Modified: trunk/freenet/src/freenet/keys/Key.java
===================================================================
--- trunk/freenet/src/freenet/keys/Key.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/Key.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -34,7 +34,10 @@
     final byte[] routingKey;
     
     /** Code for 256-bit AES with PCFB and SHA-256 */
-    static final short ALGO_AES_PCFB_256_SHA256 = 1;
+    static final byte ALGO_AES_PCFB_256_SHA256 = 2;
+    /** Code for old, insecure (only encrypts first 128 bits of block) 256-bit AES with PCFB and SHA-256.
+     * FIXME: REMOVE!! */
+	static final byte ALGO_INSECURE_AES_PCFB_256_SHA256 = 1;
 
     protected Key(byte[] routingKey) {
     	this.routingKey = routingKey;
@@ -55,11 +58,12 @@
      * @return a Key, or throw an exception, or return null if the key is not parsable.
      */
     public static final Key read(DataInput raf) throws IOException {
-        short type = raf.readShort();
-        if(type == NodeCHK.TYPE) {
-            return NodeCHK.readCHK(raf);
-        } else if(type == NodeSSK.TYPE)
-        	return NodeSSK.readSSK(raf);
+    	byte type = raf.readByte();
+    	byte subtype = raf.readByte();
+        if(type == NodeCHK.BASE_TYPE) {
+            return NodeCHK.readCHK(raf, subtype);
+        } else if(type == NodeSSK.BASE_TYPE)
+        	return NodeSSK.readSSK(raf, subtype);
         
         throw new IOException("Unrecognized format: "+type);
     }

Modified: trunk/freenet/src/freenet/keys/NodeCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/NodeCHK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/NodeCHK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -21,19 +21,22 @@
     /** 32 bytes for hash, 2 bytes for type */
     public static final short KEY_SIZE_ON_DISK = 34;
 	
-    public NodeCHK(byte[] routingKey2) {
+    public NodeCHK(byte[] routingKey2, byte cryptoAlgorithm) {
     	super(routingKey2);
         if(routingKey2.length != KEY_LENGTH)
             throw new IllegalArgumentException("Wrong length: "+routingKey2.length+" should be "+KEY_LENGTH);
+        this.cryptoAlgorithm = cryptoAlgorithm;
     }
 
     static final int KEY_LENGTH = 32;
     
-    // 01 = CHK, 01 = first version of CHK
-    public static final short TYPE = 0x0101;
+	/** Crypto algorithm */
+	final byte cryptoAlgorithm;
     /** The size of the data */
 	public static final int BLOCK_SIZE = 32768;
 
+	public static final byte BASE_TYPE = 1;
+
     public final void writeToDataOutputStream(DataOutputStream stream) throws IOException {
         write(stream);
     }
@@ -43,14 +46,14 @@
     }
 
     public final void write(DataOutput _index) throws IOException {
-        _index.writeShort(TYPE);
+        _index.writeShort(getType());
         _index.write(routingKey);
     }
     
-    public static Key readCHK(DataInput raf) throws IOException {
+    public static Key readCHK(DataInput raf, byte algo) throws IOException {
         byte[] buf = new byte[KEY_LENGTH];
         raf.readFully(buf);
-        return new NodeCHK(buf);
+        return new NodeCHK(buf, algo);
     }
 
     public boolean equals(Object key) {
@@ -66,7 +69,7 @@
     }
 
 	public short getType() {
-		return TYPE;
+		return (short) (0x100 + (cryptoAlgorithm & 0xFF));
 	}
     
     public byte[] getRoutingKey(){

Modified: trunk/freenet/src/freenet/keys/NodeSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/NodeSSK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/NodeSSK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -27,6 +27,8 @@
  */
 public class NodeSSK extends Key {
 	
+	/** Crypto algorithm */
+	final byte cryptoAlgorithm;
 	/** Public key hash */
 	final byte[] pubKeyHash;
 	/** E(H(docname)) (E = encrypt using decrypt key, which only clients know) */
@@ -39,15 +41,17 @@
 	
 	static final int PUBKEY_HASH_SIZE = 32;
 	static final int E_H_DOCNAME_SIZE = 32;
+	static final byte BASE_TYPE = 2;
 	
 	public String toString() {
 		return super.toString()+":pkh="+HexUtil.bytesToHex(pubKeyHash)+":ehd="+HexUtil.bytesToHex(encryptedHashedDocname);
 	}
 	
-	public NodeSSK(byte[] pkHash, byte[] ehDocname, DSAPublicKey pubKey) throws SSKVerifyException {
+	public NodeSSK(byte[] pkHash, byte[] ehDocname, DSAPublicKey pubKey, byte cryptoAlgorithm) throws SSKVerifyException {
 		super(makeRoutingKey(pkHash, ehDocname));
 		this.encryptedHashedDocname = ehDocname;
 		this.pubKeyHash = pkHash;
+		this.cryptoAlgorithm = cryptoAlgorithm;
 		this.pubKey = pubKey;
 		if(pubKey != null) {
 			MessageDigest md256 = SHA256.getMessageDigest();
@@ -70,22 +74,19 @@
 		return md256.digest();
 	}
 	
-	// 01 = SSK, 01 = first version of SSK
-	public static short TYPE = 0x0201;
-	
 	public void write(DataOutput _index) throws IOException {
-        _index.writeShort(TYPE);
+        _index.writeShort(getType());
         _index.write(encryptedHashedDocname);
         _index.write(pubKeyHash);
 	}
 
-    public static Key readSSK(DataInput raf) throws IOException {
+    public static Key readSSK(DataInput raf, byte cryptoAlgorithm) throws IOException {
         byte[] buf = new byte[E_H_DOCNAME_SIZE];
         raf.readFully(buf);
         byte[] buf2 = new byte[PUBKEY_HASH_SIZE];
         raf.readFully(buf2);
         try {
-			return new NodeSSK(buf2, buf, null);
+			return new NodeSSK(buf2, buf, null, cryptoAlgorithm);
 		} catch (SSKVerifyException e) {
 			IllegalStateException impossible = 
 				new IllegalStateException("Impossible: "+e);
@@ -95,7 +96,7 @@
     }
 
 	public short getType() {
-		return TYPE;
+		return (short) (0x0200 + (cryptoAlgorithm & 0xff));
 	}
 
 	public void writeToDataOutputStream(DataOutputStream stream) throws IOException {

Modified: trunk/freenet/src/freenet/keys/USK.java
===================================================================
--- trunk/freenet/src/freenet/keys/USK.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/USK.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -25,6 +25,8 @@
 	 * SSK form, and we don't need to go vice versa.
 	 */
 	static protected final String SEPARATOR = "-";
+	/** Encryption type */
+	public final byte cryptoAlgorithm;
 	/** Public key hash */
 	public final byte[] pubKeyHash;
 	/** Encryption key */
@@ -41,8 +43,9 @@
 		this.cryptoKey = cryptoKey;
 		this.siteName = siteName;
 		this.suggestedEdition = suggestedEdition;
-		if(!Arrays.equals(extra, ClientSSK.getExtraBytes()))
-			throw new MalformedURLException("Invalid extra bytes");
+		// Verify extra bytes, get cryptoAlgorithm
+		ClientSSK tmp = new ClientSSK(siteName, pubKeyHash, extra, null, cryptoKey);
+		cryptoAlgorithm = tmp.cryptoAlgorithm;
 		if(pubKeyHash.length != NodeSSK.PUBKEY_HASH_SIZE)
 			throw new MalformedURLException("Pubkey hash wrong length: "+pubKeyHash.length+" should be "+NodeSSK.PUBKEY_HASH_SIZE);
 		if(cryptoKey.length != ClientSSK.CRYPTO_KEY_LENGTH)
@@ -56,11 +59,12 @@
 		return new USK(uri.getRoutingKey(), uri.getCryptoKey(), uri.getExtra(), uri.getDocName(), uri.getSuggestedEdition());
 	}
 	
-	protected USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2, long suggestedEdition2) {
+	protected USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2, long suggestedEdition2, byte cryptoAlgorithm) {
 		this.pubKeyHash = pubKeyHash2;
 		this.cryptoKey = cryptoKey2;
 		this.siteName = siteName2;
 		this.suggestedEdition = suggestedEdition2;
+		this.cryptoAlgorithm = cryptoAlgorithm;
 		hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^
 			siteName.hashCode() ^ (int)suggestedEdition ^ (int)(suggestedEdition >> 32);
 	}
@@ -70,17 +74,18 @@
 		this.cryptoKey = ssk.cryptoKey;
 		this.siteName = ssk.docName;
 		this.suggestedEdition = myARKNumber;
+		this.cryptoAlgorithm = ssk.cryptoAlgorithm;
 		hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^
 			siteName.hashCode() ^ (int)suggestedEdition ^ (int)(suggestedEdition >> 32);
 	}
 
 	public FreenetURI getURI() {
-		return new FreenetURI(pubKeyHash, cryptoKey, ClientSSK.getExtraBytes(), siteName, suggestedEdition);
+		return new FreenetURI(pubKeyHash, cryptoKey, ClientSSK.getExtraBytes(cryptoAlgorithm), siteName, suggestedEdition);
 	}
 
 	public ClientSSK getSSK(long ver) {
 		try {
-			return new ClientSSK(siteName + SEPARATOR + ver, pubKeyHash, ClientSSK.getExtraBytes(), null, cryptoKey);
+			return new ClientSSK(siteName + SEPARATOR + ver, pubKeyHash, ClientSSK.getExtraBytes(cryptoAlgorithm), null, cryptoKey);
 		} catch (MalformedURLException e) {
 			Logger.error(this, "Caught "+e+" should not be possible in USK.getSSK", e);
 			throw new Error(e);
@@ -93,7 +98,7 @@
 	
 	public USK copy(long edition) {
 		if(suggestedEdition == edition) return this;
-		return new USK(pubKeyHash, cryptoKey, siteName, edition);
+		return new USK(pubKeyHash, cryptoKey, siteName, edition, cryptoAlgorithm);
 	}
 
 	public USK clearCopy() {
@@ -122,7 +127,7 @@
 	}
 
 	public FreenetURI getBaseSSK() {
-		return new FreenetURI("SSK", siteName, pubKeyHash, cryptoKey, ClientSSK.getExtraBytes());
+		return new FreenetURI("SSK", siteName, pubKeyHash, cryptoKey, ClientSSK.getExtraBytes(cryptoAlgorithm));
 	}
 	
 	public String toString() {

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/node/PeerNode.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -467,9 +467,9 @@
         			"\nFor:       "+getPeer());
         
         try {
-            incomingSetupCipher = new Rijndael(256,256);
+            incomingSetupCipher = new Rijndael(256,256,false);
             incomingSetupCipher.initialize(incomingSetupKey);
-            outgoingSetupCipher = new Rijndael(256,256);
+            outgoingSetupCipher = new Rijndael(256,256,false);
             outgoingSetupCipher.initialize(outgoingSetupKey);
         } catch (UnsupportedCipherException e1) {
             Logger.error(this, "Caught: "+e1);

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/node/Version.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -24,11 +24,11 @@
 	public static final String protocolVersion = "1.0";
 
 	/** The build number of the current revision */
-	private static final int buildNumber = 1009;
+	private static final int buildNumber = 1010;
 
 	/** Oldest build of Fred we will talk to */
-	private static final int oldLastGoodBuild = 1007;
-	private static final int newLastGoodBuild = 1009;
+	private static final int oldLastGoodBuild = 1010;
+	private static final int newLastGoodBuild = 1010;
 	private static final long transitionTime;
 	
 	static {

Modified: trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -31,6 +31,8 @@
 	private final Rijndael aes;
 	/** The decryption key. May be null. */
 	private final byte[] key;
+	/** Broken (old) encryption? */
+	private final boolean brokenEncryption;
 	private long dataLength;
 	private boolean readOnly;
 	private int lastOutputStream;
@@ -48,10 +50,11 @@
 		this.bucket = bucket;
 		if(bucket.size() != 0) throw new IllegalArgumentException("Bucket must be empty");
 		try {
-			aes = new Rijndael(256, 256);
+			aes = new Rijndael(256, 256, false);
 		} catch (UnsupportedCipherException e) {
 			throw new Error(e);
 		}
+		brokenEncryption = false;
 		byte[] tempKey = new byte[32];
 		origRandom.nextBytes(tempKey);
 		aes.initialize(tempKey);
@@ -78,14 +81,15 @@
 	 * @param origRandom
 	 * @throws IOException 
 	 */
-	public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize, long knownSize, byte[] key, RandomSource origRandom) throws IOException {
+	public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize, long knownSize, byte[] key, RandomSource origRandom, boolean oldCrypto) throws IOException {
 		if(bucket.size() < knownSize)
 			throw new IOException("Bucket "+bucket+" is too small on disk - knownSize="+knownSize+" but bucket.size="+bucket.size()+" for "+bucket);
 		this.dataLength = knownSize;
 		this.origRandom = origRandom;
 		this.bucket = bucket;
+		brokenEncryption = oldCrypto;
 		try {
-			aes = new Rijndael(256, 256);
+			aes = new Rijndael(256, 256, oldCrypto);
 		} catch (UnsupportedCipherException e) {
 			throw new Error(e);
 		}
@@ -113,9 +117,10 @@
 		tmp = fs.get("DecryptKey");
 		if(tmp == null)
 			throw new CannotCreateFromFieldSetException("No key");
+		brokenEncryption = fs.get("CryptoType") == null;
 		key = HexUtil.hexToBytes(tmp);
 		try {
-			aes = new Rijndael(256, 256);
+			aes = new Rijndael(256, 256, brokenEncryption);
 		} catch (UnsupportedCipherException e) {
 			throw new Error(e);
 		}
@@ -362,6 +367,8 @@
 			return null;
 		}
 		fs.put("MinPaddedSize", minPaddedSize);
+		if(!brokenEncryption)
+			fs.put("CryptoType", "aes256");
 		return fs;
 	}
 

Modified: trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -120,19 +120,6 @@
 	}
 
 	/**
-	 * Restore an encrypted temp bucket from last time.
-	 * @param filename The filename. Must exist unless len=0.
-	 * @param key The encryption key for the bucket.
-	 * @param len The data length. The file must be of at least this length.
-	 * @return
-	 * @throws IOException If the file doesn't exist or if it is too short.
-	 */
-	public Bucket registerEncryptedBucket(String filename, byte[] key, long len) throws IOException {
-		Bucket fileBucket = register(filename, len > 0);
-		return new DelayedFreeBucket(this, new PaddedEphemerallyEncryptedBucket(fileBucket, 1024, len, key, rand));
-	}
-	
-	/**
 	 * Free an allocated bucket, but only after the change has been written to disk.
 	 */
 	public void delayedFreeBucket(Bucket b) {

Modified: trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
===================================================================
--- trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java	2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java	2007-01-09 04:38:13 UTC (rev 11586)
@@ -47,7 +47,7 @@
 				FileBucket fb = new FileBucket(fnam, false, false, false, true);
 				try {
 					PaddedEphemerallyEncryptedBucket eb = 
-						new PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random);
+						new PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random, true);
 					return eb;
 				} catch (IOException e) {
 					throw new CannotCreateFromFieldSetException("Cannot create from old-format fieldset: "+e, e);




More information about the cvs mailing list