<commit_before>package org.intellimate.izou.security.storage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.intellimate.izou.security.SecurityModule;
import org.intellimate.izou.system.file.FileSystemManager;
import ro.fortsoft.pf4j.PluginDescriptor;

import javax.crypto.SecretKey;
import java.io.*;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.HashMap;

/**
 * The SecureStorage class offers a way for addOns to store data so that other addOns cannot access it. For example if
 * addOn A wants to store the users username and password to some service, it can do so using this class without any
 * other addOn having access to that information.
 * <p>
 *     While the stored information is encrypted, it is not safe from the user. In theory and with a lot of effort, the
 *     user could extract the keys and decrypt the stored information since the keys are not hidden from the user (only
 *     from addOns).
 * </p>
 */
public final class SecureStorage {
    private static boolean exists = false;
    private HashMap<String, SecureContainer> containers;
    private final Logger logger = LogManager.getLogger(this.getClass());

    /**
     * Creates an SecureStorage. There can only be one single SecureStorage, so calling this method twice
     * will cause an illegal access exception.
     *
     * @return a SecureAccess object
     * @throws IllegalAccessException thrown if this method is called more than once
     */
    public static SecureStorage createSecureStorage() throws IllegalAccessException {
        if (!exists) {
            SecureStorage secureStorage = new SecureStorage();
            exists = true;
            return secureStorage;
        }

        throw new IllegalAccessException("Cannot create more than one instance of IzouSecurityManager");
    }

    /**
     * Creates a new SecureStorage instance if and only if none has been created yet
     *
     * @throws IllegalAccessException thrown if this method is called more than once
     */
    private SecureStorage() throws IllegalAccessException, NullPointerException {
        if (exists) {
            throw new IllegalAccessException("Cannot create more than one instance of IzouSecurityManager");
        }

        SecretKey key = retrieveKey();
        if (key == null) {
            SecurityModule securityModule = new SecurityModule();
            key = securityModule.generateKey();

            if (key != null) {
                storeKey(key);
            } else {
                throw new NullPointerException("Unable to create security key");
            }
        }

        containers = retrieveContainers();
        if (containers == null) {
            containers = new HashMap<>();
        }
    }

    /**
     * Stores a {@link SecureContainer} with the given secure ID of the plugin descriptor. Each addOn can only have 1
     * secure container, so in order to update it, retrieve it and store it again.
     *
     * @param descriptor The plugin descriptor belonging to an addOn
     * @param container The secure container to be stored with an addOn
     */
    public void store(PluginDescriptor descriptor, SecureContainer container) {
        HashMap<String, String> clearTextData = container.getClearTextData();
        HashMap<byte[], byte[]> cryptData = container.getCryptData();

        SecretKey secretKey = retrieveKey();
        SecurityModule module = new SecurityModule();
        for (String key : clearTextData.keySet()) {
            cryptData.put(module.encryptAES(key, secretKey), module.encryptAES(clearTextData.get(key), secretKey));
        }

        container.setCryptData(cryptData);

        containers.put(descriptor.getSecureID(), container);
        saveContainers();
    }

    /**
     * Retrieves a {@link SecureContainer} with the given secure ID of the plugin descriptor
     *
     * @param descriptor The plugin descriptor belonging to an addOn
     * @return  container The secure container that was retrieved
     */
    public SecureContainer retrieve(PluginDescriptor descriptor) {
        SecureContainer container = containers.get(descriptor.getSecureID());
        HashMap<byte[], byte[]> cryptData = container.getCryptData();
        HashMap<String, String> clearTextData = container.getClearTextData();

        SecretKey secretKey = retrieveKey();
        SecurityModule module = new SecurityModule();
        for (byte[] key : cryptData.keySet()) {
            clearTextData.put(module.decryptAES(key, secretKey), module.decryptAES(cryptData.get(key), secretKey));
        }

        container.setClearTextData(clearTextData);
        return container;
    }

    private void saveContainers() {
        String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
        final String containerFile = workingDir + File.separator + "system" + File.separator + "data" + File.separator
                + "containers.ser";
        try {
            FileOutputStream fileOut = new FileOutputStream(containerFile);
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(containers);
            out.close();
            fileOut.close();
        } catch(IOException e) {
            logger.error("Unable to save containers to file", e);
        }
    }

    private HashMap<String, SecureContainer> retrieveContainers() {
        HashMap<String, SecureContainer> containers = null;
        String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
        final String containerFile = workingDir + File.separator + "system" + File.separator + "data" + File.separator
                + "containers.ser";
        try {
            FileInputStream fileIn = new FileInputStream(containerFile);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Object o = in.readObject();
            if (o instanceof HashMap) {
                containers = (HashMap) o;
            }
            in.close();
            fileIn.close();
        } catch(IOException | ClassNotFoundException e) {
            logger.error("Unable to save containers to file", e);
        }

        return containers;
    }

