/******************************************************************************* * 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_OAEP_AES_OFB_v0_001; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import javax.crypto.Cipher; 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.MixMessage; import staticContent.framework.message.Reply; import staticContent.framework.message.Request; import staticContent.framework.routing.RoutingMode; import staticContent.framework.util.Util; public class RSA_OAEP_AES_OFB { private AnonNode owner; private RSA_OAEP_AES_OFB_Config config; private Cipher asymmetricCipher; private Cipher asymmetricCipherReply; private KeyGenerator symKeyGenerator; private KeyGenerator macKeyGenerator; private SecureRandom secureRandom; public RSA_OAEP_AES_OFB(AnonNode owner, RSA_OAEP_AES_OFB_Config config) { this.owner = owner; this.config = config; } public void initAsClient() { // create key generators and ciphers try { this.secureRandom = SecureRandom.getInstance(config.PRNG_ALGORITHM); this.asymmetricCipher = Cipher.getInstance( config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER ); this.asymmetricCipher.init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[config.publicKeysOfMixes.length-1]); //this.minMessageSize = asymmetricCipher.getBlockSize(); this.asymmetricCipherReply = Cipher.getInstance( config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER ); this.asymmetricCipherReply.init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[config.publicKeysOfMixes.length-1]); this.symKeyGenerator = KeyGenerator.getInstance( config.NAME_OF_SYM_KEY_GENERATOR, config.CRYPTO_PROVIDER ); 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(); } } public void initAsRecoder() { assert owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING; try { this.asymmetricCipher = Cipher.getInstance(config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.asymmetricCipher.init(Cipher.DECRYPT_MODE, config.keyPair.getPrivate()); this.asymmetricCipherReply = Cipher.getInstance(config.ASYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); this.asymmetricCipherReply.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 applyLayeredEncryption(Request request) { if (request.getByteMessage() == null) request.setByteMessage(new byte[0]); if (request.getByteMessage().length == 0) { // dummy System.out.println(owner +" creating dummy"); System.out.println(owner +" config.numberOfMixes: " +config.numberOfMixes); } if (request.getByteMessage().length > config.MAX_PAYLOAD) throw new RuntimeException("can't send more than " +config.MAX_PAYLOAD +" bytes in one message"); // add padding int paddingLength = config.MAX_PAYLOAD - request.getByteMessage().length; byte[] lengthHeader = Util.intToByteArray(request.getByteMessage().length); request.setByteMessage(Util.concatArrays(lengthHeader, request.getByteMessage())); if (paddingLength > 0) { byte[] padding = new byte[paddingLength]; secureRandom.nextBytes(padding); request.setByteMessage(Util.concatArrays(request.getByteMessage(), padding)); } if (owner.IS_DUPLEX) request = addSingleUseReplyBlock(request); //String s = ""; // add header and encryption layer for each mix for (int i=config.routeLength-1; i>=0; i--) { try { // generate header (without mac; must be added later) byte[] mac; SecretKey macKey; SecretKey symKey; IvParameterSpec initVector; macKey = macKeyGenerator.generateKey(); symKey = symKeyGenerator.generateKey(); byte[] iv = new byte[symKey.getEncoded().length]; secureRandom.nextBytes(iv); initVector = new IvParameterSpec(iv); // concat header and payload byte[] plaintext = Util.concatArrays(new byte[][] { macKey.getEncoded(), symKey.getEncoded(), initVector.getIV(), request.getByteMessage() }); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // add route-info if (i == config.routeLength-1) { // last mix plaintext = Util.concatArrays(Util.intToByteArray(request.route[i]), plaintext); } else { plaintext = Util.concatArrays(Util.intToByteArray(request.route[i+1]), plaintext); } } // add mac to header Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKey); mac = macGenerator.doFinal(plaintext); plaintext = Util.concatArrays(mac, plaintext); assert macKey.getEncoded().length == config.MAC_KEY_LENGTH; assert symKey.getEncoded().length == config.SYM_KEY_LENGTH; assert initVector.getIV().length == config.IV_LENGTH; assert mac.length == config.MAC_LENGTH; // encrypt message; asymmetric part byte[] cipherText; if (owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) this.asymmetricCipher.init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[i]); else { this.asymmetricCipher.init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[request.route[i]]); // TODO: possible side-effect -> won't work anymore if mix-ids are chosen differently // System.out.println("" +owner +" using public key " +Util.md5(config.publicKeysOfMixes[request.route[i]].getEncoded()) +"for mix " +request.route[i]); } byte[] asymCipherText = asymmetricCipher.doFinal(plaintext, 0, asymmetricCipher.getBlockSize()); // encrypt message; symmetric part Cipher symCipher = Cipher.getInstance( config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER ); symCipher.init(Cipher.ENCRYPT_MODE, symKey, initVector); byte[] symCipherText = symCipher.doFinal(plaintext, asymmetricCipher.getBlockSize(), plaintext.length - asymmetricCipher.getBlockSize()); cipherText = Util.concatArrays(asymCipherText, symCipherText); /*if (config.DEBUG_ON) { 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) +")" //+"\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)); }*/ request.setByteMessage(cipherText); } catch (Exception e) { e.printStackTrace(); } } return request; } public int getMaxPayloadForNextMessage() { return config.MAX_PAYLOAD; } public int getMaxPayloadForNextReply() { return config.MAX_PAYLOAD; } public synchronized Reply extractPayload(Reply reply) { byte[] pseudonym = Arrays.copyOfRange(reply.getByteMessage(), 0, config.PSEUDONYM_LENGTH); byte[] payload = Arrays.copyOfRange(reply.getByteMessage(), config.PSEUDONYM_LENGTH, reply.getByteMessage().length ); byte[] seed; SecureRandom prng = null; SecretKey symKey; IvParameterSpec initVector; Cipher decryptCipher = null; try { seed = config.replySeeds.get(new String(pseudonym,"UTF-8")); prng = SecureRandom.getInstance(config.PRNG_ALGORITHM); prng.setSeed(seed); decryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); byte[][] symKeys = new byte[config.numberOfMixes][config.SYM_KEY_LENGTH]; byte[][] IVs= new byte[config.numberOfMixes][config.IV_LENGTH]; for(int i=0; i<config.numberOfMixes;i++) { prng.nextBytes(symKeys[i]); prng.nextBytes(IVs[i]); } for (int i=config.numberOfMixes-1; i>=0; i--) { symKey = new SecretKeySpec(symKeys[i], config.SYM_CRYPTOGRAPHY_ALGORITHM); initVector = new IvParameterSpec(IVs[i]); decryptCipher.init(Cipher.DECRYPT_MODE, symKey, initVector); payload = decryptCipher.doFinal(payload, 0, payload.length); } } catch (Exception e) { e.printStackTrace(); } // remove Padding byte[] lengthHeader = Arrays.copyOfRange(payload, 0, config.LENGTH_HEADER_LENGTH); int payloadLength = Util.byteArrayToInt(lengthHeader); if (payloadLength == 0) // dummy payload = new byte[0]; else payload = Arrays.copyOfRange(payload, config.LENGTH_HEADER_LENGTH, config.LENGTH_HEADER_LENGTH + payloadLength); reply.setByteMessage(payload); return reply; } private Request addSingleUseReplyBlock(Request request) { /*if (config.DEBUG_ON) System.out.println("Client " +client.getIdentifier() +": generating reply block"); */ byte[] payload; byte[] seed; byte[] pseudonym = null; SecureRandom prng = null; // use (seeded) prng to create keys try { prng = SecureRandom.getInstance(config.PRNG_ALGORITHM); seed = new byte[config.PRNG_SEED_LENGTH]; secureRandom.nextBytes(seed); prng.setSeed(seed); assert prng != null; pseudonym = new byte[config.PSEUDONYM_LENGTH]; // used to identify the correct seed when a reply is received secureRandom.nextBytes(pseudonym); config.replySeeds.put(new String(pseudonym, "UTF-8"), seed); } catch (Exception e) { e.printStackTrace(); } byte[] padding = new byte[asymmetricCipher.getBlockSize() - (config.MAC_KEY_LENGTH + config.SYM_KEY_LENGTH + config.IV_LENGTH + config.MAC_LENGTH +config.PSEUDONYM_LENGTH)]; secureRandom.nextBytes(padding); payload = Util.concatArrays(new byte[][] {pseudonym,padding}); for (int i=0; i<=config.routeLength-1; i++) { try { // generate header (without mac; must be added later) byte[] mac; SecretKey macKey; SecretKey symKey; IvParameterSpec initVector; byte[] macKeyAsByteArray = new byte[config.MAC_KEY_LENGTH]; secureRandom.nextBytes(macKeyAsByteArray); macKey = new SecretKeySpec(macKeyAsByteArray, config.MAC_ALGORITHM); byte[] symKeyAsByteArray = new byte[config.SYM_KEY_LENGTH]; prng.nextBytes(symKeyAsByteArray); symKey = new SecretKeySpec(symKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] iv = new byte[symKey.getEncoded().length]; prng.nextBytes(iv); initVector = new IvParameterSpec(iv); // concat header fields and payload byte[] plaintext = Util.concatArrays(new byte[][] { macKeyAsByteArray, symKeyAsByteArray, iv, payload }); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // add route-info if (i == 0) { // last mix plaintext = Util.concatArrays(Util.intToByteArray(request.route[i]), plaintext); } else { plaintext = Util.concatArrays(Util.intToByteArray(request.route[i-1]), plaintext); } } // add mac to header Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKey); mac = macGenerator.doFinal(plaintext); plaintext = Util.concatArrays(mac, plaintext); assert macKey.getEncoded().length == config.MAC_KEY_LENGTH; assert symKey.getEncoded().length == config.SYM_KEY_LENGTH; assert initVector.getIV().length == config.IV_LENGTH; assert mac.length == config.MAC_LENGTH; // encrypt message; asymmetric part byte[] cipherText; if (owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) this.asymmetricCipher.init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[i]); else { this.asymmetricCipher.init(Cipher.ENCRYPT_MODE, config.publicKeysOfMixes[request.route[i]]); // TODO: possible side-effect -> won't work anymore if mix-ids are chosen differently // System.out.println("" +owner +" using public key " +Util.md5(config.publicKeysOfMixes[request.route[i]].getEncoded()) +"for mix " +request.route[i]); } byte[] asymCipherText = asymmetricCipher.doFinal(plaintext, 0, asymmetricCipher.getBlockSize()); // encrypt message; symmetric part Cipher symCipher = Cipher.getInstance( config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER ); symCipher.init(Cipher.ENCRYPT_MODE, symKey, initVector); byte[] symCipherText = symCipher.doFinal(plaintext, asymmetricCipher.getBlockSize(), plaintext.length - asymmetricCipher.getBlockSize()); cipherText = Util.concatArrays(asymCipherText, symCipherText); if (config.DEBUG_ON) { // System.out.println("Client " +client.getIdentifier() +": message for mix "+i +":"); System.out.println("\tlength of mac: " +mac.length); System.out.println("\tlength of macKey: " +macKey.getEncoded().length); System.out.println("\tlength of symKey: " +symKey.getEncoded().length); System.out.println("\tlength of iv: " +initVector.getIV().length); System.out.println("\tlength of payload: " +payload.length); System.out.println("\tlength of asym ciphertext: " +asymCipherText.length); System.out.println("\tlength of sym ciphertext: " +symCipherText.length); System.out.println("\ttotal length of ciphertext: " +cipherText.length); System.out.println("\thash of plaintext: " +Util.md5(Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length))); System.out.println("\thash of ciphertext: " +Util.md5(cipherText)); //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(); } } byte[] lengthHeader = Util.intToByteArray(payload.length); // if (config.DEBUG_ON) // System.out.println("Client " +owner.getIdentifier() +": finished generating reply block"); request.setByteMessage( Util.concatArrays(new byte[][] { lengthHeader, payload , // == replyBlock request.getByteMessage()//message })); return request; } public synchronized Request recodeMessage(Request message) { synchronized (asymmetricCipher) { boolean isLastMix =false; if(owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING) isLastMix = owner.IS_LAST_MIX; String cipherTextHash = null; try { if (config.DEBUG_ON) { cipherTextHash = Util.md5(message.getByteMessage()); // System.out.println("Mix " +owner.getIdentifier() +": received this message (ciphertext): " +Util.md5(message.getByteMessage())); //System.out.println("my public key: " +Util.md5(keyPair.getPublic().getEncoded())); } // decrypt asymmetrically encrypted part byte[] asymPlaintext = asymmetricCipher.doFinal(message.getByteMessage(), 0, asymmetricCipher.getBlockSize()); // extract data from derypted header (= first part of the "asymPlaintext") byte[] mac; SecretKey macKey; SecretKey symKey; IvParameterSpec initVector; int pointer = 0; mac = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_LENGTH); if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // determine next hop if source routing is enabled byte[] nextHopAddress = Arrays.copyOfRange(asymPlaintext, pointer, pointer += 4); int address = Util.byteArrayToInt(nextHopAddress); if (address == owner.PUBLIC_PSEUDONYM) { // this mix is the last hop if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to \"LAST HOP\""); message.nextHopAddress = MixMessage.NONE; isLastMix= true; } else { // this mix is not the last hop if (owner.DISPLAY_ROUTE_INFO) System.out.println(""+owner +" setting nextHopAddress to " +address); message.nextHopAddress = address; } } byte[] macKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_KEY_LENGTH); macKey = new SecretKeySpec(macKeyAsByteArray, config.MAC_ALGORITHM); byte[] symKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.SYM_KEY_LENGTH); symKey = new SecretKeySpec(symKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.IV_LENGTH); initVector = new IvParameterSpec(ivAsByteArray); // decrypt symmetrically encrypted part Cipher decryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); decryptCipher.init(Cipher.DECRYPT_MODE, symKey, initVector); byte[] symPlaintext = decryptCipher.doFinal(message.getByteMessage(), asymmetricCipher.getBlockSize(), message.getByteMessage().length - asymmetricCipher.getBlockSize()); byte[] plaintext = Util.concatArrays(asymPlaintext, symPlaintext); // validate mac Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKey); byte[] signedData = Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length); if (config.DEBUG_ON) System.out.println(owner +" " +cipherTextHash +" -> " +Util.md5(signedData)); byte[] locallyGeneratedMac = macGenerator.doFinal(signedData); if (!Arrays.equals(locallyGeneratedMac, mac)) { System.out.println("wrong MAC!"); // TODO return null; } if (config.PERFORM_REPLY_DETECTION) if (config.replayDetection.isReplay(macKeyAsByteArray)) return null; if (isLastMix ) { if(!owner.IS_DUPLEX) { // remove Padding byte[] lengthHeader = Arrays.copyOfRange(plaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); int payloadLength = Util.byteArrayToInt(lengthHeader); if (payloadLength == 0) // dummy plaintext = new byte[0]; else plaintext = Arrays.copyOfRange(plaintext, pointer, pointer + payloadLength); message.setByteMessage(plaintext); } if (owner.IS_DUPLEX) { // RBlength|replyblock|PTlength|plaintext|padding byte[] RBlengthHeader = Arrays.copyOfRange(plaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); int replyblockLength = Util.byteArrayToInt(RBlengthHeader); byte[] replyblock = Arrays.copyOfRange(plaintext, pointer, pointer += replyblockLength); ArrayList<byte[]> rpList; if(config.replyblocks.containsKey(message.getOwner())) { rpList = config.replyblocks.get(message.getOwner()); } else { rpList = new ArrayList<byte[]>(); } rpList.add(replyblock); config.replyblocks.put(message.getOwner(), rpList); byte[] lengthHeader = Arrays.copyOfRange(plaintext, pointer, pointer += config.LENGTH_HEADER_LENGTH); int payloadLength = Util.byteArrayToInt(lengthHeader); if (payloadLength == 0) // dummy plaintext = new byte[0]; else plaintext = Arrays.copyOfRange(plaintext, pointer, pointer + payloadLength); message.setByteMessage(plaintext); return message; } else return message; } else { message.setByteMessage(Arrays.copyOfRange(plaintext, pointer, plaintext.length)); return message; } } catch (Exception e) { e.printStackTrace(); System.err.println(owner +" Exception-message (ciphertext): " +Util.md5(message.getByteMessage())); return null; } } } public Reply recodeReply(Reply message) { synchronized(asymmetricCipherReply) { byte [] replyblock; byte[] payload; if(message.getByteMessage().length<=config.MAX_PAYLOAD) { int paddingLength = config.MAX_PAYLOAD - message.getByteMessage().length; byte[] lengthHeader = Util.intToByteArray(message.getByteMessage().length); message.setByteMessage(Util.concatArrays(lengthHeader, message.getByteMessage())); if (paddingLength > 0) { byte[] padding = new byte[paddingLength]; new SecureRandom().nextBytes(padding); message.setByteMessage(Util.concatArrays(message.getByteMessage(),padding)); } replyblock = config.replyblocks.get(message.getOwner()).remove(0); payload = message.getByteMessage(); } else { replyblock = Arrays.copyOfRange(message.getByteMessage(), 0, message.getByteMessage().length - (config.MAX_PAYLOAD+config.LENGTH_HEADER_LENGTH)); payload = Arrays.copyOfRange(message.getByteMessage(), replyblock.length, message.getByteMessage().length); } try { // decrypt asymmetrically encrypted part byte[] asymPlaintext = asymmetricCipherReply.doFinal(replyblock, 0, asymmetricCipherReply.getBlockSize()); // extract data from derypted header (= first part of the "asymPlaintext") byte[] mac; SecretKey macKey; SecretKey symKey; IvParameterSpec initVector; int pointer = 0; mac = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_LENGTH); // // if (owner.ROUTING_MODE == RoutingMode.SOURCE_ROUTING) { // determine next hop if source routing is enabled // byte[] nextHopAddress = Arrays.copyOfRange(asymPlaintext, pointer, pointer += 4); // int address = Util.byteArrayToInt(nextHopAddress); // if (address == owner.PUBLIC_PSEUDONYM) { // this mix is the last hop // if (owner.DISPLAY_ROUTE_INFO) // System.out.println(""+owner +" setting nextHopAddress to \"LAST HOP\""); // message.nextHopAddress = MixMessage.NONE; // isLastMix = true; // } else { // this mix is not the last hop // if (owner.DISPLAY_ROUTE_INFO) // System.out.println(""+owner +" setting nextHopAddress to " +address); // message.nextHopAddress = address; // } // } byte[] macKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.MAC_KEY_LENGTH); macKey = new SecretKeySpec(macKeyAsByteArray, config.MAC_ALGORITHM); byte[] symKeyAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.SYM_KEY_LENGTH); symKey = new SecretKeySpec(symKeyAsByteArray, config.SYM_CRYPTOGRAPHY_ALGORITHM); byte[] ivAsByteArray = Arrays.copyOfRange(asymPlaintext, pointer, pointer += config.IV_LENGTH); initVector = new IvParameterSpec(ivAsByteArray); // decrypt symmetrically encrypted part Cipher decryptCipher = Cipher.getInstance(config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER); decryptCipher.init(Cipher.DECRYPT_MODE, symKey, initVector); byte[] symPlaintext = decryptCipher.doFinal(replyblock, asymmetricCipherReply.getBlockSize(), replyblock.length - asymmetricCipherReply.getBlockSize()); byte[] plaintext = Util.concatArrays(asymPlaintext, symPlaintext); // validate mac Mac macGenerator = Mac.getInstance(config.MAC_ALGORITHM); macGenerator.init(macKey); byte[] signedData = Arrays.copyOfRange(plaintext, config.MAC_LENGTH, plaintext.length); if (config.DEBUG_ON) System.out.println(owner +" plaintext: " +Util.md5(signedData) +" (of " +Util.md5(message.getByteMessage()) +")"); byte[] locallyGeneratedMac = macGenerator.doFinal(signedData); if (!Arrays.equals(locallyGeneratedMac, mac)) { System.out.println("wrong MAC!"); // TODO return null; } if (config.PERFORM_REPLY_DETECTION) if (config.replayDetection.isReplay(macKeyAsByteArray)) return null; replyblock = Arrays.copyOfRange(plaintext, pointer, plaintext.length); // encrypt payload with seeded keys Cipher symCipher = Cipher.getInstance( config.SYM_CRYPTOGRAPHY_ALGORITHM, config.CRYPTO_PROVIDER ); symCipher.init(Cipher.ENCRYPT_MODE, symKey, initVector); byte[] payloadCipherText = symCipher.doFinal(payload, 0, payload.length); byte[] cipherText = Util.concatArrays(replyblock, payloadCipherText); if (owner.IS_FIRST_MIX) { // remove Padding byte[] pseudonym = Arrays.copyOfRange(replyblock, 0, config.PSEUDONYM_LENGTH); cipherText = Util.concatArrays(pseudonym, payloadCipherText); } message.setByteMessage(cipherText); return message; } catch (Exception e) { e.printStackTrace(); return null; } } } }