/**
* Copyright (c) 2007-2009 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
*
* This file is part of SMaRt.
*
* SMaRt is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SMaRt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SMaRt. If not, see <http://www.gnu.org/licenses/>.
*/
package bftsmart.communication.client.netty;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.security.Signature;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import bftsmart.reconfiguration.ViewManager;
import bftsmart.tom.core.messages.TOMMessage;
import bftsmart.tom.util.Logger;
/**
*
* @author Paulo Sousa
*/
@ChannelPipelineCoverage("one")
public class NettyTOMMessageDecoder extends FrameDecoder {
/**
* number of measures used to calculate statistics
*/
//private final int BENCHMARK_PERIOD = 10000;
private boolean isClient;
private Map sessionTable;
private SecretKey authKey;
//private Storage st;
private int macSize;
private int signatureSize;
private ViewManager manager;
private boolean firstTime;
private ReentrantReadWriteLock rl;
//******* EDUARDO BEGIN: commented out some unused variables **************//
//private long numReceivedMsgs = 0;
//private long lastMeasurementStart = 0;
//private long max=0;
//private Storage st;
//private int count = 0;
//private Signature signatureEngine;
//******* EDUARDO END **************//
private boolean useMAC;
public NettyTOMMessageDecoder(boolean isClient, Map sessionTable, SecretKey authKey, int macLength, ViewManager manager, ReentrantReadWriteLock rl, int signatureLength, boolean useMAC) {
this.isClient = isClient;
this.sessionTable = sessionTable;
this.authKey = authKey;
//this.st = new Storage(benchmarkPeriod);
this.macSize = macLength;
this.manager = manager;
this.firstTime = true;
this.rl = rl;
this.signatureSize = signatureLength;
//this.st = new Storage(BENCHMARK_PERIOD);
this.useMAC = useMAC;
bftsmart.tom.util.Logger.println("new NettyTOMMessageDecoder!!, isClient=" + isClient);
}
@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) {
// Wait until the length prefix is available.
if (buffer.readableBytes() < 4) {
return null;
}
int dataLength = buffer.getInt(buffer.readerIndex());
//Logger.println("Receiving message with "+dataLength+" bytes.");
// Wait until the whole data is available.
if (buffer.readableBytes() < dataLength + 4) {
return null;
}
// Skip the length field because we know it already.
buffer.skipBytes(4);
int totalLength = dataLength - 1;
//read control byte indicating if message is signed
byte signed = buffer.readByte();
int authLength = 0;
if (signed == 1) {
authLength += signatureSize;
}
if (useMAC) {
authLength += macSize;
}
byte[] data = new byte[totalLength - authLength];
buffer.readBytes(data);
byte[] digest = null;
if (useMAC) {
digest = new byte[macSize];
buffer.readBytes(digest);
}
byte[] signature = null;
if (signed == 1) {
signature = new byte[signatureSize];
buffer.readBytes(signature);
}
DataInputStream dis = null;
TOMMessage sm = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
dis = new DataInputStream(bais);
sm = new TOMMessage();
sm.rExternal(dis);
sm.serializedMessage = data;
if (signed == 1) {
sm.serializedMessageSignature = signature;
sm.signed = true;
}
if (useMAC) {
sm.serializedMessageMAC = digest;
}
if (isClient) {
//verify MAC
if (useMAC) {
if (!verifyMAC(sm.getSender(), data, digest)) {
Logger.println("MAC error: message discarded");
return null;
}
}
} else { /* it's a server */
//verifies MAC if it's not the first message received from the client
rl.readLock().lock();
if (sessionTable.containsKey(sm.getSender())) {
rl.readLock().unlock();
if (useMAC) {
if (!verifyMAC(sm.getSender(), data, digest)) {
Logger.println("MAC error: message discarded");
return null;
}
}
} else {
//creates MAC/publick key stuff if it's the first message received from the client
bftsmart.tom.util.Logger.println("Creating MAC/public key stuff, first message from client" + sm.getSender());
bftsmart.tom.util.Logger.println("sessionTable size=" + sessionTable.size());
rl.readLock().unlock();
//******* EDUARDO BEGIN **************//
Mac macSend = Mac.getInstance(manager.getStaticConf().getHmacAlgorithm());
macSend.init(authKey);
Mac macReceive = Mac.getInstance(manager.getStaticConf().getHmacAlgorithm());
macReceive.init(authKey);
NettyClientServerSession cs = new NettyClientServerSession(channel, macSend, macReceive, sm.getSender(), manager.getStaticConf().getRSAPublicKey(sm.getSender()), new ReentrantLock());
//******* EDUARDO END **************//
rl.writeLock().lock();
sessionTable.put(sm.getSender(), cs);
bftsmart.tom.util.Logger.println("#active clients " + sessionTable.size());
rl.writeLock().unlock();
if (useMAC && !verifyMAC(sm.getSender(), data, digest)) {
Logger.println("MAC error: message discarded");
return null;
}
}
}
return sm;
} catch (Exception ex) {
bftsmart.tom.util.Logger.println("Impossible to decode message: "+
ex.getMessage());
ex.printStackTrace();
}
return null;
}
boolean verifyMAC(int id, byte[] data, byte[] digest) {
//long startInstant = System.nanoTime();
rl.readLock().lock();
Mac macReceive = ((NettyClientServerSession) sessionTable.get(id)).getMacReceive();
rl.readLock().unlock();
boolean result = Arrays.equals(macReceive.doFinal(data), digest);
//long duration = System.nanoTime() - startInstant;
//st.store(duration);
return result;
}
}