Author: toad
Date: 2008-12-05 20:14:58 +0000 (Fri, 05 Dec 2008)
New Revision: 24070

Modified:
   trunk/freenet/src/freenet/node/FNPPacketMangler.java
   trunk/freenet/src/freenet/node/PacketTracker.java
   trunk/freenet/src/freenet/node/PeerNode.java
   trunk/freenet/src/freenet/support/Fields.java
Log:
Neg type 4: negotiate during JFK(3) and JFK(4) whether to reuse the old 
PacketTracker.


Modified: trunk/freenet/src/freenet/node/FNPPacketMangler.java
===================================================================
--- trunk/freenet/src/freenet/node/FNPPacketMangler.java        2008-12-05 
16:50:17 UTC (rev 24069)
+++ trunk/freenet/src/freenet/node/FNPPacketMangler.java        2008-12-05 
20:14:58 UTC (rev 24070)
@@ -598,7 +598,7 @@
                        Logger.error(this, "Decrypted auth packet but invalid 
version: "+version);
                        return;
                }
-               if(!(negType == 2 || negType == 3)) {
+               if(!(negType == 2 || negType == 3 || negType == 4)) {
                        Logger.error(this, "Unknown neg type: "+negType);
                        return;
                }
@@ -663,7 +663,9 @@
                } else if (negType == 1) {
                        Logger.error(this, "Old StationToStation (negType 1) 
not supported.");
                        return;
-               } else if (negType==2 || negType == 3){ // negType == 3 => no 
new PacketTracker when rekeying
+               } else if (negType==2 || negType == 3 || negType == 4){
+                       // negType == 3 => no new PacketTracker when rekeying
+                       // negType == 4 => negotiate whether to use a new 
PacketTracker when rekeying
                        /*
                         * We implement Just Fast Keying key management 
protocol with active identity protection
                         * for the initiator and no identity protection for the 
responder
@@ -1045,6 +1047,7 @@
                        (c.getBlockSize() >> 3) + // IV
                        HASH_LENGTH + // it's at least a signature
                        8 +           // a bootid
+                       (negType >= 4 ? 8 : 0) + // packet tracker ID
                        1;            // znoderefI* is at least 1 byte long
                
                if(payload.length < expectedLength + 3) {
@@ -1148,9 +1151,20 @@
                decypheredPayloadOffset += Node.SIGNATURE_PARAMETER_LENGTH;
                byte[] data = new byte[decypheredPayload.length - 
decypheredPayloadOffset];
                System.arraycopy(decypheredPayload, decypheredPayloadOffset, 
data, 0, decypheredPayload.length - decypheredPayloadOffset);
-               long bootID = Fields.bytesToLong(data);
-               byte[] hisRef = new byte[data.length - 8];
-               System.arraycopy(data, 8, hisRef, 0, hisRef.length);
+               int ptr = 0;
+               long trackerID; 
+               if(negType >= 4) {
+                       trackerID = Fields.bytesToLong(data, ptr);
+                       if(trackerID < 0) trackerID = -1;
+                       ptr += 8;
+               } else {
+                       if(negType == 3) trackerID = -2; // emulate negtype = 3 
behaviour
+                       else trackerID = -1;
+               }
+               long bootID = Fields.bytesToLong(data, ptr);
+               ptr += 8;
+               byte[] hisRef = new byte[data.length - ptr];
+               System.arraycopy(data, ptr, hisRef, 0, hisRef.length);
                
                // construct the peernode
                if(unknownInitiator) {
@@ -1175,12 +1189,10 @@
                // At this point we know it's from the peer, so we can report a 
packet received.
                pn.receivedPacket(true, false);
                
-               // Send reply
-               sendJFKMessage4(1, negType, 3, nonceInitiator, 
nonceResponder,initiatorExponential, responderExponential, 
-                               c, Ke, Ka, authenticator, hisRef, pn, replyTo, 
unknownInitiator, setupType);
+               BlockCipher cs = null;
+               try { cs = new Rijndael(256, 256); } catch 
(UnsupportedCipherException e) {}
+               cs.initialize(Ks);
                
-               c.initialize(Ks);
-               
                // Promote if necessary
                boolean dontWant = false;
                if(oldOpennetPeer) {
@@ -1196,13 +1208,21 @@
                        // wantPeer will call node.peers.addPeer(), we don't 
have to.
                }
                
-               if(pn.completedHandshake(bootID, hisRef, 0, hisRef.length, c, 
Ks, replyTo, true, negType)) {
+               long newTrackerID = pn.completedHandshake(bootID, hisRef, 0, 
hisRef.length, cs, Ks, replyTo, true, negType, trackerID, false, false);
+               
+               if(newTrackerID > 0) {
+                       
+                       // Send reply
+                       sendJFKMessage4(1, negType, 3, nonceInitiator, 
nonceResponder,initiatorExponential, responderExponential, 
+                                       c, Ke, Ka, authenticator, hisRef, pn, 
replyTo, unknownInitiator, setupType, newTrackerID, newTrackerID == trackerID);
+                       
                        if(dontWant)
-                               node.peers.disconnect(pn, true, false);
+                               node.peers.disconnect(pn, true, false); // Let 
it connect then tell it to remove it.
                        else
                                pn.maybeSendInitialMessages();
                } else {
                        Logger.error(this, "Handshake failure! with 
"+pn.getPeer());
+                       // Don't send the JFK(4). We have not successfully 
connected.
                }
                
                final long t2=System.currentTimeMillis();
@@ -1279,6 +1299,7 @@
                        HASH_LENGTH + // HMAC of the cyphertext
                        (c.getBlockSize() >> 3) + // IV
                        Node.SIGNATURE_PARAMETER_LENGTH * 2 + // the signature
+                       (negType >= 4 ? 9 : 0) + // ID of packet tracker, plus 
boolean byte
                        8+ // bootID
                        1; // znoderefR
 
@@ -1344,25 +1365,39 @@
                decypheredPayloadOffset += Node.SIGNATURE_PARAMETER_LENGTH;
                byte[] data = new byte[decypheredPayload.length - 
decypheredPayloadOffset];
                System.arraycopy(decypheredPayload, decypheredPayloadOffset, 
data, 0, decypheredPayload.length - decypheredPayloadOffset);
-               long bootID = Fields.bytesToLong(data);
-               if(data.length - 8 < 0) {
+               int ptr = 0;
+               long trackerID;
+               boolean reusedTracker;
+               if(negType >= 4) {
+                       trackerID = Fields.bytesToLong(data, ptr);
+                       ptr += 8;
+                       reusedTracker = data[ptr++] != 0;
+               } else {
+                       if(negType == 3) trackerID = -2;
+                       else trackerID = -1;
+                       reusedTracker = false;
+               }
+               long bootID = Fields.bytesToLong(data, ptr);
+               ptr += 8;
+               if(data.length - ptr < 0) {
                        Logger.error(this, "No space for hisRef: 
data.length="+data.length+" 
myRef.length="+(pn.jfkMyRef==null?0:pn.jfkMyRef.length)+" orig data length 
"+(payload.length-inputOffset));
                        return true;
                }
-               byte[] hisRef = new byte[data.length - 8];
-               System.arraycopy(data, 8, hisRef, 0, hisRef.length);
+               byte[] hisRef = new byte[data.length - ptr];
+               System.arraycopy(data, ptr, hisRef, 0, hisRef.length);
                
                // verify the signature
                DSASignature remoteSignature = new DSASignature(new 
NativeBigInteger(1,r), new NativeBigInteger(1,s));
-               byte[] locallyGeneratedText = new byte[NONCE_SIZE * 2 + 
DiffieHellman.modulusLengthInBytes() * 2 + crypto.myIdentity.length + 8 
/*bootID*/ + hisRef.length + pn.jfkMyRef.length];
+               int dataLen = hisRef.length + 8 + (negType >= 4 ? 9 : 0);
+               byte[] locallyGeneratedText = new byte[NONCE_SIZE * 2 + 
DiffieHellman.modulusLengthInBytes() * 2 + crypto.myIdentity.length + dataLen + 
pn.jfkMyRef.length];
                int bufferOffset = NONCE_SIZE * 2 + 
DiffieHellman.modulusLengthInBytes()*2;
                System.arraycopy(jfkBuffer, 0, locallyGeneratedText, 0, 
bufferOffset);
                byte[] identity = crypto.getIdentity(unknownInitiator);
                System.arraycopy(identity, 0, locallyGeneratedText, 
bufferOffset, identity.length);
                bufferOffset += identity.length;
                // bootID
-               System.arraycopy(data, 0, locallyGeneratedText, bufferOffset, 
hisRef.length + 8);
-               bufferOffset += hisRef.length + 8;
+               System.arraycopy(data, 0, locallyGeneratedText, bufferOffset, 
dataLen);
+               bufferOffset += dataLen;
                System.arraycopy(pn.jfkMyRef, 0, locallyGeneratedText, 
bufferOffset, pn.jfkMyRef.length);
                byte[] messageHash = SHA256.digest(locallyGeneratedText);
                if(!DSA.verify(pn.peerPubKey, remoteSignature, new 
NativeBigInteger(1, messageHash), false)) {
@@ -1391,7 +1426,7 @@
                
                // We change the key
                c.initialize(pn.jfkKs);
-               if(pn.completedHandshake(bootID, data, 8, data.length - 8, c, 
pn.jfkKs, replyTo, false, negType)) {
+               if(pn.completedHandshake(bootID, hisRef, 0, hisRef.length, c, 
pn.jfkKs, replyTo, false, negType, trackerID, true, reusedTracker) >= 0) {
                        if(dontWant)
                                node.peers.disconnect(pn, true, false);
                        else
@@ -1442,9 +1477,19 @@
                if(ctx == null) return;
                byte[] ourExponential = 
stripBigIntegerToNetworkFormat(ctx.myExponential);
                pn.jfkMyRef = unknownInitiator ? 
crypto.myCompressedHeavySetupRef() : crypto.myCompressedSetupRef();
-               byte[] data = new byte[8 + pn.jfkMyRef.length];
-               System.arraycopy(Fields.longToBytes(node.bootID), 0, data, 0, 
8);
-               System.arraycopy(pn.jfkMyRef, 0, data, 8, pn.jfkMyRef.length);
+               byte[] data = new byte[(negType >= 4 ? 8 : 0) + 8 + 
pn.jfkMyRef.length];
+               int ptr = 0;
+               if(negType >= 4) {
+                       long trackerID;
+                       if(pn == null)
+                               trackerID = -1;
+                       else trackerID = pn.getReusableTrackerID();
+                       System.arraycopy(Fields.longToBytes(trackerID), 0, 
data, ptr, 8);
+                       ptr += 8;
+               }
+               System.arraycopy(Fields.longToBytes(node.bootID), 0, data, ptr, 
8);
+               ptr += 8;
+               System.arraycopy(pn.jfkMyRef, 0, data, ptr, pn.jfkMyRef.length);
                final byte[] message3 = new byte[NONCE_SIZE*2 + // nI, nR
                                           
DiffieHellman.modulusLengthInBytes()*2 + // g^i, g^r
                                           HASH_LENGTH + // authenticator
@@ -1554,7 +1599,7 @@
         * @param pn The PeerNode to encrypt the auth packet to. Cannot be 
null, because even in anonymous initiator,
         * we will have created one before calling this method.
         */
-       private void sendJFKMessage4(int version,int negType,int phase,byte[] 
nonceInitiator,byte[] nonceResponder,byte[] initiatorExponential,byte[] 
responderExponential, BlockCipher c, byte[] Ke, byte[] Ka, byte[] 
authenticator, byte[] hisRef, PeerNode pn, Peer replyTo, boolean 
unknownInitiator, int setupType)
+       private void sendJFKMessage4(int version,int negType,int phase,byte[] 
nonceInitiator,byte[] nonceResponder,byte[] initiatorExponential,byte[] 
responderExponential, BlockCipher c, byte[] Ke, byte[] Ka, byte[] 
authenticator, byte[] hisRef, PeerNode pn, Peer replyTo, boolean 
unknownInitiator, int setupType, long newTrackerID, boolean sameAsOldTrackerID)
        {
                if(logMINOR)
                        Logger.minor(this, "Sending a JFK(4) message to 
"+pn.getPeer());
@@ -1563,11 +1608,20 @@
                NativeBigInteger _initiatorExponential = new 
NativeBigInteger(1,initiatorExponential);
                
                byte[] myRef = crypto.myCompressedSetupRef();
-               byte[] data = new byte[8 + myRef.length + hisRef.length];
-               System.arraycopy(Fields.longToBytes(node.bootID), 0, data, 0, 
8);
-               System.arraycopy(myRef, 0, data, 8, myRef.length);
-               System.arraycopy(hisRef, 0, data, 8 + myRef.length, 
hisRef.length);
+               byte[] data = new byte[(negType >= 4 ? 9 : 0) + 8 + 
myRef.length + hisRef.length];
+               int ptr = 0;
+               if(negType >= 4) {
+                       System.arraycopy(Fields.longToBytes(newTrackerID), 0, 
data, ptr, 8);
+                       ptr += 8;
+                       data[ptr++] = (byte) (sameAsOldTrackerID ? 1 : 0);
+               }
                
+               System.arraycopy(Fields.longToBytes(node.bootID), 0, data, ptr, 
8);
+               ptr += 8;
+               System.arraycopy(myRef, 0, data, ptr, myRef.length);
+               ptr += myRef.length;
+               System.arraycopy(hisRef, 0, data, ptr, hisRef.length);
+               
                byte[] params = assembleDHParams(nonceInitiator, 
nonceResponder, _initiatorExponential, _responderExponential, pn.identity, 
data);
                byte[] messageHash = SHA256.digest(params);
                if(logMINOR)
@@ -2816,7 +2870,7 @@
        }
 
        public int[] supportedNegTypes() {
-               return new int[] { 2, 3 };
+               return new int[] { 2, 3, 4 };
        }
 
        public int fullHeadersLengthOneMessage() {

Modified: trunk/freenet/src/freenet/node/PacketTracker.java
===================================================================
--- trunk/freenet/src/freenet/node/PacketTracker.java   2008-12-05 16:50:17 UTC 
(rev 24069)
+++ trunk/freenet/src/freenet/node/PacketTracker.java   2008-12-05 20:14:58 UTC 
(rev 24070)
@@ -78,9 +78,16 @@
        final long createdTime;
        /** The time at which we last successfully decoded a packet. */
        private long timeLastDecodedPacket;
+       /** Tracker ID. Must be positive. */
+       final long trackerID;
 
        /** Everything is clear to start with */
        PacketTracker(PeerNode pn) {
+               this(pn, pn.node.random.nextLong() & Long.MAX_VALUE);
+       }
+
+       PacketTracker(PeerNode pn, long tid) {
+               trackerID = tid;
                this.pn = pn;
                ackQueue = new LinkedList<QueuedAck>();
                forgottenQueue = new LinkedList<QueuedForgotten>();

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java        2008-12-05 16:50:17 UTC 
(rev 24069)
+++ trunk/freenet/src/freenet/node/PeerNode.java        2008-12-05 20:14:58 UTC 
(rev 24070)
@@ -1812,11 +1812,21 @@
        * @param length Number of bytes to read.
        * @param encKey The new session key.
        * @param replyTo The IP the handshake came in on.
-       * @return True unless we rejected the handshake, or it failed to parse.
+       * @param trackerID The tracker ID proposed by the other side. If -1, 
create a new tracker. If -2,
+       * reuse the old tracker if possible. If any other value, check whether 
we have it, and if we do,
+       * return that, otherwise return the ID of the new tracker.
+       * @param isJFK4 If true, we are processing a JFK(4) and must respect 
the tracker ID chosen by the 
+       * responder. If false, we are processing a JFK(3) and we can either 
reuse the suggested tracker ID,
+       * which the other side is able to reuse, or we can create a new tracker 
ID.
+       * @param jfk4SameAsOld If true, the responder chose to use the tracker 
ID that we provided. If
+       * we don't have it now the connection fails.
+       * @return The ID of the new PacketTracker. If this is different to the 
passed-in trackerID, then
+       * it's a new tracker. -1 to indicate failure.
        */
-       public boolean completedHandshake(long thisBootID, byte[] data, int 
offset, int length, BlockCipher encCipher, byte[] encKey, Peer replyTo, boolean 
unverified, int negType) {
+       public long completedHandshake(long thisBootID, byte[] data, int 
offset, int length, BlockCipher encCipher, byte[] encKey, Peer replyTo, boolean 
unverified, int negType, long trackerID, boolean isJFK4, boolean jfk4SameAsOld) 
{
                logMINOR = Logger.shouldLog(Logger.MINOR, PeerNode.class);
                long now = System.currentTimeMillis();
+               if(logMINOR) Logger.minor(this, "Tracker ID "+trackerID+" 
isJFK4="+isJFK4+" jfk4SameAsOld="+jfk4SameAsOld);
 
                // Update sendHandshakeTime; don't send another handshake for a 
while.
                // If unverified, "a while" determines the timeout; if not, 
it's just good practice to avoid a race below.
@@ -1833,7 +1843,7 @@
                        }
                        Logger.error(this, "Failed to parse new noderef for " + 
this + ": " + e1, e1);
                        node.peers.disconnected(this);
-                       return false;
+                       return -1;
                }
                boolean routable = true;
                boolean newer = false;
@@ -1869,6 +1879,7 @@
                KeyTracker prev = null;
                KeyTracker newTracker;
                MessageItem[] messagesTellDisconnected = null;
+               PacketTracker packets = null;
                synchronized(this) {
                        handshakeCount = 0;
                        bogusNoderef = false;
@@ -1894,11 +1905,52 @@
                        } else if(bootIDChanged && logMINOR)
                                Logger.minor(this, "Changed boot ID from " + 
bootID + " to " + thisBootID + " for " + getPeer());
                        this.bootID = thisBootID;
-                       PacketTracker packets;
                        boolean newPacketTracker = false;
-                       if(bootIDChanged) {
+                       if(currentTracker != null && 
currentTracker.packets.trackerID == trackerID && 
!currentTracker.packets.isDeprecated()) {
+                               if(isJFK4 && !jfk4SameAsOld)
+                                       Logger.error(this, "In JFK(4), found 
tracker ID "+trackerID+" but other side says is new! for "+this);
+                               packets = currentTracker.packets;
+                               if(logMINOR) Logger.minor(this, "Re-using 
packet tracker ID "+trackerID+" on "+this+" from current "+currentTracker);
+                       } else if(previousTracker != null && 
previousTracker.packets.trackerID == trackerID && 
!previousTracker.packets.isDeprecated()) {
+                               if(isJFK4 && !jfk4SameAsOld)
+                                       Logger.error(this, "In JFK(4), found 
tracker ID "+trackerID+" but other side says is new! for "+this);
+                               packets = previousTracker.packets;
+                               if(logMINOR) Logger.minor(this, "Re-using 
packet tracker ID "+trackerID+" on "+this+" from prev "+previousTracker);
+                       } else if(isJFK4 && jfk4SameAsOld) {
+                               isConnected = false;
+                               Logger.error(this, "Can't reuse old tracker ID 
"+trackerID+" as instructed - disconnecting");
+                               return -1;
+                       } else if(trackerID == -1) {
+                               // Create a new tracker unconditionally
                                packets = new PacketTracker(this);
                                newPacketTracker = true;
+                               if(logMINOR) Logger.minor(this, "Creating new 
PacketTracker as instructed for "+this);
+                       } else if(trackerID == -2 && !bootIDChanged) {
+                               // Reuse if not deprecated and not boot ID 
changed
+                               if(currentTracker != null && 
!currentTracker.packets.isDeprecated() && negType >= 3) {
+                                       packets = currentTracker.packets;
+                                       if(logMINOR) Logger.minor(this, 
"Re-using packet tracker (not given an ID): "+packets.trackerID+" on "+this+" 
from current "+currentTracker);
+                               } else if(previousTracker != null && 
!previousTracker.packets.isDeprecated() && negType >= 3) {
+                                       packets = previousTracker.packets;
+                                       if(logMINOR) Logger.minor(this, 
"Re-using packet tracker (not given an ID): "+packets.trackerID+" on "+this+" 
from prev "+previousTracker);
+                               } else {
+                                       packets = new PacketTracker(this);
+                                       newPacketTracker = true;
+                                       if(logMINOR) Logger.minor(this, "Cannot 
reuse trackers (not given an ID) on "+this);
+                               }
+                       } else {
+                               if(isJFK4 && negType >= 4 && trackerID < 0)
+                                       Logger.error(this, "JFK(4) packet with 
neg type "+negType+" has negative tracker ID: "+trackerID);
+                                       
+                               if(isJFK4/* && !jfk4SameAsOld implied */ && 
trackerID >= 0) {
+                                       packets = new PacketTracker(this, 
trackerID);
+                               } else
+                                       packets = new PacketTracker(this);
+                               newPacketTracker = true;
+                               if(logMINOR) Logger.minor(this, "Creating new 
tracker (last resort) on "+this);
+                       }
+                       if(bootIDChanged) {
+                               newPacketTracker = true;
                                oldPrev = previousTracker;
                                oldCur = currentTracker;
                                previousTracker = null;
@@ -1910,14 +1962,6 @@
                                this.offeredMainJarVersion = 0;
                        } else {
                                // else it's a rekey
-                               if(currentTracker != null && 
!currentTracker.packets.isDeprecated() && negType >= 3)
-                                       packets = currentTracker.packets;
-                               else if(previousTracker != null && 
!previousTracker.packets.isDeprecated() && negType >= 3)
-                                       packets = previousTracker.packets;
-                               else {
-                                       packets = new PacketTracker(this);
-                                       newPacketTracker = true;
-                               }
                        }
                        newTracker = new KeyTracker(this, packets, encCipher, 
encKey);
                        if(logMINOR) Logger.minor(this, "New key tracker in 
completedHandshake: "+newTracker+" for "+shortToString()+" neg type "+negType+" 
new packet tracker: "+newPacketTracker);
@@ -1990,7 +2034,7 @@
                        onConnect();
                }
                
-               return true;
+               return packets.trackerID;
        }
 
        /**
@@ -4201,4 +4245,17 @@
                
                return false;
        }
+
+       /**
+        * @return The ID of a reusable PacketTracker if there is one, 
otherwise -1.
+        */
+       public long getReusableTrackerID() {
+               KeyTracker cur;
+               synchronized(this) {
+                       cur = currentTracker;
+               }
+               if(cur == null) return -1;
+               if(cur.packets.isDeprecated()) return -1;
+               return cur.packets.trackerID;
+       }
 }

Modified: trunk/freenet/src/freenet/support/Fields.java
===================================================================
--- trunk/freenet/src/freenet/support/Fields.java       2008-12-05 16:50:17 UTC 
(rev 24069)
+++ trunk/freenet/src/freenet/support/Fields.java       2008-12-05 20:14:58 UTC 
(rev 24070)
@@ -502,11 +502,18 @@
         * Convert an array of bytes to a single long.
         */
        public static long bytesToLong(byte[] buf) {
-               if(buf.length < 8)
+               return bytesToLong(buf, 0);
+       }
+
+       /**
+        * Convert an array of bytes to a single long.
+        */
+       public static long bytesToLong(byte[] buf, int offset) {
+               if(buf.length < 8 + offset)
                        throw new IllegalArgumentException();
                long x = 0;
                for(int j = 7; j >= 0; j--) {
-                       long y = (buf[j] & 0xff);
+                       long y = (buf[j + offset] & 0xff);
                        x = (x << 8) | y;
                }
                return x;

_______________________________________________
cvs mailing list
[email protected]
http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs

Reply via email to