5
\$\begingroup\$

I made a custom encryption library/class which allows to en-/decrypt Strings with a custom charset. I think you'll get what I mean with that when you take a look at the code.

Since I haven't much cryptography knowledge, I'd like to ask if someone who knows about it could tell me if this is "safe". I know nothing is 100% safe, but I'd like to know how far it is safe and maybe some improvements.

I currently have two programs on a server running, which are trying to brute-force it. After nearly 2 months now, there is nothing found yet, so I guess it's working at least a bit.

PreCrypt.java

package net.prefixaut.prelib.crypt;

import java.security.SecureRandom;
import java.util.List;
import java.util.ArrayList;
import java.math.BigInteger;

public class PreCrypt {

    // Remove Constructor
    private PreCrypt() {}

    /**
     * Default Charset which contains only default Characters.<br/>
     * Supports: a-z, A-Z, 0-9 and most special-characters. <b>Current amount of supported Characters: 92</b>
     */
    public static final String defaultCharset = "abcdefghijklmopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#?!\\§$%&/{[(=+-~*_.:,;µ@€<>|)]}";
    /**
     * Complete Charset which contains most relevant Characters<br/>
     * <b>Current amount of supported Characters: 102</b>
     */
    public static final String completeCharset = defaultCharset + "\n\t\r" + // Extra Space Chars
            "üäöÜÄÖ"; // German Extra Chars

    public static char countUp(char c, int amount) {
        return PreCrypt.countUp(c, amount, defaultCharset);
    }

    public static char countUp(char c, int amount, String str) {
        return PreCrypt.countUp(c, amount, str.toCharArray());
    }

    public static char countUp(char c, int amount, char[] charset) {
        boolean set = false;
        for (int i = 0; i < amount; i++) {
            for (int o = 0; o < charset.length; o++) {
                if (c == charset[o]) {
                    if (o + 1 >= charset.length) o = 0;
                    else o++;
                    c = charset[o];
                    set = true;
                    break;
                }
            }
            if (!set) c++;
            else set = false;
        }
        return c;
    }

    public static char countDown(char c, int amount) {
        return PreCrypt.countDown(c, amount, defaultCharset);
    }

    public static char countDown(char c, int amount, String str) {
        return PreCrypt.countDown(c, amount, str.toCharArray());
    }

    public static char countDown(char c, int amount, char[] charset) {
        boolean set = false;
        for (int i = 0; i < amount; i++) {
            for (int o = 0; o < charset.length; o++) {
                if (c == charset[o]) {
                    if (o - 1 < 0) o = charset.length - 1;
                    else o--;
                    c = charset[o];
                    set = true;
                    break;
                }
            }
            if (!set) c--;
            else set = false;
        }
        return c;
    }

    public static String encrypt(String code, String key) {
        return PreCrypt.encrypt(code, key, PreCrypt.defaultCharset);
    }

    public static String encrypt(String code, String key, String charset) {
        return PreCrypt.encrypt(code, key, charset.toCharArray());
    }

    public static String encrypt(String code, String key, char[] charset) {
        String r = "";
        r = PreCrypt.count(code, key, charset, (boolean foo, int number, char c) -> {
            if (foo) c = PreCrypt.countUp(c, number, charset);
                else c = PreCrypt.countDown(c, number, charset);
                return c;
            });
        return r;
    }

    public static String decrypt(String code, String key) {
        return PreCrypt.decrypt(code, key, PreCrypt.defaultCharset);
    }

    public static String decrypt(String code, String key, String charset) {
        return PreCrypt.decrypt(code, key, charset.toCharArray());
    }

    public static String decrypt(String code, String key, char[] charset) {
        String r = "";
        r = PreCrypt.count(code, key, charset, (boolean foo, int number, char c) -> {
            if (foo) c = PreCrypt.countDown(c, number, charset);
                else c = PreCrypt.countUp(c, number, charset);
                return c;
            });
        return r;
    }

    @FunctionalInterface
    private interface Handler {

        public char onCode(boolean foo, int number, char c);
    }

    private static String count(String code, String key, char[] charset, Handler handler) {
        String r = "";
        List<Integer> keys = new ArrayList<Integer>();
        BigInteger keySum = new BigInteger(new byte[] { 0 });
        // Load all Key-Values into KEYS, and summarize them in KEYSUM
        for (int i = 0; i < key.length(); i++) {
            int v = (int) key.charAt(i);
            keys.add(v);
            keySum = keySum.add(new BigInteger("" + v));
        }
        List<Integer> temp = new ArrayList<Integer>();
        for (int i = 0; i < code.length(); i++) {
            char c = code.charAt(i);
            int kIndex = i;
            // Get current Index
            while (kIndex > keys.size() - 1)
                kIndex -= keys.size() - 1;
            // Load from Index till end, and from 0 till Index into TEMP
            for (int o = kIndex; o < keys.size(); o++)
                temp.add(keys.get(o));
            for (int o = 0; o < kIndex; o++)
                temp.add(keys.get(o));
            // Encode
            for (int o = 0; o < temp.size(); o++) {
                int count = (temp.get(o) * (o + 1)) / (keySum.intValue() / key.length()) + keySum.intValue();
                count = count >> o + 1;
                c = handler.onCode( (temp.get(o) * (o + 1)) % 2 == 0, count, c);
            }
            r += c;
            temp.clear();
        }
        return r;
    }

