package org.expeditee.auth.account; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; 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.Iterator; import java.util.Map; import java.util.Properties; import java.util.Scanner; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.expeditee.auth.AuthenticatorBrowser; import org.expeditee.auth.mail.Mail; import org.expeditee.auth.mail.Mail.MailEntry; import org.expeditee.auth.tags.AuthenticationTag; import org.expeditee.encryption.CryptographyConstants; import org.expeditee.gui.DisplayController; import org.expeditee.gui.Frame; import org.expeditee.gui.FrameIO; import org.expeditee.gui.MessageBay; import org.expeditee.items.Text; import org.expeditee.settings.UserSettings; import org.expeditee.settings.identity.passwordrecovery.Colleagues; import org.expeditee.settings.identity.secrets.KeyList; import org.expeditee.stats.Formatter; import com.codahale.shamir.Scheme; public class Password implements CryptographyConstants { /** * Changes the recorded password for a user in the key store; given the username signaling whose password to change, along with the existing and new password. */ public static void changePassword(Map userdata) throws NoSuchAlgorithmException, KeyStoreException, FileNotFoundException, CertificateException, IOException, ClassNotFoundException, SQLException { String username = userdata.get(AuthenticationTag.Username); String password = userdata.get(AuthenticationTag.Password); String newpassword = userdata.get(AuthenticationTag.NewPassword); final SecretKey key = AuthenticatorBrowser.getInstance().getSecretKey(username, password); if (key == null) { MessageBay.errorMessage("The username + existing password combination was incorrect."); } else { AuthenticatorBrowser.getInstance().putKey(username, newpassword, key); MessageBay.displayMessage("Password changed successfully."); DisplayController.setCurrentFrame(FrameIO.LoadFrame("multiuser1"), true); } } /** * Generates a intergalaictic number for a specified user and emails that number using the specified email. * @param userData */ public static void generateAndDeliverIntergalacticNumber(Map userData) { String username = userData.get(AuthenticationTag.Username); String email = userData.get(AuthenticationTag.Email); try { // Generate message text. String intergalacticNumber = AuthenticatorBrowser.getInstance().newIntergalacticNumber(username, email); String nl = System.getProperty("line.separator"); StringBuilder sb = new StringBuilder(); sb.append("You are receiving this email because someone is attempting to reset your Expeditee password." + nl); sb.append("If you did not make this request then no action is required." + nl); sb.append("If it was you who made this request, the following string of characters is your intergalactic number: " + intergalacticNumber + nl); sendEmail(email, sb); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException | IOException | SQLException | MessagingException e) { e.printStackTrace(); } } /** * Confirms that the specified intergalaictic number matches the one of file for the specified username. * Passing this test it then alerts the users pw colleagues through a one-off secure Expeditee message. * @param tags */ public static void confirmIntergalacticNumberAndAlertTrustedUsers(Map tags) { // Confirm intergalactic numbers match String username = tags.get(AuthenticationTag.Username); String intergalacticNumber = tags.get(AuthenticationTag.IntergalacticNumber); boolean match = false; try { match = AuthenticatorBrowser.getInstance().confirmIntergalaticNumber(username, intergalacticNumber); } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | ClassNotFoundException | IOException | SQLException e) { e.printStackTrace(); return; } if (!match) { MessageBay.errorMessage("The provided identity number does not match the one stored on file."); return; } // Get colleagues to distribute messages too. String[] colleagues = getPasswordColleaguesFromUsername(username); // Send secure message to colleague one String colleagueOne = colleagues[0]; String time = Formatter.getDateTime(); String topic = "Password Recovery for " + username; String message = "Your colleague " + username + " would like you to help them recover access to their account."; Map options = new HashMap(); options.put("Provide assistance", "AuthEmailPasswordShare " + username); MailEntry mail = new MailEntry(time, username, colleagueOne, topic, message, options); Mail.sendOneOffMail(mail, colleagueOne, Base64.getDecoder().decode(intergalacticNumber)); // Send secure message to colleague two String colleagueTwo = colleagues[1]; Mail.sendOneOffMail(mail, colleagueTwo, Base64.getDecoder().decode(intergalacticNumber)); String nl = System.getProperty("line.separator"); StringBuilder sb = new StringBuilder(); sb.append("You are receiving this email because one of your Expeditee contacts has sent you a one-off secure message." + nl); sb.append("When you log into Expeditee and check your mail it will be there waiting for you." + nl); sb.append("You will need the following key to read this message: " + nl); sb.append(intergalacticNumber); try { // Send email with key to colleague one String colleagueOneEmail = colleagues[2]; sendEmail(colleagueOneEmail, sb); // Send email with key to colleague two String colleagueTwoEmail = colleagues[3]; sendEmail(colleagueTwoEmail, sb); MessageBay.displayMessage("Identity confirmed. Your trusted contacts have been notified via one-off secure Expeditee message. " + "You will recieve an email message with a password share from each once they have completed their part of the process. Enter them below."); } catch (MessagingException e) { e.printStackTrace(); } } private static String[] getPasswordColleaguesFromUsername(String username) { Path credentialsFilePath = Paths.get(FrameIO.PROFILE_PATH).resolve(username).resolve("pwcolleagues.inf"); String fileName = null; if (credentialsFilePath.toFile().exists()) { try (Scanner in = new Scanner(credentialsFilePath)) { fileName = in.nextLine(); } catch (IOException e) { MessageBay.errorMessage("Unable to find trusted users contact frame for specified user, are they registered on this computer?"); return null; } } else { MessageBay.errorMessage("Unable to find trusted users contact frame for specified user, are they registered on this computer?"); return null; } int number = Integer.parseInt(fileName.replace(".exp", "")); Frame pwColleagueFrame = FrameIO.LoadFrame(username + number, FrameIO.PROFILE_PATH); Collection textItems = pwColleagueFrame.getTextItems(); textItems.removeIf(text -> !text.getText().startsWith("User_")); String[] ret = new String[4]; Iterator it = textItems.iterator(); while(it.hasNext()) { String content = it.next().getText().toLowerCase().trim(); if (content.contains("user_one:")) { ret[0] = content.replace("user_one:", "").trim(); } else if (content.contains("user_two:")) { ret[1] = content.replace("user_two:", "").trim(); } } // find colleague one email Path credentialsDirectoryPath = UserSettings.PublicAndPrivateResources ? Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username) : Paths.get(FrameIO.PARENT_FOLDER); credentialsDirectoryPath = credentialsDirectoryPath.resolve("contacts").resolve(ret[0] + "-credentials"); try (Scanner in = new Scanner(credentialsDirectoryPath.resolve("credentials.inf").toFile())) { int parseInt = Integer.parseInt(in.nextLine().replace(".exp", "")); Frame frame = FrameIO.LoadFrame(ret[0] + "-credentials" + parseInt, credentialsDirectoryPath.toAbsolutePath().getParent().toString() + File.separator); textItems = frame.getTextItems(); textItems.removeIf(text -> !text.getText().startsWith("Email:")); ret[2] = textItems.iterator().next().getText().replace("Email:", "").trim(); } catch (FileNotFoundException e) { MessageBay.errorMessage("You do not appear to have contact with your nominated password colleague: " + ret[0]); return null; } // find colleague two email credentialsDirectoryPath = UserSettings.PublicAndPrivateResources ? Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-" + username) : Paths.get(FrameIO.PARENT_FOLDER); credentialsDirectoryPath = credentialsDirectoryPath.resolve("contacts").resolve(ret[1] + "-credentials"); try (Scanner in = new Scanner(credentialsDirectoryPath.resolve("credentials.inf").toFile())) { int parseInt = Integer.parseInt(in.nextLine().replace(".exp", "")); Frame frame = FrameIO.LoadFrame(ret[1] + "-credentials" + parseInt, credentialsDirectoryPath.toAbsolutePath().getParent().toString() + File.separator); textItems = frame.getTextItems(); textItems.removeIf(text -> !text.getText().startsWith("Email:")); ret[3] = textItems.iterator().next().getText().replace("Email:", "").trim(); } catch (FileNotFoundException e) { MessageBay.errorMessage("You do not appear to have contact with your nominated password colleague: " + ret[1]); return null; } return ret; } public static void sendEmail(String email, StringBuilder sb) throws MessagingException, AddressException { // Establish properties for email. Properties properties = System.getProperties(); properties.setProperty("mail.transport.protocol", "smtp"); properties.setProperty("mail.smtp.host", "smtp.gmail.com"); properties.setProperty("mail.smtp.port", "465"); properties.setProperty("mail.smtp.starttls.enable", "true"); properties.setProperty("mail.smtp.auth", "true"); properties.setProperty("mail.smtp.debug", "true"); properties.setProperty("mail.smtp.auth", "true"); properties.setProperty("mail.smtp.socketFactory.port", "465"); properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); properties.setProperty("mail.smtp.socketFactory.fallback", "false"); Session session = Session.getDefaultInstance(properties, new javax.mail.Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { //return new PasswordAuthentication("noreply.expeditee", "intergalacticnumber"); return new PasswordAuthentication("noreply.expeditee", "exped!tee"); }; }); // construct email message final MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("noreply@expeditee.com")); message.addRecipient(Message.RecipientType.TO, new InternetAddress(email)); message.setSubject("Expeditee Password Recovery"); message.setText(sb.toString()); // send email message Transport.send(message); } public static void setPWColleagues(String colleagueOne, String colleagueTwo) { // Get needed text items. Frame pwRecoveryFrame = FrameIO.LoadFrame(UserSettings.UserName.get() + AuthenticatorBrowser.PASSWORD_RECOVERY_FRAME); Collection textItems = pwRecoveryFrame.getTextItems(); textItems.removeIf(t -> !t.getText().toLowerCase().startsWith("user_")); // Find colleague one and two text items. Set appropriate values. Iterator it = textItems.iterator(); while (it.hasNext()) { Text text = it.next(); if (text.getText().toLowerCase().startsWith("user_one:")) { text.setText("User_one: " + colleagueOne); Colleagues.User_One.set(colleagueOne); } else if (text.getText().toLowerCase().startsWith("user_two:")) { text.setText("User_two:" + colleagueTwo); Colleagues.User_Two.set(colleagueTwo); } } FrameIO.ForceSaveFrame(pwRecoveryFrame); // Key to split and distribute String fullKey = KeyList.PersonalKey.get().getData().get(0); byte[] keyBytes = Base64.getDecoder().decode(fullKey); // Initialise Shamir int totalShares = 2; int requiredShares = 2; Scheme scheme = new Scheme(new SecureRandom(), totalShares, requiredShares); // Create shares Map shares = scheme.split(keyBytes); String colleagueOneShare = Base64.getEncoder().encodeToString(shares.get(1)); String colleagueTwoShare = Base64.getEncoder().encodeToString(shares.get(2)); // Distribute share zero to colleague one String time = org.expeditee.stats.Formatter.getDateTime(); String sender = UserSettings.UserName.get(); String topic = "Please help me secure my Expeditee account."; String message = "Run the below action to store a secret key that will help me recover access to my account should I ever loose it."; Map options = new HashMap(); options.put("Store Secret Key for " + sender, "AuthAddSecretKey " + sender + "PersonalKeyShare " + colleagueOneShare); MailEntry mail = new MailEntry(time, sender, colleagueOne, topic, message, options); Mail.sendMail(mail, colleagueOne); // Distribute share one to colleague two options = new HashMap(); options.put("Store Secret Key for " + sender, "AuthAddSecretKey " + sender + "PersonalKeyShare " + colleagueTwoShare); mail = new MailEntry(time, sender, colleagueTwo, topic, message, options); Mail.sendMail(mail, colleagueTwo); MessageBay.displayMessage("You PW Colleagues have been set to " + colleagueOne + " and " + colleagueTwo + ". " + "They have been sent a Expeditee mail that they can use to store a share of your secret key."); } public static void emailPasswordShare(String colleagueName) { Path credentialsDirectoryPath = Paths.get(FrameIO.CONTACTS_PATH).resolve(colleagueName + "-credentials"); String colleagueEmail = null; try (Scanner in = new Scanner(credentialsDirectoryPath.resolve("credentials.inf").toFile())) { int parseInt = Integer.parseInt(in.nextLine().replace(".exp", "")); Frame frame = FrameIO.LoadFrame(colleagueName + "-credentials" + parseInt, credentialsDirectoryPath.toAbsolutePath().getParent().toString() + File.separator); Collection textItems = frame.getTextItems(); textItems.removeIf(text -> !text.getText().startsWith("Email:")); colleagueEmail = textItems.iterator().next().getText().replace("Email:", "").trim(); } catch (FileNotFoundException e) { MessageBay.errorMessage("You do not appear to have contact with: " + colleagueName); return; } Frame secretsFrame = FrameIO.LoadFrame(UserSettings.UserName.get() + AuthenticatorBrowser.SECRETS_FRAME); Collection textItems = secretsFrame.getTextItems(); textItems.removeIf(text -> !text.getText().toLowerCase().equals(colleagueName + "personalkeyshare")); String key = textItems.iterator().next().getData().get(0); String nl = System.getProperty("line.separator"); StringBuilder sb = new StringBuilder(); sb.append("In responce to your request for assistance regaining access to your Expeditee account, your colleague " + UserSettings.UserName.get() + " has provided you with the following key share:" + nl); sb.append(key + nl); try { sendEmail(colleagueEmail, sb); MessageBay.displayMessage("Your share of " + colleagueName + "'s password has been sent to their public email address."); } catch (MessagingException e) { MessageBay.errorMessage("An error occured sending a email to your colleage " + colleagueName + " with the email " + colleagueEmail); } } public static void regainAccountAccess(Map userData) { // Store shares in map Map contributingParts = new HashMap(); contributingParts.put(1, Base64.getDecoder().decode(userData.get(AuthenticationTag.PasswordSliceOne))); contributingParts.put(2, Base64.getDecoder().decode(userData.get(AuthenticationTag.PasswordSliceTwo))); // initialise shamir int totalShares = 2; int requiredShares = 2; Scheme scheme = new Scheme(new SecureRandom(), totalShares, requiredShares); // perform joining byte[] join = scheme.join(contributingParts); try { // TODO: YIKES! We can currently change anyone's password! AuthenticatorBrowser.getInstance().putKey(userData.get(AuthenticationTag.Username), userData.get(AuthenticationTag.NewPassword), new SecretKeySpec(join, SymmetricAlgorithm)); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | ClassNotFoundException | IOException | SQLException e) { e.printStackTrace(); } MessageBay.displayMessage("Your new password has been set."); } }