Logger.minor(this, error);
} else {
Logger.error(this, error);
}
}
BlockCipher c = null;
try { c = new Rijndael(256, 256); } catch (UnsupportedCipherException e) { throw new RuntimeException(e); }
final int expectedLength =
HASH_LENGTH + // HMAC of the cyphertext
(c.getBlockSize() >> 3) + // IV
signLength + // the signature
9 + // ID of packet tracker, plus boolean byte
8+ // bootID
1; // znoderefR
if(payload.length - inputOffset < expectedLength + 3) {
Logger.error(this, "Packet too short from "+pn.getPeer()+": "+payload.length+" after decryption in JFK(4), should be "+(expectedLength + 3));
return false;
}
byte[] jfkBuffer = pn.getJFKBuffer();
if(jfkBuffer == null) {
Logger.normal(this, "We have already handled this message... might be a replay or a bug - "+pn);
return false;
}
byte[] hmac = Arrays.copyOfRange(payload, inputOffset, inputOffset+HASH_LENGTH);
inputOffset += HASH_LENGTH;
c.initialize(pn.jfkKe);
int ivLength = PCFBMode.lengthIV(c);
int decypheredPayloadOffset = 0;
// We compute the HMAC of ("R"+cyphertext) : the cyphertext includes the IV!
byte[] decypheredPayload = Arrays.copyOf(JFK_PREFIX_RESPONDER, JFK_PREFIX_RESPONDER.length + payload.length - inputOffset);
decypheredPayloadOffset += JFK_PREFIX_RESPONDER.length;
System.arraycopy(payload, inputOffset, decypheredPayload, decypheredPayloadOffset, payload.length-inputOffset);
if(!HMAC.verifyWithSHA256(pn.jfkKa, decypheredPayload, hmac)) {
Logger.normal(this, "The digest-HMAC doesn't match; let's discard the packet - "+pn.getPeer());
return false;
}
// Try to find the HMAC in the cache:
// If it is already present it indicates duplicate/replayed message4 and we can discard
// If it's not, we can add it with a timestamp
byte[] message4Timestamp = null;
synchronized (authenticatorCache) {
ByteArrayWrapper hmacBAW = new ByteArrayWrapper(hmac);
message4Timestamp = authenticatorCache.get(hmacBAW);
if(message4Timestamp == null) { // normal behaviour
authenticatorCache.put(hmacBAW, Fields.longToBytes(t1));
}
}
if(message4Timestamp != null) {
Logger.normal(this, "We got a replayed message4 (first handled at "+TimeUtil.formatTime(t1-Fields.bytesToLong(message4Timestamp))+") from - "+pn);
return true;
}
// Get the IV
final PCFBMode pk = PCFBMode.create(c, decypheredPayload, decypheredPayloadOffset);
decypheredPayloadOffset += ivLength;
// Decrypt the payload
pk.blockDecipher(decypheredPayload, decypheredPayloadOffset, decypheredPayload.length - decypheredPayloadOffset);
/*
* DecipheredData Format:
* Signature-r,s
* bootID, znoderef
*/
byte[] sig = new byte[signLength];
System.arraycopy(decypheredPayload, decypheredPayloadOffset, sig, 0, signLength);
decypheredPayloadOffset += signLength;
byte[] data = new byte[decypheredPayload.length - decypheredPayloadOffset];
System.arraycopy(decypheredPayload, decypheredPayloadOffset, data, 0, decypheredPayload.length - decypheredPayloadOffset);
int ptr = 0;
long trackerID;
boolean reusedTracker;
trackerID = Fields.bytesToLong(data, ptr);
ptr += 8;
reusedTracker = data[ptr++] != 0;
long bootID = Fields.bytesToLong(data, ptr);
ptr += 8;
byte[] hisRef = Arrays.copyOfRange(data, ptr, data.length);
// verify the signature
int dataLen = hisRef.length + 8 + 9;
int nonceSize = getNonceSize(negType);
int nonceSizeHashed = (negType > 8 ? HASH_LENGTH : nonceSize);
byte[] identity = crypto.getIdentity(negType, unknownInitiator);
byte[] locallyGeneratedText = new byte[nonceSizeHashed + nonceSize + modulusLength * 2 + identity.length + dataLen + pn.jfkMyRef.length];
int bufferOffset = nonceSizeHashed + nonceSize + modulusLength*2;
System.arraycopy(jfkBuffer, 0, locallyGeneratedText, 0, bufferOffset);
System.arraycopy(identity, 0, locallyGeneratedText, bufferOffset, identity.length);
bufferOffset += identity.length;
// bootID
System.arraycopy(data, 0, locallyGeneratedText, bufferOffset, dataLen);
bufferOffset += dataLen;
System.arraycopy(pn.jfkMyRef, 0, locallyGeneratedText, bufferOffset, pn.jfkMyRef.length);
if(negType < 9) { // DSA sig
byte[] r = new byte[Node.SIGNATURE_PARAMETER_LENGTH];
System.arraycopy(sig, 0, r, 0, Node.SIGNATURE_PARAMETER_LENGTH);
byte[] s = new byte[Node.SIGNATURE_PARAMETER_LENGTH];
System.arraycopy(sig, Node.SIGNATURE_PARAMETER_LENGTH, s, 0, Node.SIGNATURE_PARAMETER_LENGTH);
DSASignature remoteSignature = new DSASignature(new NativeBigInteger(1,r), new NativeBigInteger(1,s));
byte[] messageHash = SHA256.digest(locallyGeneratedText);
if(!DSA.verify(pn.peerPubKey, remoteSignature, new NativeBigInteger(1, messageHash), false)) {
String error = "The signature verification has failed!! JFK(4) -"+pn.getPeer()+" message hash "+HexUtil.bytesToHex(messageHash)+" length "+locallyGeneratedText.length+" hisRef "+hisRef.length+" hash "+Fields.hashCode(hisRef)+" myRef "+pn.jfkMyRef.length+" hash "+Fields.hashCode(pn.jfkMyRef)+" boot ID "+bootID;
Logger.error(this, error);
return true;
}
} else { // ECDSA sig
if(!ECDSA.verify(Curves.P256, pn.peerECDSAPubKey(), sig, locallyGeneratedText)) {
Logger.error(this, "The ECDSA signature verification has failed!! JFK(4) - "+pn.getPeer()+" length "+locallyGeneratedText.length+" hisRef "+hisRef.length+" hash "+Fields.hashCode(hisRef)+" myRef "+pn.jfkMyRef.length+" hash "+Fields.hashCode(pn.jfkMyRef)+" boot ID "+bootID);
return true;
}
}
// Received a packet
pn.receivedPacket(true, false);
// Promote if necessary
boolean dontWant = false;
if(oldOpennetPeer && pn instanceof OpennetPeerNode /* true */) {
OpennetPeerNode opn = (OpennetPeerNode) pn;
OpennetManager opennet = node.getOpennet();
if(opennet == null) {
Logger.normal(this, "Dumping incoming old-opennet peer as opennet just turned off: "+pn+".");
return true;
}
/* When an old-opennet-peer connects, add it at the top of the LRU, so that it isn't
* immediately dropped when there is no droppable peer to drop. If it was dropped
* from the bottom of the LRU list, we would not have added it to the LRU; so it was
* somewhere in the middle. */
if(!opennet.wantPeer(opn, false, false, true, ConnectionType.RECONNECT)) {
Logger.normal(this, "No longer want peer "+pn+" - dumping it after connecting");
dontWant = true;
opennet.purgeOldOpennetPeer(opn);
}
// wantPeer will call node.peers.addPeer(), we don't have to.
}
if((!dontWant) && !crypto.allowConnection(pn, replyTo.getFreenetAddress())) {
Logger.normal(this, "Rejecting connection because already have something with the same IP");
dontWant = true;
}
// Set acknowledging method acording to negType
pn.setAcknowledgeType(negType);
// We change the key
BlockCipher ivCipher = null;
BlockCipher outgoingCipher = null;
BlockCipher incommingCipher = null;
try {
ivCipher = new Rijndael(256, 256);
outgoingCipher = new Rijndael(256, 256);
incommingCipher = new Rijndael(256, 256);
} catch (UnsupportedCipherException e) {
throw new RuntimeException(e);
}
outgoingCipher.initialize(pn.outgoingKey);
incommingCipher.initialize(pn.incommingKey);
ivCipher.initialize(pn.ivKey);
long newTrackerID = pn.completedHandshake(
bootID, hisRef, 0, hisRef.length, outgoingCipher, pn.outgoingKey, incommingCipher,
pn.incommingKey, replyTo, false, negType, trackerID, true, reusedTracker, pn.hmacKey,