Author: toad
Date: 2007-12-05 00:28:01 +0000 (Wed, 05 Dec 2007)
New Revision: 16295

Modified:
   trunk/freenet/src/freenet/node/FNPPacketMangler.java
   trunk/freenet/src/freenet/node/PeerNode.java
Log:
Phase 2 and phase 4 need a peernode

Modified: trunk/freenet/src/freenet/node/FNPPacketMangler.java
===================================================================
--- trunk/freenet/src/freenet/node/FNPPacketMangler.java        2007-12-05 
00:14:41 UTC (rev 16294)
+++ trunk/freenet/src/freenet/node/FNPPacketMangler.java        2007-12-05 
00:28:01 UTC (rev 16295)
@@ -223,6 +223,8 @@
                        if(length > Node.SYMMETRIC_KEY_LENGTH /* iv */ + 
HASH_LENGTH + 2 && !node.isStopping()) {
                                // Might be an auth packet
                                if(tryProcessAuth(buf, offset, length, opn, 
peer, false, now)) return;
+                               // Might be a reply to an anon auth packet
+                               if(tryProcessAuthAnonReply(buf, offset, length, 
opn, peer, now)) return;
                        }
                }
                PeerNode[] peers = crypto.getPeerNodes();
@@ -255,6 +257,7 @@
                                pn = peers[i];
                                if(pn == opn) continue;
                                if(tryProcessAuth(buf, offset, length, pn, 
peer,false, now)) return;
+                               if(tryProcessAuthAnonReply(buf, offset, length, 
pn, peer, now)) return;
                        }
                }
                OpennetManager opennet = node.getOpennet();