    private SecretKey retrieveKey() {
        SecretKey key = null;
        try {
            String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
            final String keyStoreFile = workingDir + File.separator + "system" + File.separator + "izou.keystore";
            KeyStore keyStore = createKeyStore(keyStoreFile, "4b[X:+H4CS&avY<)");

            KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection("Ev45j>eP}QTR?K9_".toCharArray());
            KeyStore.Entry entry = keyStore.getEntry("izou_key", keyPassword);
            key = ((KeyStore.SecretKeyEntry) entry).getSecretKey();
        } catch(NullPointerException e) {
            return null;
        } catch (UnrecoverableEntryException | NoSuchAlgorithmException | KeyStoreException e) {
            logger.error("Unable to retrieve key", e);
        }

        return key;
    }

    private void storeKey(SecretKey key) {
        String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
        final String keyStoreFile = workingDir + File.separator + "system" + File.separator + "izou.keystore";
        KeyStore keyStore = createKeyStore(keyStoreFile, "4b[X:+H4CS&avY<)");


        try {
            KeyStore.SecretKeyEntry keyStoreEntry = new KeyStore.SecretKeyEntry(key);
            KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection("Ev45j>eP}QTR?K9_".toCharArray());
            keyStore.setEntry("izou_key", keyStoreEntry, keyPassword);
            keyStore.store(new FileOutputStream(keyStoreFile), "4b[X:+H4CS&avY<)".toCharArray());
        } catch (NoSuchAlgorithmException | KeyStoreException
                | CertificateException | IOException e) {
            logger.error("Unable to store key", e);
        }
    }

    private KeyStore createKeyStore(String fileName, String password)  {
        File file = new File(fileName);
        KeyStore keyStore = null;
        try {
            keyStore = KeyStore.getInstance("JCEKS");
            if (file.exists()) {
                keyStore.load(new FileInputStream(file), password.toCharArray());
            } else {
                keyStore.load(null, null);
                keyStore.store(new FileOutputStream(fileName), password.toCharArray());
            }
        } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) {
            logger.error("Unable to create key store", e);
        }

        return keyStore;
    }
}
<commit_msg>added javadoc<commit_after>package org.intellimate.izou.security.storage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.intellimate.izou.security.SecurityModule;
import org.intellimate.izou.system.file.FileSystemManager;
import ro.fortsoft.pf4j.PluginDescriptor;

import javax.crypto.SecretKey;
import java.io.*;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.HashMap;

/**
 * The SecureStorage class offers a way for addOns to store data so that other addOns cannot access it. For example if
 * addOn A wants to store the users username and password to some service, it can do so using this class without any
 * other addOn having access to that information.
 * <p>
 *     While the stored information is encrypted, it is not safe from the user. In theory and with a lot of effort, the
 *     user could extract the keys and decrypt the stored information since the keys are not hidden from the user (only
 *     from addOns).
 * </p>
 */
public final class SecureStorage {
    private static boolean exists = false;
    private HashMap<String, SecureContainer> containers;
    private final Logger logger = LogManager.getLogger(this.getClass());

    /**
     * Creates an SecureStorage. There can only be one single SecureStorage, so calling this method twice
     * will cause an illegal access exception.
     *
     * @return a SecureAccess object
     * @throws IllegalAccessException thrown if this method is called more than once
     */
    public static SecureStorage createSecureStorage() throws IllegalAccessException {
        if (!exists) {
            SecureStorage secureStorage = new SecureStorage();
            exists = true;
            return secureStorage;
        }

        throw new IllegalAccessException("Cannot create more than one instance of IzouSecurityManager");
    }

    /**
     * Creates a new SecureStorage instance if and only if none has been created yet
     *
     * @throws IllegalAccessException thrown if this method is called more than once
     */
    private SecureStorage() throws IllegalAccessException, NullPointerException {
        if (exists) {
            throw new IllegalAccessException("Cannot create more than one instance of IzouSecurityManager");
        }

        SecretKey key = retrieveKey();
        if (key == null) {
            SecurityModule securityModule = new SecurityModule();
            key = securityModule.generateKey();

            if (key != null) {
                storeKey(key);
            } else {
                throw new NullPointerException("Unable to create security key");
            }
        }

        containers = retrieveContainers();
        if (containers == null) {
            containers = new HashMap<>();
        }
    }

    /**
     * Stores a {@link SecureContainer} with the given secure ID of the plugin descriptor. Each addOn can only have 1
     * secure container, so in order to update it, retrieve it and store it again.
     *
     * @param descriptor The plugin descriptor belonging to an addOn
     * @param container The secure container to be stored with an addOn
     */
    public void store(PluginDescriptor descriptor, SecureContainer container) {
        HashMap<String, String> clearTextData = container.getClearTextData();
        HashMap<byte[], byte[]> cryptData = container.getCryptData();

        SecretKey secretKey = retrieveKey();
        SecurityModule module = new SecurityModule();
        for (String key : clearTextData.keySet()) {
            cryptData.put(module.encryptAES(key, secretKey), module.encryptAES(clearTextData.get(key), secretKey));
        }

        container.setCryptData(cryptData);

        containers.put(descriptor.getSecureID(), container);
        saveContainers();
    }

