On Sat, Dec 6, 2008 at 4:14 AM, <toad at freenetproject.org> wrote:
> 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.
Can we remove type 3 now? All nodes support type 2.
> 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
> cvs at freenetproject.org
> http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs
>