/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package userGeneratedContent.testbedPlugIns.layerPlugIns.layer2recodingScheme.noDelay_v0_001; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import staticContent.framework.AnonNode; import staticContent.framework.controller.Implementation; import staticContent.framework.interfaces.Layer2RecodingSchemeMix; import staticContent.framework.message.MixMessage; import staticContent.framework.message.Reply; import staticContent.framework.message.Request; import staticContent.framework.message.ExternalMessage.DummyStatus; import staticContent.framework.routing.MixList; import staticContent.framework.routing.RoutingMode; import staticContent.framework.routing.UnpackedIdArray; import staticContent.framework.userDatabase.User; import staticContent.framework.util.Util; public class MixPlugIn extends Implementation implements Layer2RecodingSchemeMix { private SecureRandom secureRandom; private boolean RECURSIVE_HEADERS_ENABLED = false; @Override public void constructor() { } @Override public void initialize() { try { this.secureRandom = SecureRandom.getInstance(settings.getProperty("PRNG_ALGORITHM")); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); System.exit(1); } if (settings.isPropertyPresent("REQUIRE_RECURSIVE_HEADERS") && settings.getPropertyAsBoolean("REQUIRE_RECURSIVE_HEADERS")) this.RECURSIVE_HEADERS_ENABLED = true; } @Override public void begin() { if (anonNode.IS_DUPLEX) new ReplyThread().start(); new RequestThread().start(); } @Override public int getMaxSizeOfNextReply() { return anonNode.MAX_PAYLOAD; } @Override public int getMaxSizeOfNextRequest() { return anonNode.MAX_PAYLOAD; /*removed, because the payload should always be the same (headers are allowed to become bigger) * if (anonNode.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) { return anonNode.MAX_PAYLOAD; } else if (anonNode.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { return anonNode.MAX_PAYLOAD - getRouteHeaderSize(anonNode); } else if (anonNode.ROUTING_MODE == RoutingMode.DYNAMIC_ROUTING) { return anonNode.MAX_PAYLOAD; } else { throw new RuntimeException("not supported routing mode: " +anonNode.ROUTING_MODE); }*/ } @Override public Request generateDummy(int[] route, User user) { throw new RuntimeException("not supported"); /*Request request = MixMessage.getInstanceRequest(new byte[0], settings); request.setOwner(user); request.route = route; return request;*/ } @Override public Request generateDummy(User user) { byte[] padding = new byte[anonNode.MAX_PAYLOAD]; secureRandom.nextBytes(padding); byte[] lengthHeader = Util.shortToByteArray(0); byte[] message = Util.concatArrays(lengthHeader, padding); Request request = MixMessage.getInstanceRequest(message); request.setOwner(user); request.setDummyStatus(DummyStatus.DUMMY); return request; } @Override public Reply generateDummyReply(int[] route, User user) { throw new RuntimeException("not supported"); /*Reply reply = MixMessage.getInstanceReply(new byte[0], settings); reply.setOwner(user); reply.route = route; return reply;*/ } @Override public Reply generateDummyReply(User user) { Reply reply; if (!anonNode.IS_LAST_MIX) { byte[] padding = new byte[anonNode.MAX_PAYLOAD]; secureRandom.nextBytes(padding); byte[] lengthHeader = Util.shortToByteArray(0); byte[] message = Util.concatArrays(lengthHeader, padding); reply = MixMessage.getInstanceReply(message); } else { // will be done later by ReplyThread reply = MixMessage.getInstanceReply(new byte[0]); } reply.setOwner(user); reply.isFirstReplyHop = true; reply.setDummyStatus(DummyStatus.DUMMY); return reply; } class RequestThread extends Thread { @Override public void run() { while (true) { Request[] requests = anonNode.getFromRequestInputQueue(); for (Request request:requests) { if (anonNode.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) { if (RECURSIVE_HEADERS_ENABLED) extractHeader(request); } else if (anonNode.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // TODO: RECURSIVE_HEADERS_ENABLED... byte[][] splitted = Util.split(getRouteHeaderSize(anonNode), request.getByteMessage()); UnpackedIdArray routeInfo = MixList.unpackIdArrayWithPos(splitted[0]); if (routeInfo.pos >= routeInfo.route.length) { if (anonNode.DISPLAY_ROUTE_INFO) System.out.println(""+anonNode +" setting nextHopAddress to \"LAST HOP\" (pos: " +routeInfo.pos +")"); request.nextHopAddress = MixMessage.NONE; request.setByteMessage(splitted[1]); } else { if (anonNode.DISPLAY_ROUTE_INFO) System.out.println(""+anonNode +" setting nextHopAddress to " +routeInfo.route[routeInfo.pos] +", pos: " +routeInfo.pos); request.nextHopAddress = routeInfo.route[routeInfo.pos]; routeInfo.pos++; System.arraycopy(MixList.packIdArray(routeInfo), 0, request.getByteMessage(), 0, getRouteHeaderSize(anonNode)); } } else if (anonNode.ROUTING_MODE == RoutingMode.DYNAMIC_ROUTING) { request.nextHopAddress = dynamicRoutingPlugInMix.getNextHop(); if (RECURSIVE_HEADERS_ENABLED) extractHeader(request); } else { throw new RuntimeException("not supported routing mode: " +anonNode.ROUTING_MODE); } if (request.isFinalHop(anonNode)) { // remove padding int msgLength = Util.byteArrayToShort(Arrays.copyOfRange(request.getByteMessage(), 0, 2)); if (msgLength == 0) { request.setDummyStatus(DummyStatus.DUMMY); request.setByteMessage(new byte[0]); } else { request.setDummyStatus(DummyStatus.NO_DUMMY); request.setByteMessage(Arrays.copyOfRange(request.getByteMessage(), 2, msgLength+2)); } } outputStrategyLayerMix.addRequest(request); } } } } class ReplyThread extends Thread { @Override public void run() { while (true) { Reply[] replies = anonNode.getFromReplyInputQueue(); for (Reply reply:replies) { if (reply.isFirstReplyHop) { if (reply.getByteMessage().length > anonNode.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +anonNode.MAX_PAYLOAD +" bytes in one message"); byte[] lengthHeader = Util.shortToByteArray(reply.getByteMessage().length); if (reply.getByteMessage().length < anonNode.MAX_PAYLOAD) { // add padding int paddingLength = anonNode.MAX_PAYLOAD - reply.getByteMessage().length; byte[] padding = new byte[paddingLength]; secureRandom.nextBytes(padding); reply.setByteMessage(Util.concatArrays(reply.getByteMessage(), padding)); } reply.setByteMessage(Util.concatArrays(lengthHeader, reply.getByteMessage())); //System.out.println("mix sends reply: " +Util.toHex(reply.getByteMessage())); // TODO: remove if (RECURSIVE_HEADERS_ENABLED) { byte[] header = new HeaderBlock(reply.headers).getHeaderForClient(); reply.setByteMessage(Util.concatArrays(header, reply.getByteMessage())); } } if (RECURSIVE_HEADERS_ENABLED) { extractHeader(reply); } if (anonNode.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) { outputStrategyLayerMix.addReply(reply); } else if (anonNode.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { outputStrategyLayerMix.addReply(reply); } else if (anonNode.ROUTING_MODE == RoutingMode.DYNAMIC_ROUTING) { reply.nextHopAddress = anonNode.mixList.getRandomMixId(); outputStrategyLayerMix.addReply(reply); } else { throw new RuntimeException("not supported routing mode: " +anonNode.ROUTING_MODE); } } } } } private void extractHeader(MixMessage msg) { int beginOfHeader = 2; int beginOfMessage = Util.byteArrayToShort(Arrays.copyOf(msg.getByteMessage(), 2))+2; byte[] header = Arrays.copyOfRange(msg.getByteMessage(), beginOfHeader, beginOfMessage); byte[] message = Arrays.copyOfRange(msg.getByteMessage(), beginOfMessage, msg.getByteMessage().length); HeaderBlock h = new HeaderBlock(header); msg.headers = new byte[1][]; if (h.getHeaderForThisMix() != null) msg.headers[0] = h.getHeaderForThisMix(); if (h.getHeaderForNextMix() != null) { msg.setByteMessage(Util.concatArrays(h.getHeaderForNextMix() , message)); } else { msg.setByteMessage(message); } } protected static int getRouteHeaderSize(AnonNode anonNode) { return ((anonNode.FREE_ROUTE_LENGTH-1)*4)+2; } }