    /**
     * Retrieves a {@link SecureContainer} with the given secure ID of the plugin descriptor
     *
     * @param descriptor The plugin descriptor belonging to an addOn
     * @return  container The secure container that was retrieved
     */
    public SecureContainer retrieve(PluginDescriptor descriptor) {
        SecureContainer container = containers.get(descriptor.getSecureID());
        HashMap<byte[], byte[]> cryptData = container.getCryptData();
        HashMap<String, String> clearTextData = container.getClearTextData();

        SecretKey secretKey = retrieveKey();
        SecurityModule module = new SecurityModule();
        for (byte[] key : cryptData.keySet()) {
            clearTextData.put(module.decryptAES(key, secretKey), module.decryptAES(cryptData.get(key), secretKey));
        }

        container.setClearTextData(clearTextData);
        return container;
    }

    /**
     * Saves the containers to ./system/data/containers.ser
     */
    private void saveContainers() {
        String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
        final String containerFile = workingDir + File.separator + "system" + File.separator + "data" + File.separator
                + "containers.ser";
        try {
            FileOutputStream fileOut = new FileOutputStream(containerFile);
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(containers);
            out.close();
            fileOut.close();
        } catch(IOException e) {
            logger.error("Unable to save containers to file", e);
        }
    }

    /**
     * Retrieves the containers from ./system/data/containers.ser if the file is found, else returns null
     *
     * @return the containers from ./system/data/containers.ser if the file is found, else null
     */
    private HashMap<String, SecureContainer> retrieveContainers() {
        HashMap<String, SecureContainer> containers = null;
        String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
        final String containerFile = workingDir + File.separator + "system" + File.separator + "data" + File.separator
                + "containers.ser";
        try {
            FileInputStream fileIn = new FileInputStream(containerFile);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Object o = in.readObject();
            if (o instanceof HashMap) {
                containers = (HashMap) o;
            }
            in.close();
            fileIn.close();
        } catch(IOException | ClassNotFoundException e) {
            logger.error("Unable to save containers to file", e);
        }

        return containers;
    }

    /**
     * Retrieves the izou aes key stored in a keystore
     *
     * @return the izou aes key stored in a keystore
     */
    private SecretKey retrieveKey() {
        SecretKey key = null;
        try {
            String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
            final String keyStoreFile = workingDir + File.separator + "system" + File.separator + "izou.keystore";
            KeyStore keyStore = createKeyStore(keyStoreFile, "4b[X:+H4CS&avY<)");

            KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection("Ev45j>eP}QTR?K9_".toCharArray());
            KeyStore.Entry entry = keyStore.getEntry("izou_key", keyPassword);
            key = ((KeyStore.SecretKeyEntry) entry).getSecretKey();
        } catch(NullPointerException e) {
            return null;
        } catch (UnrecoverableEntryException | NoSuchAlgorithmException | KeyStoreException e) {
            logger.error("Unable to retrieve key", e);
        }

        return key;
    }

    /**
     * Stores the izou aes key in a keystore
     *
     * @param key the key to store
     */
    private void storeKey(SecretKey key) {
        String workingDir = FileSystemManager.FULL_WORKING_DIRECTORY;
        final String keyStoreFile = workingDir + File.separator + "system" + File.separator + "izou.keystore";
        KeyStore keyStore = createKeyStore(keyStoreFile, "4b[X:+H4CS&avY<)");


        try {
            KeyStore.SecretKeyEntry keyStoreEntry = new KeyStore.SecretKeyEntry(key);
            KeyStore.PasswordProtection keyPassword = new KeyStore.PasswordProtection("Ev45j>eP}QTR?K9_".toCharArray());
            keyStore.setEntry("izou_key", keyStoreEntry, keyPassword);
            keyStore.store(new FileOutputStream(keyStoreFile), "4b[X:+H4CS&avY<)".toCharArray());
        } catch (NoSuchAlgorithmException | KeyStoreException
                | CertificateException | IOException e) {
            logger.error("Unable to store key", e);
        }
    }

    /**
     * Creates a new keystore for the izou aes key
     *
     * @param fileName the path to the keystore
     * @param password the password to use with the keystore
     * @return the newly created keystore
     */
    private KeyStore createKeyStore(String fileName, String password)  {
        File file = new File(fileName);
        KeyStore keyStore = null;
        try {
            keyStore = KeyStore.getInstance("JCEKS");
            if (file.exists()) {
                keyStore.load(new FileInputStream(file), password.toCharArray());
            } else {
                keyStore.load(null, null);
                keyStore.store(new FileOutputStream(fileName), password.toCharArray());
            }
        } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) {
            logger.error("Unable to create key store", e);
        }

        return keyStore;
    }
}

{'fraction_non_alphanumeric': 0.08762886597938144, 'fraction_numerical': 0.0017362995116657625, 'mean_word_length': 2.693587174348697, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1891764', 'n_tokens_mistral': 4734, 'n_tokens_neox': 4612, 'n_words': 1401}