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