    /**
     * Generates a Random String with the length of length, from the {@link PreCrypt#defaultCharset default-Charset}
     * 
     * @param length
     *            Length of the random String
     */
    public static String generateRandomString(int length) {
        return PreCrypt.generateRandomString(length, PreCrypt.defaultCharset);
    }

    /**
     * Generates a Random String with the length of the given length, from the given charset
     * 
     * @param length
     *            Length of the random String
     * @param charset
     *            Charset which contains all Characters which are allowed for the random String
     */
    public static String generateRandomString(int length, CharSequence charset) {
        return PreCrypt.generateRandomString(length, (String) charset);
    }

    /**
     * Generates a Random String with the length of the given length, from the given charset
     * 
     * @param length
     *            Length of the random String
     * @param charset
     *            Charset which contains all Characters which are allowed for the random String
     */
    public static String generateRandomString(int length, String charset) {
        return PreCrypt.generateRandomString(length, charset.toCharArray());
    }

    /**
     * Generates a Random String with the length of the given length, from the given charset
     * 
     * @param length
     *            Length of the random String
     * @param charset
     *            Charset which contains all Characters which are allowed for the random String
     */
    public static String generateRandomString(int length, char[] charset) {
        String re = "";
        SecureRandom r = new SecureRandom();
        for (int i = 0; i < length; i++)
            re += charset[ (r.nextInt(charset.length))];
        return re;
    }
}
\$\endgroup\$
  • 3
    \$\begingroup\$ Brute-forcing is not the only way to break an encryption system. Before thinking rolling your own encryption program provide real security please read this : security.stackexchange.com/questions/106186/… (It's a good exercise to do, but do not protect real world data or application with your own scheme) \$\endgroup\$ – Marc-Andre Nov 26 '15 at 18:40
  • 1
    \$\begingroup\$ I see. Well, like I said at the beginning, I have no real Idea about Cryptography. Thanks that you showed me that, I'll keep it in mind :) \$\endgroup\$ – PreFiXAUT Nov 26 '15 at 18:44
  • 1
    \$\begingroup\$ This is one the reason I warned about rolling your own! I have not enough meat to provide you with a real alternative or what you could do better, but one thing is sure is crypto is hard! \$\endgroup\$ – Marc-Andre Nov 26 '15 at 18:50
  • 1
    \$\begingroup\$ What are the requirements that are forcing you to roll your own system? There are really good completely free compatible-with-everything libraries for symmetric cryptography out there that are completely safe. \$\endgroup\$ – Groostav Nov 26 '15 at 18:58
  • 1
    \$\begingroup\$ Well I just wanted to make my own and experiment with it. I also wanted to provide a simple Interface, since allot of Encryption-Libraries I saw use a very (imo) weird and long way to encode/decode Stuff which is simply not needed what I think. Anyways, why do you question about it? This is not really a Question about if to use, more like a proof of concept and should obviously not be used as real crypt-software. \$\endgroup\$ – PreFiXAUT Nov 26 '15 at 19:13
2
\$\begingroup\$

I don't think I can give you a very good answer, but here goes:

  • If the span of (temp.get(o) * (o + 1)) (for use in the evenenss test) is not itself even, then your cyphertext is going to be biased. In other words, if the total span of numbers covered by that function is { 0, 1, 2, ... 7, 8}, Then, { 0, 2, 4, 6, 8 } will output 0, and { 1, 3, 5, 7 } will output 1. notice that there is one less member in the one-output camp than there is in the zero output camp. This means that, when encrypting, there will be more countUp's than countDown's, which is a statistical weakness.

  • How is SecureRandom being used?

  • In the main loop of the count function, I see no usage of the result variable or any kind of persistent state. If you're not carrying state between rounds of the main loop, then your cypher is operating in electronic codebook mode, which is not a good thing. How are you chaining your rounds?

Even if you address my concerns, I'm no cryptologist, so I would be very surprised if we can come up with a robust crypto system on this forum. I would strongly encourage you to look into one of the many libraries that provide the AES or a modern stream cypher like Spritz or HC-128 if performance is a constraint.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.