/******************************************************************************* * 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.RSA_AES_LossTolerantChannel_v0_001; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import staticContent.framework.AnonNode; import staticContent.framework.message.Reply; import staticContent.framework.message.Request; import staticContent.framework.routing.RoutingMode; import staticContent.framework.util.Util; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer2recodingScheme.RSA_AES_LossTolerantChannel_v0_001.MixPlugIn.ChannelData; public class RSA_AES_LossTolerantChannel { private AnonNode owner; private RSA_AES_LossTolerantChannel_Config config; private Cipher asymmetricCipher; private Cipher asymmetricCiphers[]; private KeyGenerator symKeyGenerator; private KeyGenerator macKeyGenerator; private SecretKey[] macKeys; private SecretKey[] sessionKeysForRequestChannel; private SecretKey[] sessionKeysForReplyChannel; private IvParameterSpec[] sessionIVsForRequestChannel; private IvParameterSpec[] sessionIVsForReplyChannel; private Cipher[] symmetricEncryptCiphers; private Cipher[] symmetricDecryptCiphers; private SecureRandom secureRandom; private boolean channelEstablished = false; public RSA_AES_LossTolerantChannel(AnonNode owner, RSA_AES_LossTolerantChannel_Config config) { this.owner = owner; this.config = config; } public void initAsClient() { if (owner.ROUTING_MODE != RoutingMode.GLOBAL_ROUTING) throw new RuntimeException("not supported"); // TODO: support it... this.macKeys = new SecretKey[config.numberOfMixes]; this.sessionKeysForRequestChannel = new SecretKey[config.numberOfMixes]; this.sessionKeysForReplyChannel = new SecretKey[config.numberOfMixes]; this.sessionIVsForRequestChannel = new IvParameterSpec[config.numberOfMixes]; this.sessionIVsForReplyChannel = new IvParameterSpec[config.numberOfMixes]; this.symmetricEncryptCiphers = new Cipher[config.numberOfMixes]; this.symmetricDecryptCiphers = new Cipher[config.numberOfMixes]; this.asymmetricCiphers = new Cipher[config.numberOfMixes]; // create key generators and ciphers try { this.secureRandom = SecureRandom.getInstance(config.PRNG_ALGORITHM); this.symKeyGenerator = KeyGenerator.getInstance(config.NAME_OF_SYM_KEY_GENERATOR); this.symKeyGenerator.init(config.SYM_KEY_LENGTH * 8); this.macKeyGenerator = KeyGenerator.getInstance(config.MAC_ALGORITHM); this.macKeyGenerator.init(config.MAC_KEY_LENGTH * 8); } catch (Exception e) { e.printStackTrace(); } } // TODO: aufrufen! public void initAsRecoder() { try { this.secureRandom = SecureRandom.getInstance(config.PRNG_ALGORITHM); this.asymmetricCipher = Cipher.getInstance(config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.asymmetricCipher.init(Cipher.DECRYPT_MODE, config.keyPair.getPrivate()); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("could not init asym cipher at mix"); } } public synchronized Request recodeMessage(Request message, ChannelData channelData) { if (channelData.established) { // weak check if channel already established return recodeChannelMessage(message, channelData); } else { synchronized (config) { // secure check if (!channelData.established) { return recodeChannelEstablishMessage(message, channelData); } else { return recodeChannelMessage(message, channelData); } } } } private Request recodeChannelEstablishMessage(Request message, ChannelData channelData) { String cipherTextHash = null; try { if (config.DEBUG_ON) { cipherTextHash = Util.md5(message.getByteMessage()); } // decrypt asymmetrically encrypted part byte[] asymCipherText = Arrays.copyOfRange(message.getByteMessage(), 0 ,asymmetricCipher.getBlockSize()); byte[] asymPlaintext = asymmetricCipher.doFinal(asymCipherText); // extract data from derypted header (= first part of the "asymPlaintext") byte[] mac; //SecretKey symKey; IvParameterSpec initVector; int payloadLength; int pointer = 0; mac = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_LENGTH); byte[] macKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_KEY_LENGTH); channelData.macKey = new SecretKeySpec(macKeyAsByteArray, config.MAC_ALGORITHM); byte[] symKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.SYM_KEY_LENGTH); channelData.symSessionKey = new SecretKeySpec(symKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.IV_LENGTH); initVector = new IvParameterSpec(ivAsByteArray); channelData.decryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); channelData.decryptCipher.init(Cipher.DECRYPT_MODE, channelData.symSessionKey, initVector); if (owner.IS_DUPLEX) { byte[] symRepKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.SYM_KEY_LENGTH); SecretKey symRepKey = new SecretKeySpec(symRepKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); ivAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.IV_LENGTH); initVector = new IvParameterSpec(ivAsByteArray); channelData.encryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); channelData.encryptCipher.init(Cipher.ENCRYPT_MODE, symRepKey, initVector); } byte[] payloadLengthAsArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); payloadLength = Util.byteArrayToInt(payloadLengthAsArray); // decrypt symmetrically encrypted part byte[] symCipherText = Arrays.copyOfRange(message.getByteMessage(), asymmetricCipher.getBlockSize(), message.getByteMessage().length); byte[] symPlaintext = channelData.decryptCipher.update(symCipherText); assert symCipherText.length == symPlaintext.length: owner.toString() +": symCipherText.length != symPlaintext.length (" +symCipherText.length +"!=" +symPlaintext.length +")"; symPlaintext = Arrays.copyOfRange(symPlaintext, ivAsByteArray.length, symPlaintext.length); // explicit IV mode; first block must be removed (cf. S. Vaudenay: "Security Flaws Induced by CBC Padding-Applications to SSL, IPSEC, WTLS") byte[] plaintext = Util.concatArrays(asymPlaintext, symPlaintext); //System.out.println("-[-[[-mix: asymPlaintext (" +Util.md5(asymPlaintext) +") + symPlaintext ("+Util.md5(symPlaintext) +") -> (" +Util.md5(asymCipherText) +"), (" +Util.md5(symCipherText) +") using " +Util.md5(symKeyAsByteArray) +", " +Util.md5(channelData.decryptCipher.getIV())); // validate mac Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(channelData.macKey); byte[] signedData = Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length); if (config.DEBUG_ON) System.out.println(owner +" " +cipherTextHash +" -> " +Util.md5(signedData) +" key: " +Util.md5(channelData.macKey.getEncoded())); byte[] locallyGeneratedMac = macGenerator.doFinal(signedData); System.out.println("mix: mac-data: " +Util.md5(signedData) +", mac: " +Util.md5(locallyGeneratedMac) +", received mac: " +Util.md5(mac)); // TODO if (!Arrays.equals(locallyGeneratedMac, mac)) { System.out.println("wrong MAC!"); //System.out.println("%&/%&/ wrong MAC: " +Util.md5(mac) + ", signedData: " +Util.md5(signedData)); // TODO return null; } if (config.PERFORM_REPLY_DETECTION) if (config.replayDetection.isReplay(macKeyAsByteArray)) return null; // extract payload (= receive data without headers and padding) if (payloadLength == 0) {// dummy message.setByteMessage(new byte[0]); } else { message.setByteMessage(Arrays.copyOfRange(plaintext, pointer, (pointer + payloadLength))); } channelData.established = true; return message; } catch (Exception e) { e.printStackTrace(); System.err.println(owner +" Exception-message (ciphertext): " +Util.md5(message.getByteMessage())); return null; } } private Request recodeChannelMessage(Request message, ChannelData channelData) { try { String ct = Util.md5(message.getByteMessage()); byte[] ivAsArray = Arrays.copyOf(message.getByteMessage(), config.IV_LENGTH); IvParameterSpec iv = new IvParameterSpec(ivAsArray); byte[] cipherText = Arrays.copyOfRange(message.getByteMessage(), config.IV_LENGTH, message.getByteMessage().length); byte[] plaintext; synchronized (channelData.decryptCipher) { channelData.decryptCipher.init(Cipher.DECRYPT_MODE, channelData.symSessionKey, iv); plaintext = channelData.decryptCipher.doFinal(cipherText); } assert cipherText.length == plaintext.length; plaintext = Arrays.copyOfRange(plaintext, ivAsArray.length, plaintext.length); // explicit IV mode; first block must be removed (cf. S. Vaudenay: "Security Flaws Induced by CBC Padding-Applications to SSL, IPSEC, WTLS") // validate mac int pointer = 0; byte[] mac = Arrays.copyOfRange(plaintext, pointer, pointer += config.MAC_LENGTH); Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(channelData.macKey); byte[] signedData = Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length); byte[] locallyGeneratedMac = macGenerator.doFinal(signedData); //System.out.println("mix: mac-data: " +Util.md5(signedData) +", mac: " +Util.md5(locallyGeneratedMac) +", received mac: " +Util.md5(mac) +"\n" +new String(signedData) +"\n\n\n"); // TODO //System.out.println("mix: plaintext: " +Util.md5(plaintext) +", ciphertext: " +Util.md5(cipherText) +", symiv: " +Util.md5(ivAsArray) +", symKey: " +Util.md5(channelData.symSessionKey.getEncoded()) +", macKey: " +Util.md5(channelData.macKey.getEncoded()) +", mac-data: " +Util.md5(signedData) +", mac (generated): " +Util.md5(locallyGeneratedMac) +", received mac: " +Util.md5(mac)); if (!Arrays.equals(locallyGeneratedMac, mac)) { System.err.println(owner.toString() +" wrong MAC cm! " +ct +" mac of " +Util.md5(signedData) +" is " +Util.md5(mac) +" key: " +Util.md5(channelData.macKey.getEncoded())); // TODO return null; } // extract payload (= receive data without headers and padding) byte[] payloadLengthAsArray = Arrays.copyOfRange(plaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); int payloadLength = Util.byteArrayToInt(payloadLengthAsArray); if (payloadLength == 0) { // dummy message.setByteMessage(new byte[0]); } else { message.setByteMessage(Arrays.copyOfRange(plaintext, pointer, pointer += payloadLength)); } return message; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { e.printStackTrace(); return null; } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); return null; } catch (IllegalBlockSizeException e) { e.printStackTrace(); return null; } catch (BadPaddingException e) { e.printStackTrace(); return null; } } public synchronized Reply recodeReply(Reply message, ChannelData channelData) { if (owner.IS_LAST_MIX) { if (message.getByteMessage() == null) // dummy message.setByteMessage(new byte[0]); if (message.getByteMessage().length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); // add length header byte[] lengthHeader = Util.intToByteArray(message.getByteMessage().length); message.setByteMessage(Util.concatArrays(lengthHeader, message.getByteMessage())); // add padding int desiredLength = config.MAX_PAYLOAD + config.LENGTH_HEADER_LENGTH; int diffToBlockSize = desiredLength % channelData.encryptCipher.getBlockSize(); if (diffToBlockSize != 0) desiredLength += channelData.encryptCipher.getBlockSize() - diffToBlockSize; byte[] padding = new byte[desiredLength - message.getByteMessage().length]; secureRandom.nextBytes(padding); message.setByteMessage(Util.concatArrays(message.getByteMessage(), padding)); assert (message.getByteMessage().length % channelData.encryptCipher.getBlockSize()) == 0; } byte[] result = channelData.encryptCipher.update(message.getByteMessage()); //System.out.println(" oOoOo " +mix.toString() +": " +Util.md5(message.getByteMessage()) + " -> " +Util.md5(result)); assert result.length == message.getByteMessage().length; message.setByteMessage(result); return message; } public synchronized Request applyLayeredEncryption(Request request) { if (!channelEstablished) { channelEstablished = true; return createChannelEstablishMessage(request); } else { return createChannelMessage(request); } } private Request createChannelEstablishMessage(Request request) { byte[] payload = request.getByteMessage(); if (payload == null) // dummy payload = new byte[0]; if (payload.length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); // generate keys, init ciphers etc. for (int i=config.numberOfMixes-1; i>=0; i--) { try { this.asymmetricCiphers[i] = Cipher.getInstance(config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.asymmetricCiphers[i].init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[i]); this.sessionKeysForRequestChannel[i] = symKeyGenerator.generateKey(); this.macKeys[i] = macKeyGenerator.generateKey(); byte[] ivReq = new byte[sessionKeysForRequestChannel[i].getEncoded().length]; secureRandom.nextBytes(ivReq); this.sessionIVsForRequestChannel[i] = new IvParameterSpec(ivReq); this.symmetricEncryptCiphers[i] = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.symmetricEncryptCiphers[i].init(Cipher.ENCRYPT_MODE, sessionKeysForRequestChannel[i], sessionIVsForRequestChannel[i]); if (owner.IS_DUPLEX) { this.sessionKeysForReplyChannel[i] = symKeyGenerator.generateKey(); byte[] ivRep = new byte[sessionKeysForReplyChannel[i].getEncoded().length]; secureRandom.nextBytes(ivRep); this.sessionIVsForReplyChannel[i] = new IvParameterSpec(ivRep); this.symmetricDecryptCiphers[i] = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.symmetricDecryptCiphers[i].init(Cipher.DECRYPT_MODE, sessionKeysForReplyChannel[i], sessionIVsForReplyChannel[i]); } } catch (Exception e) { e.printStackTrace(); } } String s = ""; // add header and encryption layer for each mix for (int i=config.numberOfMixes-1; i>=0; i--) { try { // generate header (without mac; must be added later) byte[] mac; byte[] plaintext; byte[] payloadLength = Util.intToByteArray(payload.length); // concat header and payload if (owner.IS_DUPLEX) { plaintext = Util.concatArrays(new byte[][] { macKeys[i].getEncoded(), sessionKeysForRequestChannel[i].getEncoded(), sessionIVsForRequestChannel[i].getIV(), sessionKeysForReplyChannel[i].getEncoded(), sessionIVsForReplyChannel[i].getIV(), payloadLength, payload }); } else { plaintext = Util.concatArrays(new byte[][] { macKeys[i].getEncoded(), sessionKeysForRequestChannel[i].getEncoded(), sessionIVsForRequestChannel[i].getIV(), payloadLength, payload }); } // add padding for last mix' payload if (i == config.numberOfMixes-1 && payload.length != config.MAX_PAYLOAD) { byte[] padding = new byte[config.MAX_PAYLOAD - payload.length]; secureRandom.nextBytes(padding); plaintext = Util.concatArrays(plaintext, padding); } // make sure message lengths are a multiple of the sym crypto algorithm's block size int symPlaintextLength = (plaintext.length + config.MAC_LENGTH) - asymmetricCiphers[i].getBlockSize(); // note that "getBlockSize()" returns the max length of a plaintext encryptable with the asym. cipher (= aysm. keylength - overhead) int diffToBlockSize = symPlaintextLength % symmetricEncryptCiphers[i].getBlockSize(); if (diffToBlockSize != 0) { byte[] padding = new byte[symmetricEncryptCiphers[i].getBlockSize() - diffToBlockSize]; secureRandom.nextBytes(padding); plaintext = Util.concatArrays(plaintext, padding); symPlaintextLength += padding.length; } // add mac to header Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKeys[i]); mac = macGenerator.doFinal(plaintext); System.out.println("client: mac-data: " +Util.md5(plaintext) +", mac: " +Util.md5(mac)); // TODO plaintext = Util.concatArrays(mac, plaintext); assert macKeys[i].getEncoded().length == config.MAC_KEY_LENGTH; assert sessionKeysForRequestChannel[i].getEncoded().length == config.SYM_KEY_LENGTH; assert sessionIVsForRequestChannel[i].getIV().length == config.IV_LENGTH; assert mac.length == config.MAC_LENGTH; if (owner.IS_DUPLEX) { assert sessionKeysForReplyChannel[i].getEncoded().length == config.SYM_KEY_LENGTH; assert sessionIVsForReplyChannel[i].getIV().length == config.IV_LENGTH; } // encrypt message; asymmetric part byte[] cipherText; byte[] asymPt = Arrays.copyOfRange(plaintext, 0, asymmetricCiphers[i].getBlockSize()); byte[] asymCipherText = asymmetricCiphers[i].doFinal(asymPt); // encrypt message; symmetric part byte[] symPt = Arrays.copyOfRange(plaintext, asymmetricCiphers[i].getBlockSize(), plaintext.length); assert symPt.length == symPlaintextLength; assert symPt.length % symmetricEncryptCiphers[i].getBlockSize() == 0; // explicit IV mode; first block must be random (cf. S. Vaudenay: "Security Flaws Induced by CBC Padding-Applications to SSL, IPSEC, WTLS") byte[] rand = new byte[config.IV_LENGTH]; secureRandom.nextBytes(rand); symPt = Util.concatArrays(rand, symPt); byte[] symCipherText = symmetricEncryptCiphers[i].doFinal(symPt); assert symCipherText.length == symPt.length; cipherText = Util.concatArrays(asymCipherText, symCipherText); assert cipherText.length % symmetricEncryptCiphers[i].getBlockSize() == 0; if (config.DEBUG_ON) { //System.out.println("-[-[[: asymPlaintext (" +Util.md5(asymPt) +") + symPlaintext ("+Util.md5(symPt) +") -> (" +Util.md5(asymCipherText) +"), (" +Util.md5(symCipherText) +") using " +Util.md5(sessionKeysForRequestChannel[i].getEncoded()) +", " +Util.md5(symmetricEncryptCiphers[i].getIV())); s += "[msg mix "+i +":" //+"\n\tlength of mac: " +mac.length //+"\n\tlength of macKey: " +macKey.getEncoded().length //+"\n\tlength of symKey: " +symKey.getEncoded().length //+"\n\tlength of iv: " +initVector.getIV().length //+"\n\tlength of payload: " +payload.length //+"\n\tlength of asym ciphertext: " +asymCipherText.length //+"\n\tlength of sym ciphertext: " +symCipherText.length //+"\n\ttotal length of ciphertext: " +cipherText.length +"(pt " +Util.md5(Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length)) +")" +", (ct: " +Util.md5(cipherText) +") key: " +Util.md5(macKeys[i].getEncoded()) //+"\n\thash of public key: " +Util.md5(publicKeysOfMixes[i].getEncoded()) +"] " ; //System.out.println(Util.display(Arrays.copyOfRange(plaintext, MAC_LENGTH, plaintext.length))); //System.out.println(Util.display(cipherText)); } payload = cipherText; } catch (Exception e) { e.printStackTrace(); } } System.out.println(s); s = owner.toString() +": "; request.setByteMessage(payload); return request; } private Request createChannelMessage(Request request) { byte[] payload = request.getByteMessage(); if (payload == null) // dummy payload = new byte[0]; if (payload.length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); for (int i=config.numberOfMixes-1; i>=0; i--) { // add length-header try { byte[] payloadLength = Util.intToByteArray(payload.length); payload = Util.concatArrays(payloadLength, payload); // add padding for last mix' payload if (i == config.numberOfMixes-1 && payload.length != config.MAX_PAYLOAD) { byte[] padding = new byte[(config.MAX_PAYLOAD + config.MAC_LENGTH) - payload.length]; secureRandom.nextBytes(padding); payload = Util.concatArrays(payload, padding); } // make sure message lengths are a multiple of the sym crypto algorithm's block size int symPlaintextLength = payload.length + config.MAC_LENGTH; int diffToBlockSize = symPlaintextLength % symmetricEncryptCiphers[i].getBlockSize(); if (diffToBlockSize != 0) { byte[] padding = new byte[symmetricEncryptCiphers[i].getBlockSize() - diffToBlockSize]; secureRandom.nextBytes(padding); payload = Util.concatArrays(payload, padding); symPlaintextLength += padding.length; } // add mac to header Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKeys[i]); byte[] mac = macGenerator.doFinal(payload); payload = Util.concatArrays(mac, payload); // explicit IV mode; first block must be random (cf. S. Vaudenay: "Security Flaws Induced by CBC Padding-Applications to SSL, IPSEC, WTLS") byte[] rand = new byte[config.IV_LENGTH]; secureRandom.nextBytes(rand); payload = Util.concatArrays(rand, payload); // include iv with packet byte[] ivAsArray = new byte[config.IV_LENGTH]; secureRandom.nextBytes(ivAsArray); IvParameterSpec iv = new IvParameterSpec(ivAsArray); symmetricEncryptCiphers[i].init(Cipher.ENCRYPT_MODE, sessionKeysForRequestChannel[i], iv); byte[] ciphertext = symmetricEncryptCiphers[i].update(payload); assert ciphertext.length == payload.length; ciphertext = Util.concatArrays(ivAsArray, ciphertext); payload = ciphertext; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException(""); } catch (InvalidKeyException e) { e.printStackTrace(); throw new RuntimeException(""); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } } request.setByteMessage(payload); return request; } public int getMaxPayloadForNextMessage() { return config.MAX_PAYLOAD; } public int getMaxPayloadForNextReply() { return config.MAX_PAYLOAD; } public Reply extractPayload(Reply reply) { byte[] result = reply.getByteMessage(); for (int i=0; i<config.numberOfMixes; i++) { Cipher cipher = symmetricDecryptCiphers[i]; byte[] plaintext = cipher.update(result); assert plaintext.length == result.length; //System.out.println(" oOoOo " +client.toString() +": " +Util.md5(plaintext) + " <- " +Util.md5(result)); result = plaintext; } int lengthOfPayload = Util.byteArrayToInt(Arrays.copyOf(result, 4)); byte[] payload = Arrays.copyOfRange(result, config.LENGTH_HEADER_LENGTH, config.LENGTH_HEADER_LENGTH + lengthOfPayload); reply.setByteMessage(payload); return reply; } }