package org.expeditee.auth.account; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.sql.SQLException; import java.util.Base64; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.function.Consumer; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apollo.io.AudioPathManager; import org.expeditee.agents.ExistingFramesetException; import org.expeditee.agents.InvalidFramesetNameException; import org.expeditee.auth.Actions; import org.expeditee.auth.AuthenticatorBrowser; import org.expeditee.auth.tags.AuthenticationTag; import org.expeditee.core.Colour; import org.expeditee.gui.DisplayController; import org.expeditee.gui.Frame; import org.expeditee.gui.FrameIO; import org.expeditee.gui.MessageBay; import org.expeditee.gui.MessageBay.Progress; import org.expeditee.io.ExpReader; import org.expeditee.items.Item; import org.expeditee.items.PermissionPair; import org.expeditee.items.Text; import org.expeditee.items.UserAppliedPermission; import org.expeditee.setting.GenericSetting; import org.expeditee.setting.Setting; import org.expeditee.setting.TextSetting; import org.expeditee.settings.UserSettings; import org.expeditee.settings.folders.FolderSettings; import org.expeditee.settings.identity.secrets.KeyList; import org.ngikm.cryptography.CryptographyConstants; public class Create implements CryptographyConstants { /** * Create a user account using the specified information in userdata. Creates and stores user keys. * @param userdata Should contain username, password and email. */ public static CreateResult createAccount(Map userdata) { // Track progress String message = "Creating new user account..."; int progress = 0; int step = 16; // Extract user details String username = userdata.get(AuthenticationTag.Username); String password = userdata.get(AuthenticationTag.Password); String email = userdata.get(AuthenticationTag.Email); Progress progressBar = MessageBay.displayProgress(message); progress = progress(message + "Generating Keys.", progress, step, progressBar); // Generate keys // Personal key String personalKey = generatePersonalKey(username, password); if (personalKey == null) { return CreateResult.ErrorSymmetricKey; } // Public and private keys String[] keys = generateAsymmetricKeys(); if (keys == null) { return CreateResult.ErrorAsymmetricKeys; } String privateKey = keys[0]; String publicKey = keys[1]; progress = progress(message + "Creating Profile Frameset.", progress, step, progressBar); // Update in memory settings System.setProperty("user.name", username); UserSettings.UserName.set(username); UserSettings.ProfileName.set(username); UserSettings.setupDefaultFolders(); Frame profile; try { profile = createNewProfile(username, email, personalKey, privateKey, publicKey); } catch (InvalidFramesetNameException | ExistingFramesetException e) { return CreateResult.ErrorNewProfile; } if (AuthenticatorBrowser.CREDENTIALS_FRAME == -1) { return CreateResult.ErrorCredentialsFrame; } progress = progress(message + "Establishing user credentials.", progress, step, progressBar); // Create credentials boolean success = setupCredentialsFrame(username, profile); if (!success) { return CreateResult.ErrorIODuringCredentialsFrameSetup; } progress = progress(message + "Creating Individual Space.", progress, step, progressBar); // Copy private resources to personal area Path personalResources = createPersonalArea(username); progress = progress(message + "Creating Space For Dead Drops.", progress, step, progressBar); createDeaddropsArea(personalResources); System.err.println("**** Hardwired call in Apollo's AuthioPathManager"); AudioPathManager.activateAndScanAudioDir(); // **** progress = progress(message + "Done.", progress, step, progressBar); return CreateResult.SuccessCreateAccount; } public static CreateResult createAlternativeAccess(Map userdata) { String username = userdata.get(AuthenticationTag.Username); FrameIO.CreateFrame(username, null, "default1"); return null; } public enum CreateResult { SuccessCreateAccount ("Account created."), SuccessAlternativeAccount ("Alternative access to account established."), ErrorSymmetricKey ("An error occured while trying to generate your personal key."), ErrorAsymmetricKeys ("An error occured while trying to generate asymmetric keys."), ErrorNewProfile ("An error occured while creating the profile frames."), ErrorCredentialsFrame ("Unable to establish credentials frame for new profile frame."), ErrorIODuringCredentialsFrameSetup ("An error occured during the setup of the new users credentials frame."); private String message = null; private CreateResult(String message) { this.message = message; } public String toString() { return message; } } private static int progress(String message, int progress, int step, Progress progressBar) { try { progressBar.UpdateMessage(message, progress += step); } catch (Exception e) { e.printStackTrace(); } DisplayController.refreshBayArea(); return progress; } private static void createDeaddropsArea(Path personalResources) { File deadDropsDir = new File(personalResources.resolve("deaddrops").toAbsolutePath().toString()); deadDropsDir.mkdir(); } private static Path createPersonalArea(String username) { Path personalResources = UserSettings.PublicAndPrivateResources ? FrameIO.setupPersonalResources(username) : Paths.get(FrameIO.PARENT_FOLDER); File contactsDir = new File(personalResources.resolve("contacts").toAbsolutePath().toString()); contactsDir.mkdir(); return personalResources; } private static boolean setupCredentialsFrame(String username, Frame profile) { try { File credentialsDir = new File(profile.getFramesetPath() + username + "-credentials"); credentialsDir.mkdir(); // credentials.inf file. String credentialsPath = credentialsDir.getAbsolutePath() + File.separator + "credentials.inf"; File credentialsFile = new File(credentialsPath); credentialsFile.createNewFile(); FileWriter out = new FileWriter(credentialsFile); out.write(AuthenticatorBrowser.CREDENTIALS_FRAME + ".exp"); out.flush(); out.close(); // migrate credentials frame Frame credentialsFrame = FrameIO.LoadFrame(username + AuthenticatorBrowser.CREDENTIALS_FRAME); Path destinationDirectory = Paths.get(credentialsDir.getAbsolutePath()); Path destinationFile = destinationDirectory.resolve(AuthenticatorBrowser.CREDENTIALS_FRAME + ExpReader.EXTENTION); FrameIO.migrateFrame(credentialsFrame, destinationFile); return true; } catch (IOException e) { return false; } } private static Frame createNewProfile(String username, String email, String personalKey, String privateKey, String publicKey) throws InvalidFramesetNameException, ExistingFramesetException { // Establish the initial settings for the created user. Map initialSettings = new HashMap(); initialSettings.put("settings.identity.secrets.PersonalKey", constructTextSetting("The AES key used to secure your profile frame - do not share with anyone!", "PersonalKey", personalKey)); initialSettings.put("settings.identity.secrets.PrivateKey", constructTextSetting("The RSA key used to decrypt things encrypted with your RSA public key - do not share with anyone!", "PrivateKey", privateKey)); initialSettings.put("settings.identity.PublicKey", constructTextSetting("The RSA key used to decrypt things encrypted with your RSA public key.", "PublicKey", publicKey)); initialSettings.put("settings.identity.Email", constructGenericSetting(String.class, "Your public-facing email address.", "Email", email, username)); initialSettings.put("settings.UserSettings.UserName", constructGenericSetting(String.class, "Username", "Username", username, username)); initialSettings.put("settings.UserSettings.ProfileName", constructGenericSetting(String.class, "Profilename", "Profilename", username, username)); initialSettings.put("settings.UserSettings.HomeFrame", constructGenericSetting(String.class, "The home frame", "HomeFrame", username + 1, username)); initialSettings.put("org.expeditee.gui.folders.FolderSettings.FrameDirs", FolderSettings.FrameDirs); initialSettings.put("org.expeditee.gui.folders.FolderSettings.ImageDirs", FolderSettings.ImageDirs); initialSettings.put("org.expeditee.gui.folders.FolderSettings.AudioDirs", FolderSettings.AudioDirs); // Record the credentials frame number and password colleagues frame Map> notifiers = new HashMap>(); notifiers.put("settings.identity", frame -> { AuthenticatorBrowser.CREDENTIALS_FRAME = frame.getNumber(); frame.addToData("MultiuserCredentials"); Collection textItems = frame.getTextItems(); for (Text t: textItems) { if (t.getText().equals("Secrets")) { t.setPermission(new PermissionPair(UserAppliedPermission.followLinks, UserAppliedPermission.denied)); break; } } }); notifiers.put("settings.identity.passwordrecovery", frame -> { AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME = frame.getNumber(); }); // Create users profile Frame profile = FrameIO.CreateNewProfile(username, initialSettings, notifiers); int lastNumber = FrameIO.getLastNumber(profile.getFramesetName()); for (int i = 1; i <= lastNumber; i++) { Frame f = FrameIO.LoadFrame(profile.getFramesetName() + i); Text titleItem = f.getTitleItem(); if (i == 1 && titleItem != null) { titleItem.delete(); f.setBackgroundColor(new Colour(1, 1, 0.39f)); } f.setOwner(username); f.getAllItems().stream().forEach(item -> item.setOwner(username)); f.setChanged(true); if (f.getNumber() != AuthenticatorBrowser.CREDENTIALS_FRAME && f.getNumber() != AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME) { f.setEncryptionLabel(AuthenticatorBrowser.PROFILEENCRYPTIONLABEL); } Collection secretsLink = Actions.getByContent(f, "Secrets"); Collection publicKeyItem = Actions.getByContent(f, "PublicKey"); if (!secretsLink.isEmpty() && !publicKeyItem.isEmpty()) { //Then we are on credentials frame f.addToData("MultiuserCredentials"); } Text backupPersonalKey = KeyList.PersonalKey.get(); Text tempPersonalKey = KeyList.PersonalKey.generateText(); tempPersonalKey.setData(personalKey); KeyList.PersonalKey.setSetting(tempPersonalKey); FrameIO.SaveFrame(f); KeyList.PersonalKey.setSetting(backupPersonalKey); } return profile; } private static String generatePersonalKey(String username, String password) { try { Random rand = new SecureRandom(); byte[] keyBytes = new byte[16]; rand.nextBytes(keyBytes); SecretKey key = new SecretKeySpec(keyBytes, SymmetricAlgorithm); AuthenticatorBrowser.getInstance().putKey(username, password, key); String personalKey = Base64.getEncoder().encodeToString(key.getEncoded()); return personalKey; } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException | IOException | SQLException e) { return null; } } private static String[] generateAsymmetricKeys() { try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(AsymmetricAlgorithm); keyGen.initialize(1024); KeyPair keyPair = keyGen.generateKeyPair(); String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); return new String[] { privateKey, publicKey }; } catch (NoSuchAlgorithmException e) { return null; } } private static TextSetting constructTextSetting(String tooltip, String text, String data) { return new TextSetting(tooltip, text) { @Override public Text generateText() { Text t = new Text(text); t.setData(data); return t; } }; } private static GenericSetting constructGenericSetting(Class type, String tooltip, String name, T value, String frameset) { return new GenericSetting(type, tooltip, name, value) { @Override public Text generateRepresentation(String name, String frameset) { Text t = new Text(name + ": " + value); return t; } }; } }