@@ -337,7 +340,8 @@
        }

        /**
-        * Might be an anonymous-initiator negotiation packet.
+        * Might be an anonymous-initiator negotiation packet (i.e.
+        * we are the responder).
         * Anonymous initiator is used for seednode connections, 
         * and will in future be used for other things for example
         * one-side-only invites, password based invites etc. 
@@ -400,6 +404,71 @@
                }
        }

+       /**
+        * Might be a reply to an anonymous-initiator negotiation
+        * packet (i.e. we are the initiator).
+        * Anonymous initiator is used for seednode connections, 
+        * and will in future be used for other things for example
+        * one-side-only invites, password based invites etc. 
+        * @param buf The buffer to read bytes from
+        * @param offset The offset at which to start reading
+        * @param length The number of bytes to read
+        * @param pn The PeerNode we think is responsible
+        * @param peer The Peer to send a reply to
+        * @param now The time at which the packet was received
+        * @return True if we handled a negotiation packet, false otherwise.
+        */
+       private boolean tryProcessAuthAnonReply(byte[] buf, int offset, int 
length, PeerNode pn, Peer peer, long now) {
+               BlockCipher authKey = pn.anonymousInitiatorSetupCipher;
+               // Does the packet match IV E( H(data) data ) ?
+               PCFBMode pcfb = PCFBMode.create(authKey);
+               int ivLength = pcfb.lengthIV();
+               MessageDigest md = SHA256.getMessageDigest();
+               int digestLength = HASH_LENGTH;
+               if(length < digestLength + ivLength + 4) {
+                       if(logMINOR) Logger.minor(this, "Too short: "+length+" 
should be at least "+(digestLength + ivLength + 4));
+                       SHA256.returnMessageDigest(md);
+                       return false;
+               }
+               // IV at the beginning
+               pcfb.reset(buf, offset);
+               // Then the hash, then the data
+               // => Data starts at ivLength + digestLength
+               // Decrypt the hash
+               byte[] hash = new byte[digestLength];
+               System.arraycopy(buf, offset+ivLength, hash, 0, digestLength);
+               pcfb.blockDecipher(hash, 0, hash.length);
+
+               int dataStart = ivLength + digestLength + offset+2;
+
+               int byte1 = ((pcfb.decipher(buf[dataStart-2])) & 0xff);
+               int byte2 = ((pcfb.decipher(buf[dataStart-1])) & 0xff);
+               int dataLength = (byte1 << 8) + byte2;
+               if(logMINOR) Logger.minor(this, "Data length: "+dataLength+" (1 
= "+byte1+" 2 = "+byte2+ ')');
+               if(dataLength > length - (ivLength+hash.length+2)) {
+                       if(logMINOR) Logger.minor(this, "Invalid data length 
"+dataLength+" ("+(length - (ivLength+hash.length+2))+") in tryProcessAuth");
+                       SHA256.returnMessageDigest(md);
+                       return false;
+               }
+               // Decrypt the data
+               byte[] payload = new byte[dataLength];
+               System.arraycopy(buf, dataStart, payload, 0, dataLength);
+               pcfb.blockDecipher(payload, 0, payload.length);
+
+               md.update(payload);
+               byte[] realHash = md.digest();
+               SHA256.returnMessageDigest(md); md = null;
+
+               if(Arrays.equals(realHash, hash)) {
+                       // Got one
+                       processDecryptedAuthAnonReply(payload, peer, pn);
+                       return true;
+               } else {
+                       if(logMINOR) Logger.minor(this, "Incorrect hash in 
tryProcessAuth for "+peer+" (length="+dataLength+"): \nreal 
hash="+HexUtil.bytesToHex(realHash)+"\n bad hash="+HexUtil.bytesToHex(hash));
+                       return false;
+               }
+       }
+       
        // Anonymous-initiator setup types
        /** Connect to a node hoping it will act as a seednode for us */
        static final byte SETUP_OPENNET_SEEDNODE = 1;
@@ -407,8 +476,6 @@
        private void processDecryptedAuthAnon(byte[] payload, Peer replyTo) {
                if(logMINOR) Logger.minor(this, "Processing decrypted auth 
packet from "+replyTo);

-               long now = System.currentTimeMillis();
-
                /** Protocol version. Should be 1. */
                int version = payload[0];
                /** Negotiation type. 2 = JFK. Other types might indicate other 
DH variants, 
@@ -444,12 +511,56 @@
                        processJFKMessage1(payload,4,null,replyTo);
                } else if(packetType == 2) {
                        // Phase 3
-                       processJFKMessage3(payload, 3, null, replyTo, false);
+                       processJFKMessage3(payload, 4, null, replyTo, false);
                } else {
                        Logger.error(this, "Invalid phase "+packetType+" for 
anonymous-initiator (we are the responder)");
                }
        }

+       private void processDecryptedAuthAnonReply(byte[] payload, Peer 
replyTo, PeerNode pn) {
+               if(logMINOR) Logger.minor(this, "Processing decrypted auth 
packet from "+replyTo+" for "+pn);
+               
+               /** Protocol version. Should be 1. */
+               int version = payload[0];
+               /** Negotiation type. 2 = JFK. Other types might indicate other 
DH variants, 
+                * or even non-DH-based algorithms such as password based key 
setup. */
+               int negType = payload[1];
+               /** Packet phase. */
+               int packetType = payload[2];
+               /** Setup type. See above. */
+               int setupType = payload[3];
+               
+               if(logMINOR) Logger.minor(this, "Received anonymous auth packet 
(phase="+packetType+", v="+version+", nt="+negType+", setup type="+setupType+") 
from "+replyTo+"");
+               
+               if(version != 1) {
+                       Logger.error(this, "Decrypted auth packet but invalid 
version: "+version);
+                       return;
+               }
+               if(negType != 2) {
+                       Logger.error(this, "Unknown neg type: "+negType);
+                       return;
+               }
+               
+               // Known setup types
+               if(setupType != SETUP_OPENNET_SEEDNODE) {
+                       Logger.error(this, "Unknown setup type "+negType);
+                       return;
+               }
+               
+               // We are the INITIATOR.
+               // Therefore, we can only get packets of phase 2 and 4 here.
+               
+               if(packetType == 1) {
+                       // Phase 2
+                       processJFKMessage2(payload, 4, pn, replyTo);
+               } else if(packetType == 3) {
+                       // Phase 4
+                       processJFKMessage4(payload, 4, pn, replyTo, false);
+               } else {
+                       Logger.error(this, "Invalid phase "+packetType+" for 
anonymous-initiator (we are the responder)");
+               }
+       }
+
        /**
         * Process a decrypted, authenticated auth packet.
         * @param payload The packet payload, after it has been decrypted.

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java        2007-12-05 00:14:41 UTC 
(rev 16294)
+++ trunk/freenet/src/freenet/node/PeerNode.java        2007-12-05 00:28:01 UTC 
(rev 16295)
@@ -222,6 +222,10 @@
        final BlockCipher incomingSetupCipher;
        /** Outgoing setup cipher (see above) */
        final BlockCipher outgoingSetupCipher;
+       /** Anonymous-connect cipher. This is used in link setup if
+        * we are trying to get a connection to this node even though
+        * it doesn't know us, e.g. as a seednode. */
+       final BlockCipher anonymousInitiatorSetupCipher;
        /** The context object for the currently running negotiation. */
        private KeyAgreementSchemeContext ctx;
        /** The other side's boot ID. This is a random number generated
@@ -464,6 +468,8 @@
                        incomingSetupCipher.initialize(incomingSetupKey);
                        outgoingSetupCipher = new Rijndael(256, 256);
                        outgoingSetupCipher.initialize(outgoingSetupKey);
+                       anonymousInitiatorSetupCipher = new Rijndael(256, 256);
+                       anonymousInitiatorSetupCipher.initialize(identityHash);
                } catch(UnsupportedCipherException e1) {
                        Logger.error(this, "Caught: " + e1);
                        throw new Error(e1);


Reply via email to