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);


Reply via email to