private static Evidence checkShuffleMisbehavior(
      Map<Integer, VerificationKey> players,
      Map<VerificationKey, DecryptionKey> decryptionKeys,
      Map<VerificationKey, Packet> shuffleMessages,
      Map<VerificationKey, Packet> broadcastMessages)
      throws FormatException {

    if (players == null
        || decryptionKeys == null
        || shuffleMessages == null
        || broadcastMessages == null) throw new NullPointerException();

    SortedSet<String> outputs = new TreeSet<>();

    // Go through the steps of shuffling messages.
    for (int i = 1; i < players.size(); i++) {

      Packet packet = shuffleMessages.get(players.get(i + 1));
      if (packet == null) {
        // TODO Blame a player for lying.

        return null;
      }

      Message message = packet.payload();

      // Grab the correct number of addresses and decrypt them.
      SortedSet<String> decrypted = new TreeSet<>();
      for (int j = 0; j < i; j++) {
        if (message.isEmpty()) {
          return Evidence.ShuffleMisbehaviorDropAddress(
              players.get(i), decryptionKeys, shuffleMessages, broadcastMessages);
        }

        String address = message.readString();
        message = message.rest();
        for (int k = i + 1; k <= players.size(); k++) {
          address = decryptionKeys.get(players.get(k)).decrypt(address);
        }

        // There shouldn't be duplicates.
        if (decrypted.contains(address)) {
          return Evidence.ShuffleMisbehaviorDropAddress(
              players.get(i), decryptionKeys, shuffleMessages, broadcastMessages);
        }
        decrypted.add(address);
      }

      // Does this contain all the previous addresses?
      if (!decrypted.containsAll(outputs)) {
        return Evidence.ShuffleMisbehaviorDropAddress(
            players.get(i), decryptionKeys, shuffleMessages, broadcastMessages);
      }

      decrypted.removeAll(outputs);

      // There should be one new address.
      if (decrypted.size() != 1) {
        return Evidence.ShuffleMisbehaviorDropAddress(
            players.get(i), decryptionKeys, shuffleMessages, broadcastMessages);
      }

      outputs.add(decrypted.first());
    }

    // Now check the last set of messages from player N.
    // All broadcast messages should have the same content and we should
    // have already checked for this. Therefore we just look for the first
    // one that is available.
    // (The message for player 1 should always be available, so theoretically
    // we don't need to loop through everybody, but who knows what might have happened.)
    Packet packet = null;
    for (int j = 1; j <= players.size(); j++) {
      packet = broadcastMessages.get(players.get(j));

      if (packet != null) {
        break;
      }
    }

    if (packet == null) {
      // TODO blame someone.
      return null;
    }

    Message message = packet.payload();

    // Grab the correct number of addresses and decrypt them.
    SortedSet<String> addresses = new TreeSet<>();
    for (int j = 0; j < players.size(); j++) {
      if (message.isEmpty()) {
        return Evidence.ShuffleMisbehaviorDropAddress(
            players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages);
      }

      Address address = message.readAddress();

      // There shouldn't be duplicates.
      if (addresses.contains(address.toString())) {
        return Evidence.ShuffleMisbehaviorDropAddress(
            players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages);
      }
      addresses.add(address.toString());
    }

    // Does this contain all the previous addresses?
    if (!addresses.containsAll(outputs)) {
      return Evidence.ShuffleMisbehaviorDropAddress(
          players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages);
    }

    addresses.removeAll(outputs);

    // There should be one new address.
    if (addresses.size() != 1) {
      return Evidence.ShuffleMisbehaviorDropAddress(
          players.get(players.size()), decryptionKeys, shuffleMessages, broadcastMessages);
    }

    return null;
  }