From 41095dba4895bd95b6f058ac08abefc2d749b461 Mon Sep 17 00:00:00 2001 From: tonikelope Date: Wed, 5 Oct 2016 22:42:18 +0200 Subject: [PATCH] 1.13 Mega account disk encryption --- src/megabasterd/CryptTools.java | 29 + src/megabasterd/FileGrabberDialog.java | 131 +++-- .../GetMegaMasterPasswordDialog.form | 144 +++++ .../GetMegaMasterPasswordDialog.java | 246 +++++++++ src/megabasterd/MainPanel.java | 134 ++++- src/megabasterd/MainPanelView.java | 26 +- src/megabasterd/MegaAPI.java | 2 +- src/megabasterd/MegaCrypterAPI.java | 9 +- src/megabasterd/MiscTools.java | 5 +- .../SetMegaMasterPasswordDialog.form | 173 ++++++ .../SetMegaMasterPasswordDialog.java | 262 +++++++++ src/megabasterd/SettingsDialog.form | 102 +++- src/megabasterd/SettingsDialog.java | 498 +++++++++++++++--- src/megabasterd/StreamerDialog.java | 2 +- src/megabasterd/TransferenceManager.java | 2 +- src/megabasterd/lock_medium.png | Bin 0 -> 7465 bytes 16 files changed, 1576 insertions(+), 189 deletions(-) create mode 100644 src/megabasterd/GetMegaMasterPasswordDialog.form create mode 100644 src/megabasterd/GetMegaMasterPasswordDialog.java create mode 100644 src/megabasterd/SetMegaMasterPasswordDialog.form create mode 100644 src/megabasterd/SetMegaMasterPasswordDialog.java create mode 100644 src/megabasterd/lock_medium.png diff --git a/src/megabasterd/CryptTools.java b/src/megabasterd/CryptTools.java index 81d7bfc68..ea9cf38ee 100644 --- a/src/megabasterd/CryptTools.java +++ b/src/megabasterd/CryptTools.java @@ -7,13 +7,16 @@ import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; import java.security.spec.RSAPrivateKeySpec; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import static megabasterd.MiscTools.Bin2UrlBASE64; import static megabasterd.MiscTools.UrlBASE642Bin; @@ -30,7 +33,9 @@ public final class CryptTools { public static final byte[] AES_ZERO_IV = i32a2bin(AES_ZERO_IV_I32A); + public static final byte[] PBKDF2_SALT = MiscTools.hex2bin("70acca76a94e9f78"); + public static final int PBKDF2_ITERATIONS = 0x10000; public static Cipher genDecrypter(String algo, String mode, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { @@ -79,6 +84,13 @@ public final class CryptTools { return cryptor.doFinal(data); } + public static byte[] aes_cbc_encrypt_pkcs7(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher cryptor = CryptTools.genCrypter("AES", "AES/CBC/PKCS5Padding", key, iv); + + return cryptor.doFinal(data); + } + public static byte[] aes_cbc_decrypt(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { Cipher decryptor = CryptTools.genDecrypter("AES", "AES/CBC/NoPadding", key, iv); @@ -86,6 +98,13 @@ public final class CryptTools { return decryptor.doFinal(data); } + public static byte[] aes_cbc_decrypt_pkcs7(byte[] data, byte[] key, byte[] iv) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + + Cipher decryptor = CryptTools.genDecrypter("AES", "AES/CBC/PKCS5Padding", key, iv); + + return decryptor.doFinal(data); + } + public static byte[] aes_ecb_encrypt(byte[] data, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { Cipher cryptor = CryptTools.genCrypter("AES", "AES/ECB/NoPadding", key, null); @@ -310,6 +329,16 @@ public final class CryptTools { return pkey; } + + public static byte[] PBKDF2HMACSHA256(String password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeySpecException { + + SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + + KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, iterations, 256); + + return f.generateSecret(ks).getEncoded(); + } + private CryptTools() { } diff --git a/src/megabasterd/FileGrabberDialog.java b/src/megabasterd/FileGrabberDialog.java index 7c2ae9d92..19796bf39 100644 --- a/src/megabasterd/FileGrabberDialog.java +++ b/src/megabasterd/FileGrabberDialog.java @@ -2,6 +2,7 @@ package megabasterd; import java.awt.Color; import java.awt.Font; +import java.awt.Frame; import java.io.File; import java.util.ArrayList; import java.util.Enumeration; @@ -22,6 +23,7 @@ import static megabasterd.MiscTools.formatBytes; import static megabasterd.MiscTools.genID; import static megabasterd.MiscTools.sortTree; import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; import static megabasterd.MiscTools.updateFont; @@ -37,6 +39,7 @@ public final class FileGrabberDialog extends javax.swing.JDialog { private long _total_space; private String _last_selected_account; private final MainPanel _main_panel; + private boolean _remember_master_pass; public boolean isUpload() { return _upload; @@ -57,9 +60,11 @@ public final class FileGrabberDialog extends javax.swing.JDialog { public JTextField getDir_name_textfield() { return dir_name_textfield; } + + public boolean isRemember_master_pass() { + return _remember_master_pass; + } - - /** * Creates new form FileGrabber * @param modal @@ -72,6 +77,7 @@ public final class FileGrabberDialog extends javax.swing.JDialog { _total_space = 0L; _base_path = null; _upload = false; + _remember_master_pass = true; initComponents(); @@ -117,7 +123,6 @@ public final class FileGrabberDialog extends javax.swing.JDialog { swingReflectionInvoke("setEnabled", dir_name_textfield, false); } - } /** @@ -410,16 +415,17 @@ public final class FileGrabberDialog extends javax.swing.JDialog { private void account_comboboxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_account_comboboxItemStateChanged - - if(!swingReflectionInvokeAndWaitForReturn("getSelectedItem", account_combobox).equals(_last_selected_account)) { - - _last_selected_account = (String) swingReflectionInvokeAndWaitForReturn("getSelectedItem", account_combobox); - - final String email = (String)account_combobox.getSelectedItem(); + String selected_item = (String)swingReflectionInvokeAndWaitForReturn("getSelectedItem", account_combobox); - final FileGrabberDialog fg = this; + if(selected_item != null && !selected_item.equals(_last_selected_account)) { + + _last_selected_account = selected_item; + + final String email = selected_item; + + final FileGrabberDialog tthis = this; - swingReflectionInvoke("setForeground", fg.used_space_label, Color.black); + swingReflectionInvoke("setForeground", used_space_label, Color.black); swingReflectionInvoke("setText", used_space_label, "Checking account quota, please wait..."); @@ -439,21 +445,69 @@ public final class FileGrabberDialog extends javax.swing.JDialog { @Override public void run() { - HashMap account_info = (HashMap)fg._main_panel.getMega_accounts().get(email); + HashMap account_info = (HashMap)_main_panel.getMega_accounts().get(email); Long[] quota = null; - MegaAPI ma = fg._main_panel.getMega_active_accounts().get(fg.account_combobox.getSelectedItem()); + MegaAPI ma = _main_panel.getMega_active_accounts().get(account_combobox.getSelectedItem()); if(ma == null) { ma = new MegaAPI(); - + + String password_aes, user_hash; + try { + + if(_main_panel.getMega_master_pass_hash() != null) { + + if(_main_panel.getMega_master_pass() == null) { + + GetMegaMasterPasswordDialog dialog = new GetMegaMasterPasswordDialog((Frame)getParent(), true, _main_panel.getMega_master_pass_hash()); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, tthis); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + if(dialog.isPass_ok()) { + + _main_panel.setMega_master_pass(dialog.getPass()); + + dialog.deletePass(); + + _remember_master_pass = dialog.getRemember_checkbox().isSelected(); + + dialog.dispose(); + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("password_aes")), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("user_hash")), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } else { + + dialog.dispose(); + + throw new Exception(); + } + + } else { + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("password_aes")), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("user_hash")), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } - ma.login(email, (String)account_info.get("password")); + } else { + + password_aes = (String)account_info.get("password_aes"); + + user_hash = (String)account_info.get("user_hash"); + } + + ma.fastLogin(email, MiscTools.bin2i32a(MiscTools.BASE642Bin(password_aes)), user_hash); - fg._main_panel.getMega_active_accounts().put(email, ma); + _main_panel.getMega_active_accounts().put(email, ma); quota = ma.getQuota(); @@ -461,6 +515,7 @@ public final class FileGrabberDialog extends javax.swing.JDialog { getLogger(FileGrabberDialog.class.getName()).log(Level.SEVERE, null, ex); } + } else { @@ -471,38 +526,42 @@ public final class FileGrabberDialog extends javax.swing.JDialog { if(quota[0] <= Math.round((double)quota[1]/2)) { - swingReflectionInvoke("setForeground", fg.used_space_label, new Color(0,128,0)); + swingReflectionInvoke("setForeground", used_space_label, new Color(0,128,0)); } else if(quota[0] < quota[1]) { - swingReflectionInvoke("setForeground", fg.used_space_label, new Color(230,115,0)); + swingReflectionInvoke("setForeground", used_space_label, new Color(230,115,0)); } else { - swingReflectionInvoke("setForeground", fg.used_space_label, Color.red); + swingReflectionInvoke("setForeground", used_space_label, Color.red); } - swingReflectionInvoke("setText", fg.used_space_label, formatBytes(quota[0])+" / "+formatBytes(quota[1])); + swingReflectionInvoke("setText", used_space_label, formatBytes(quota[0])+" / "+formatBytes(quota[1])); + + swingReflectionInvoke("setEnabled", dance_button, true); + swingReflectionInvoke("setEnabled", add_files_button, true); + swingReflectionInvoke("setEnabled", add_folder_button, true); + swingReflectionInvoke("setEnabled", file_tree, true); + swingReflectionInvoke("setEnabled", dir_name_textfield, true); + swingReflectionInvoke("setEnabled", total_file_size_label, true); + swingReflectionInvoke("setEnabled", skip_button, true); + swingReflectionInvoke("setEnabled", skip_rest_button, true); + swingReflectionInvoke("setEnabled", warning_label, true); + swingReflectionInvoke("setEnabled", account_combobox, true); } else { - swingReflectionInvoke("setForeground", fg.used_space_label, Color.red); - - swingReflectionInvoke("setText", fg.used_space_label, "ERROR checking account quota! (Retry in few minutes)."); + swingReflectionInvoke("setEnabled", account_combobox, true); + + account_combobox.setSelectedIndex(-1); + + _last_selected_account = null; + + swingReflectionInvoke("setForeground", used_space_label, Color.red); + + swingReflectionInvoke("setText", used_space_label, "ERROR checking account quota!"); } - - - swingReflectionInvoke("setEnabled", fg.dance_button, true); - swingReflectionInvoke("setEnabled", fg.add_files_button, true); - swingReflectionInvoke("setEnabled", fg.add_folder_button, true); - swingReflectionInvoke("setEnabled", fg.file_tree, true); - swingReflectionInvoke("setEnabled", fg.dir_name_textfield, true); - swingReflectionInvoke("setEnabled", fg.account_combobox, true); - swingReflectionInvoke("setEnabled", fg.total_file_size_label, true); - swingReflectionInvoke("setEnabled", fg.skip_button, true); - swingReflectionInvoke("setEnabled", fg.skip_rest_button, true); - swingReflectionInvoke("setEnabled", fg.warning_label, true); - } }); diff --git a/src/megabasterd/GetMegaMasterPasswordDialog.form b/src/megabasterd/GetMegaMasterPasswordDialog.form new file mode 100644 index 000000000..25007140e --- /dev/null +++ b/src/megabasterd/GetMegaMasterPasswordDialog.form @@ -0,0 +1,144 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/GetMegaMasterPasswordDialog.java b/src/megabasterd/GetMegaMasterPasswordDialog.java new file mode 100644 index 000000000..110067f7c --- /dev/null +++ b/src/megabasterd/GetMegaMasterPasswordDialog.java @@ -0,0 +1,246 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package megabasterd; + +import java.awt.Font; +import java.awt.event.KeyEvent; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JCheckBox; +import javax.swing.JOptionPane; +import javax.swing.JPasswordField; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.updateFont; + + +/** + * + * @author tonikelope + */ +public class GetMegaMasterPasswordDialog extends javax.swing.JDialog { + + private boolean _pass_ok; + + private String _current_pass_hash; + + private byte[] _pass; + + public JPasswordField getNew_pass_textfield() { + return current_pass_textfield; + } + + public JCheckBox getRemember_checkbox() { + return remember_checkbox; + } + + public boolean isPass_ok() { + return _pass_ok; + } + + public byte[] getPass() { + return _pass; + } + + public void deletePass() { + + for(byte c:_pass) { + + c = 0; + } + + _pass = null; + } + + /** + * Creates new form MegaPassDialog + */ + public GetMegaMasterPasswordDialog(java.awt.Frame parent, boolean modal, String current_pass_hash) { + super(parent, modal); + initComponents(); + + updateFont(ok_button, FONT_DEFAULT, Font.PLAIN); + updateFont(cancel_button, FONT_DEFAULT, Font.PLAIN); + updateFont(please_label, FONT_DEFAULT, Font.PLAIN); + updateFont(status_label, FONT_DEFAULT, Font.PLAIN); + updateFont(remember_checkbox, FONT_DEFAULT, Font.PLAIN); + + _current_pass_hash = current_pass_hash; + + _pass_ok = false; + + _pass = null; + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + current_pass_textfield = new javax.swing.JPasswordField(); + cancel_button = new javax.swing.JButton(); + ok_button = new javax.swing.JButton(); + lock_label = new javax.swing.JLabel(); + please_label = new javax.swing.JLabel(); + status_label = new javax.swing.JLabel(); + remember_checkbox = new javax.swing.JCheckBox(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("MegaMasterPassword"); + setResizable(false); + + current_pass_textfield.setFont(new java.awt.Font("Dialog", 0, 18)); // NOI18N + current_pass_textfield.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyPressed(java.awt.event.KeyEvent evt) { + current_pass_textfieldKeyPressed(evt); + } + }); + + cancel_button.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + cancel_button.setText("CANCEL"); + cancel_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancel_buttonActionPerformed(evt); + } + }); + + ok_button.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + ok_button.setText("OK"); + ok_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + ok_buttonActionPerformed(evt); + } + }); + + lock_label.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megabasterd/lock_medium.png"))); // NOI18N + + please_label.setFont(new java.awt.Font("Dialog", 1, 26)); // NOI18N + please_label.setText("Please, enter your master password"); + + status_label.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + + remember_checkbox.setFont(new java.awt.Font("Dialog", 1, 16)); // NOI18N + remember_checkbox.setSelected(true); + remember_checkbox.setText("Remember for this session"); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addComponent(status_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ok_button) + .addGap(18, 18, 18) + .addComponent(cancel_button)) + .addGroup(layout.createSequentialGroup() + .addComponent(lock_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(please_label, javax.swing.GroupLayout.PREFERRED_SIZE, 449, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(current_pass_textfield) + .addComponent(remember_checkbox)))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(please_label) + .addGap(18, 18, 18) + .addComponent(current_pass_textfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(remember_checkbox)) + .addComponent(lock_label)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(ok_button) + .addComponent(cancel_button)) + .addComponent(status_label, javax.swing.GroupLayout.Alignment.TRAILING)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void cancel_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancel_buttonActionPerformed + + swingReflectionInvoke("setVisible", this, false); + }//GEN-LAST:event_cancel_buttonActionPerformed + + private void ok_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ok_buttonActionPerformed + + swingReflectionInvoke("setText", status_label, "Verifying your password, please wait..."); + + final GetMegaMasterPasswordDialog tthis = this; + + THREAD_POOL.execute(new Runnable(){ + + @Override + public void run() {try { + byte[] pass = CryptTools.PBKDF2HMACSHA256(new String(current_pass_textfield.getPassword()), CryptTools.PBKDF2_SALT, CryptTools.PBKDF2_ITERATIONS); + + String pass_hash = MiscTools.Bin2BASE64(MiscTools.HashBin("SHA-1", pass)); + + if(!pass_hash.equals(_current_pass_hash)) { + + JOptionPane.showMessageDialog(tthis, "Bad password!", "Error", JOptionPane.ERROR_MESSAGE); + + swingReflectionInvoke("setText", status_label, ""); + + } else { + + _pass = pass; + + _current_pass_hash = pass_hash; + + _pass_ok = true; + + swingReflectionInvoke("setVisible", tthis, false); + } + + for(byte b:pass) { + + b = 0; + } + + } catch (Exception ex) { + Logger.getLogger(GetMegaMasterPasswordDialog.class.getName()).log(Level.SEVERE, null, ex); + }}}); + + }//GEN-LAST:event_ok_buttonActionPerformed + + private void current_pass_textfieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_current_pass_textfieldKeyPressed + + if(evt.getKeyCode() == KeyEvent.VK_ENTER) { + + ok_buttonActionPerformed(null); + } + }//GEN-LAST:event_current_pass_textfieldKeyPressed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cancel_button; + private javax.swing.JPasswordField current_pass_textfield; + private javax.swing.JLabel lock_label; + private javax.swing.JButton ok_button; + private javax.swing.JLabel please_label; + private javax.swing.JCheckBox remember_checkbox; + private javax.swing.JLabel status_label; + // End of variables declaration//GEN-END:variables +} diff --git a/src/megabasterd/MainPanel.java b/src/megabasterd/MainPanel.java index 9bf22be60..40203a71c 100644 --- a/src/megabasterd/MainPanel.java +++ b/src/megabasterd/MainPanel.java @@ -47,6 +47,7 @@ import static megabasterd.MiscTools.bin2i32a; import static megabasterd.MiscTools.createAndRegisterFont; import static megabasterd.MiscTools.setNimbusLookAndFeel; import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; import static megabasterd.Transference.LIMIT_TRANSFERENCE_SPEED_DEFAULT; import static megabasterd.Transference.MAX_TRANSFERENCE_SPEED_DEFAULT; @@ -58,7 +59,7 @@ import static megabasterd.Transference.MAX_TRANSFERENCE_SPEED_DEFAULT; */ public final class MainPanel { - public static final String VERSION="1.12"; + public static final String VERSION="1.13"; public static final int CONNECTION_TIMEOUT = 30000; public static final int THROTTLE_SLICE_SIZE=16*1024; public static final int STREAMER_PORT = 1337; @@ -94,7 +95,8 @@ public final class MainPanel { private TrayIcon _trayicon; private final ClipboardSpy _clipboardspy; private KissVideoStreamServer _streamserver; - + private byte[] _mega_master_pass; + private String _mega_master_pass_hash; public MainPanel() { if(checkAppIsRunning()) { @@ -145,8 +147,44 @@ public final class MainPanel { } catch (IOException ex) { getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); } + + _mega_master_pass = null; } - + + public String getMega_master_pass_hash() { + return _mega_master_pass_hash; + } + + public void setMega_master_pass_hash(String mega_master_pass_hash) { + _mega_master_pass_hash = mega_master_pass_hash; + } + + + public byte[] getMega_master_pass() { + return _mega_master_pass; + } + + public void setMega_master_pass(byte[] pass) { + + if(_mega_master_pass != null) { + + for(byte b:_mega_master_pass) { + + b = 0; + } + } + + _mega_master_pass = pass; + + if(pass != null) { + + for(byte b:pass) { + + b = 0; + } + } + } + public MainPanelView getView() { return _view == null?(_view = new MainPanelView(this)):_view; @@ -336,7 +374,8 @@ public final class MainPanel { } catch (SQLException ex) { getLogger(MainPanel.class.getName()).log(SEVERE, null, ex); } - + + _mega_master_pass_hash = DBTools.selectSettingValueFromDB("mega_master_pass_hash"); } public void _byebye() { @@ -474,8 +513,6 @@ public final class MainPanel { if(conta_downloads>0) { - swingReflectionInvoke("setText", getView().getStatus_down_label(), "Starting downloads provisioning, please wait..."); - getDownload_manager().secureNotify(); getView().getjTabbedPane1().setSelectedIndex(0); @@ -582,12 +619,15 @@ public final class MainPanel { int conta_uploads = 0; + boolean remember_pass = true; + ArrayList> res = selectUploads(); - - for(HashMap o:res) { + try{ + + String email = (String)o.get("email"); MegaAPI ma; @@ -600,17 +640,64 @@ public final class MainPanel { if(ma == null) { - try { - + ma = new MegaAPI(); - ma.login(email, bin2i32a(BASE642Bin((String)account_info.get("password_aes"))), (String)account_info.get("user_hash")); + String password_aes, user_hash; - _mega_active_accounts.put(email, ma); - - } catch (Exception ex) { - getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); - } + if(getMega_master_pass_hash() != null) { + + if(getMega_master_pass() == null) { + + getView().getjTabbedPane1().setSelectedIndex(1); + + GetMegaMasterPasswordDialog dialog = new GetMegaMasterPasswordDialog(getView(), true, getMega_master_pass_hash()); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, getView()); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + if(dialog.isPass_ok()) { + + setMega_master_pass(dialog.getPass()); + + dialog.deletePass(); + + remember_pass = dialog.getRemember_checkbox().isSelected(); + + dialog.dispose(); + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("password_aes")), getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("user_hash")), getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } else { + + dialog.dispose(); + + throw new Exception(); + } + + } else { + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("password_aes")), getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)account_info.get("user_hash")), getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } + + } else { + + password_aes = (String)account_info.get("password_aes"); + + user_hash = (String)account_info.get("user_hash"); + } + + ma.fastLogin(email, MiscTools.bin2i32a(MiscTools.BASE642Bin(password_aes)), user_hash); + + _mega_active_accounts.put(email, ma); + + } Upload upload = new Upload(tthis, ma, (String)o.get("filename"), (String)o.get("parent_node"), (String)o.get("ul_key")!=null?bin2i32a(BASE642Bin((String)o.get("ul_key"))):null, (String)o.get("url"), (String)o.get("root_node"), BASE642Bin((String)o.get("share_key")), (String)o.get("folder_link"), _use_slots_up, _default_slots_up, false); @@ -623,17 +710,24 @@ public final class MainPanel { deleteUpload((String)o.get("filename"), email); } + + } catch (Exception ex) { + getLogger(MainPanelView.class.getName()).log(SEVERE, null, ex); + } } if(conta_uploads>0) { - - swingReflectionInvoke("setText", getView().getStatus_up_label(), "Starting uploads provisioning, please wait..."); - + getUpload_manager().secureNotify(); getView().getjTabbedPane1().setSelectedIndex(1); - } + } + + if(!remember_pass) { + + setMega_master_pass(null); + } swingReflectionInvoke("setText", getView().getStatus_up_label(), ""); diff --git a/src/megabasterd/MainPanelView.java b/src/megabasterd/MainPanelView.java index 40e41a5fb..9c8f74f1e 100644 --- a/src/megabasterd/MainPanelView.java +++ b/src/megabasterd/MainPanelView.java @@ -29,8 +29,6 @@ import static megabasterd.DBTools.deleteMegaAccount; import static megabasterd.MainPanel.FONT_DEFAULT; import static megabasterd.MainPanel.ICON_FILE; import static megabasterd.MainPanel.VERSION; -import static megabasterd.MiscTools.BASE642Bin; -import static megabasterd.MiscTools.bin2i32a; import static megabasterd.MiscTools.findAllRegex; import static megabasterd.MiscTools.findFirstRegex; import static megabasterd.MiscTools.genID; @@ -629,6 +627,11 @@ public final class MainPanelView extends javax.swing.JFrame { _main_panel.getUpload_manager().secureNotify(); } + + if(!dialog.isRemember_master_pass()) { + + _main_panel.setMega_master_pass(null); + } dialog.dispose(); }//GEN-LAST:event_settings_menuActionPerformed @@ -714,30 +717,20 @@ public final class MainPanelView extends javax.swing.JFrame { final String mega_account = (String)dialog.getAccount_combobox().getSelectedItem(); - HashMap data_account = (HashMap)_main_panel.getMega_accounts().get(mega_account); - final String base_path = dialog.getBase_path(); final String dir_name=dialog.getDir_name_textfield().getText(); - final int[] mega_aes_pass = bin2i32a(BASE642Bin((String)data_account.get("password_aes"))); - - final String mega_user_hash = (String)data_account.get("user_hash"); - jTabbedPane1.setSelectedIndex(1); Runnable run = new Runnable(){ @Override public void run() { - MegaAPI ma=getMain_panel().getMega_active_accounts().get(mega_account)!=null?getMain_panel().getMega_active_accounts().get(mega_account):new MegaAPI(); + MegaAPI ma=getMain_panel().getMega_active_accounts().get(mega_account); try { - - ma.login(mega_account, mega_aes_pass, mega_user_hash); - - getMain_panel().getMega_active_accounts().put(mega_account, ma); - + byte[] parent_key = ma.genFolderKey(); byte[] share_key = ma.genShareKey(); @@ -820,6 +813,11 @@ public final class MainPanelView extends javax.swing.JFrame { }catch(Exception ex) {} + if(!dialog.isRemember_master_pass()) { + + _main_panel.setMega_master_pass(null); + } + dialog.dispose(); }//GEN-LAST:event_new_upload_menuActionPerformed diff --git a/src/megabasterd/MegaAPI.java b/src/megabasterd/MegaAPI.java index f59a47e4b..e55f84851 100644 --- a/src/megabasterd/MegaAPI.java +++ b/src/megabasterd/MegaAPI.java @@ -171,7 +171,7 @@ public final class MegaAPI { _realLogin(); } - public void login(String email, int[] password_aes, String user_hash) throws Exception, MegaAPIException { + public void fastLogin(String email, int[] password_aes, String user_hash) throws Exception, MegaAPIException { _email = email; diff --git a/src/megabasterd/MegaCrypterAPI.java b/src/megabasterd/MegaCrypterAPI.java index a79fb3deb..5de22d991 100644 --- a/src/megabasterd/MegaCrypterAPI.java +++ b/src/megabasterd/MegaCrypterAPI.java @@ -7,7 +7,6 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; import java.util.Arrays; import java.util.HashMap; import java.util.logging.Level; @@ -16,8 +15,6 @@ import java.util.zip.GZIPInputStream; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; import javax.swing.JOptionPane; import static megabasterd.MiscTools.BASE642Bin; import static megabasterd.MiscTools.Bin2BASE64; @@ -225,13 +222,9 @@ public final class MegaCrypterAPI { if(password!=null) { - SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); - - KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, (int)Math.pow(2, iterations), 256); - try { - info_key=f.generateSecret(ks).getEncoded(); + info_key=CryptTools.PBKDF2HMACSHA256(password, salt, (int)Math.pow(2, iterations)); decrypter = CryptTools.genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv); diff --git a/src/megabasterd/MiscTools.java b/src/megabasterd/MiscTools.java index 0b1434852..294fe0a5e 100644 --- a/src/megabasterd/MiscTools.java +++ b/src/megabasterd/MiscTools.java @@ -58,7 +58,7 @@ public final class MiscTools { public static final int EXP_BACKOFF_SECS_RETRY=1; public static final int EXP_BACKOFF_MAX_WAIT_TIME=128; private static final ConcurrentHashMap REFLECTION_METHOD_CACHE = new ConcurrentHashMap<>(); - private static final Comparator _treeNodeComparator = new Comparator< DefaultMutableTreeNode>() { + private static final Comparator TREE_NODE_COMPARATOR = new Comparator< DefaultMutableTreeNode>() { @Override public int compare(DefaultMutableTreeNode a, DefaultMutableTreeNode b) { @@ -597,7 +597,7 @@ public final class MiscTools { children.add((DefaultMutableTreeNode) parent.getChildAt(i)); } - Collections.sort(children, _treeNodeComparator); + Collections.sort(children, TREE_NODE_COMPARATOR); parent.removeAllChildren(); @@ -838,7 +838,6 @@ public final class MiscTools { } - private MiscTools() { } diff --git a/src/megabasterd/SetMegaMasterPasswordDialog.form b/src/megabasterd/SetMegaMasterPasswordDialog.form new file mode 100644 index 000000000..5aefba9ec --- /dev/null +++ b/src/megabasterd/SetMegaMasterPasswordDialog.form @@ -0,0 +1,173 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/megabasterd/SetMegaMasterPasswordDialog.java b/src/megabasterd/SetMegaMasterPasswordDialog.java new file mode 100644 index 000000000..bdad5bfae --- /dev/null +++ b/src/megabasterd/SetMegaMasterPasswordDialog.java @@ -0,0 +1,262 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package megabasterd; + +import java.awt.Font; +import java.awt.event.KeyEvent; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; +import static megabasterd.MainPanel.FONT_DEFAULT; +import static megabasterd.MainPanel.THREAD_POOL; +import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.updateFont; + + +/** + * + * @author tonikelope + */ +public class SetMegaMasterPasswordDialog extends javax.swing.JDialog { + + private boolean _pass_ok; + + private String _new_pass_hash; + + private byte[] _new_pass; + + public boolean isPass_ok() { + return _pass_ok; + } + + public byte[] getNew_pass() { + return _new_pass; + } + + public void deletePass() { + + if(_new_pass != null) { + + for(byte c:_new_pass) { + + c = 0; + } + } + + _new_pass = null; + } + + public String getNew_pass_hash() { + return _new_pass_hash; + } + + /** + * Creates new form MegaPassDialog + */ + public SetMegaMasterPasswordDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + + updateFont(new_pass_label, FONT_DEFAULT, Font.PLAIN); + updateFont(confirm_pass_label, FONT_DEFAULT, Font.PLAIN); + updateFont(ok_button, FONT_DEFAULT, Font.PLAIN); + updateFont(cancel_button, FONT_DEFAULT, Font.PLAIN); + updateFont(warning_label, FONT_DEFAULT, Font.PLAIN); + updateFont(status_label, FONT_DEFAULT, Font.PLAIN); + + _pass_ok = false; + + _new_pass = null; + + _new_pass_hash = null; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + confirm_pass_textfield = new javax.swing.JPasswordField(); + confirm_pass_label = new javax.swing.JLabel(); + cancel_button = new javax.swing.JButton(); + ok_button = new javax.swing.JButton(); + lock_label = new javax.swing.JLabel(); + warning_label = new javax.swing.JLabel(); + new_pass_label = new javax.swing.JLabel(); + new_pass_textfield = new javax.swing.JPasswordField(); + status_label = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("MegaMasterPassword"); + setResizable(false); + + confirm_pass_textfield.setFont(new java.awt.Font("Dialog", 0, 18)); // NOI18N + confirm_pass_textfield.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyPressed(java.awt.event.KeyEvent evt) { + confirm_pass_textfieldKeyPressed(evt); + } + }); + + confirm_pass_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + confirm_pass_label.setText("Confirm new:"); + + cancel_button.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + cancel_button.setText("CANCEL"); + cancel_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancel_buttonActionPerformed(evt); + } + }); + + ok_button.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + ok_button.setText("OK"); + ok_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + ok_buttonActionPerformed(evt); + } + }); + + lock_label.setIcon(new javax.swing.ImageIcon(getClass().getResource("/megabasterd/lock_medium.png"))); // NOI18N + + warning_label.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + warning_label.setText("WARNING: if you forget this password, you will have to insert all your MEGA accounts again."); + + new_pass_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N + new_pass_label.setText("New pass:"); + + new_pass_textfield.setFont(new java.awt.Font("Dialog", 0, 18)); // NOI18N + + status_label.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lock_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(confirm_pass_label) + .addComponent(new_pass_label)) + .addGap(15, 15, 15) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(confirm_pass_textfield) + .addComponent(new_pass_textfield)) + .addContainerGap()) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(status_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ok_button) + .addGap(18, 18, 18) + .addComponent(cancel_button) + .addGap(15, 15, 15)) + .addGroup(layout.createSequentialGroup() + .addComponent(warning_label, javax.swing.GroupLayout.PREFERRED_SIZE, 577, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 12, Short.MAX_VALUE)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lock_label) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(new_pass_textfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(new_pass_label)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(confirm_pass_textfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(confirm_pass_label)))) + .addGap(18, 18, 18) + .addComponent(warning_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(ok_button) + .addComponent(cancel_button)) + .addComponent(status_label, javax.swing.GroupLayout.Alignment.TRAILING)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void cancel_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancel_buttonActionPerformed + + swingReflectionInvoke("setVisible", this, false); + }//GEN-LAST:event_cancel_buttonActionPerformed + + private void ok_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ok_buttonActionPerformed + + swingReflectionInvoke("setText", status_label, "Verifying your password, please wait..."); + + final SetMegaMasterPasswordDialog tthis = this; + + THREAD_POOL.execute(new Runnable(){ + + @Override + public void run() { + + try { + + if(Arrays.equals(new_pass_textfield.getPassword(), confirm_pass_textfield.getPassword())) { + + swingReflectionInvoke("setText", status_label, "Processing your password, please wait..."); + + if(new_pass_textfield.getPassword().length > 0) { + + _new_pass = CryptTools.PBKDF2HMACSHA256(new String(new_pass_textfield.getPassword()), CryptTools.PBKDF2_SALT, CryptTools.PBKDF2_ITERATIONS); + + _new_pass_hash = MiscTools.Bin2BASE64(MiscTools.HashBin("SHA-1", _new_pass)); + } + + _pass_ok = true; + + swingReflectionInvoke("setVisible", tthis, false); + + } else { + + JOptionPane.showMessageDialog(tthis, "Passwords does not match!", "Error", JOptionPane.ERROR_MESSAGE); + + swingReflectionInvoke("setText", status_label, ""); + } + + } catch (Exception ex) { + Logger.getLogger(SetMegaMasterPasswordDialog.class.getName()).log(Level.SEVERE, null, ex); + } + }}); + }//GEN-LAST:event_ok_buttonActionPerformed + + private void confirm_pass_textfieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_confirm_pass_textfieldKeyPressed + + if(evt.getKeyCode() == KeyEvent.VK_ENTER) { + + ok_buttonActionPerformed(null); + } + }//GEN-LAST:event_confirm_pass_textfieldKeyPressed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cancel_button; + private javax.swing.JLabel confirm_pass_label; + private javax.swing.JPasswordField confirm_pass_textfield; + private javax.swing.JLabel lock_label; + private javax.swing.JLabel new_pass_label; + private javax.swing.JPasswordField new_pass_textfield; + private javax.swing.JButton ok_button; + private javax.swing.JLabel status_label; + private javax.swing.JLabel warning_label; + // End of variables declaration//GEN-END:variables +} diff --git a/src/megabasterd/SettingsDialog.form b/src/megabasterd/SettingsDialog.form index 0d55bd3fa..db493da17 100644 --- a/src/megabasterd/SettingsDialog.form +++ b/src/megabasterd/SettingsDialog.form @@ -29,7 +29,7 @@ - + @@ -42,24 +42,16 @@ - + - - - - - - - - - - - - - - - + + + + + + + @@ -181,7 +173,7 @@ - + @@ -316,10 +308,9 @@ - + - @@ -341,13 +332,22 @@ - + + + + + + + + + + @@ -357,8 +357,13 @@ - - + + + + + + + @@ -384,7 +389,7 @@ - + @@ -397,7 +402,7 @@ - + @@ -526,6 +531,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -533,7 +583,7 @@ - + diff --git a/src/megabasterd/SettingsDialog.java b/src/megabasterd/SettingsDialog.java index 5ddb4a5d1..666dd2d5c 100644 --- a/src/megabasterd/SettingsDialog.java +++ b/src/megabasterd/SettingsDialog.java @@ -1,25 +1,34 @@ package megabasterd; import java.awt.Font; +import java.awt.Frame; import java.io.File; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; +import java.util.logging.Logger; import static java.util.logging.Logger.getLogger; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.showOptionDialog; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.table.DefaultTableModel; import static megabasterd.DBTools.insertSettingValueInDB; import static megabasterd.MainPanel.FONT_DEFAULT; import static megabasterd.MainPanel.THREAD_POOL; -import static megabasterd.MiscTools.Bin2BASE64; -import static megabasterd.MiscTools.i32a2bin; import static megabasterd.MiscTools.swingReflectionInvoke; +import static megabasterd.MiscTools.swingReflectionInvokeAndWait; import static megabasterd.MiscTools.swingReflectionInvokeAndWaitForReturn; import static megabasterd.MiscTools.truncateText; import static megabasterd.MiscTools.updateFont; @@ -31,6 +40,7 @@ public final class SettingsDialog extends javax.swing.JDialog { private boolean _settings_ok; private final Set _deleted_accounts; private final MainPanel _main_panel; + private boolean _remember_master_pass; public boolean isSettings_ok() { return _settings_ok; @@ -39,8 +49,10 @@ public final class SettingsDialog extends javax.swing.JDialog { public Set getDeleted_accounts() { return _deleted_accounts; } - - + + public boolean isRemember_master_pass() { + return _remember_master_pass; + } /** * Creates new form Settings @@ -71,16 +83,16 @@ public final class SettingsDialog extends javax.swing.JDialog { updateFont(accounts_label, FONT_DEFAULT, Font.PLAIN); updateFont(defaut_slots_up_label, FONT_DEFAULT, Font.PLAIN); updateFont(max_uploads_label, FONT_DEFAULT, Font.PLAIN); - updateFont(max_down_speed_label, FONT_DEFAULT, Font.PLAIN); updateFont(limit_download_speed_checkbox, FONT_DEFAULT, Font.PLAIN); updateFont(max_down_speed_spinner, FONT_DEFAULT, Font.PLAIN); - updateFont(max_up_speed_spinner, FONT_DEFAULT, Font.PLAIN); updateFont(max_up_speed_label, FONT_DEFAULT, Font.PLAIN); updateFont(limit_upload_speed_checkbox, FONT_DEFAULT, Font.PLAIN); - updateFont(default_dir_label, FONT_DEFAULT, Font.PLAIN); + updateFont(encrypt_pass_checkbox, FONT_DEFAULT, Font.PLAIN); + updateFont(unlock_accounts_button, FONT_DEFAULT, Font.PLAIN); + updateFont(delete_all_accounts_button, FONT_DEFAULT, Font.PLAIN); _main_panel = ((MainPanelView)parent).getMain_panel(); @@ -256,20 +268,71 @@ public final class SettingsDialog extends javax.swing.JDialog { swingReflectionInvoke("setEnabled", max_uploads_label, true); swingReflectionInvoke("setEnabled", default_slots_up, true); } - - DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); - for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { - - HashMap data = (HashMap)pair.getValue(); + encrypt_pass_checkbox.setSelected((_main_panel.getMega_master_pass_hash() != null)); - String[] new_row_data = {(String)pair.getKey(), (String)data.get("password")}; + swingReflectionInvoke("setEnabled", remove_account_button, (mega_accounts_table.getRowCount()>0)); + + DefaultTableModel model = (DefaultTableModel)mega_accounts_table.getModel(); + + if(_main_panel.getMega_master_pass_hash() != null) { - model.addRow(new_row_data); - } + if(_main_panel.getMega_master_pass() == null) { + + swingReflectionInvoke("setEnabled", encrypt_pass_checkbox, false); + + swingReflectionInvoke("setEnabled", mega_accounts_table, false); + + swingReflectionInvoke("setEnabled", remove_account_button, false); + + swingReflectionInvoke("setEnabled", add_account_button, false); + + swingReflectionInvoke("setVisible", unlock_accounts_button, true); + + } else { + + swingReflectionInvoke("setVisible", unlock_accounts_button, false); + + for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { + + HashMap data = (HashMap)pair.getValue(); + + String pass = null; + + try { + + pass = new String(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)data.get("password")), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } catch (Exception ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + String[] new_row_data = {(String)pair.getKey(), pass}; + + model.addRow(new_row_data); + } + } - swingReflectionInvoke("setEnabled", remove_account_button, (jTable1.getRowCount()>0)); + } else { + + swingReflectionInvoke("setVisible", unlock_accounts_button, false); + + for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { + HashMap data = (HashMap)pair.getValue(); + + String[] new_row_data = {(String)pair.getKey(), (String)data.get("password")}; + + model.addRow(new_row_data); + } + } + + _remember_master_pass = true; + + swingReflectionInvoke("setEnabled", remove_account_button, (mega_accounts_table.getRowCount()>0)); + _deleted_accounts = new HashSet(); _settings_ok = false; @@ -303,7 +366,7 @@ public final class SettingsDialog extends javax.swing.JDialog { default_dir_label = new javax.swing.JLabel(); uploads_panel = new javax.swing.JPanel(); accounts_scrollpane = new javax.swing.JScrollPane(); - jTable1 = new javax.swing.JTable(); + mega_accounts_table = new javax.swing.JTable(); accounts_label = new javax.swing.JLabel(); remove_account_button = new javax.swing.JButton(); add_account_button = new javax.swing.JButton(); @@ -315,6 +378,9 @@ public final class SettingsDialog extends javax.swing.JDialog { max_up_speed_label = new javax.swing.JLabel(); max_up_speed_spinner = new javax.swing.JSpinner(); limit_upload_speed_checkbox = new javax.swing.JCheckBox(); + encrypt_pass_checkbox = new javax.swing.JCheckBox(); + unlock_accounts_button = new javax.swing.JButton(); + delete_all_accounts_button = new javax.swing.JButton(); status = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -457,13 +523,13 @@ public final class SettingsDialog extends javax.swing.JDialog { .addComponent(max_down_speed_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(18, 18, 18) .addComponent(verify_file_down_checkbox) - .addContainerGap(221, Short.MAX_VALUE)) + .addContainerGap(225, Short.MAX_VALUE)) ); jTabbedPane1.addTab("Downloads", downloads_panel); - jTable1.setFont(new java.awt.Font("Dialog", 0, 18)); // NOI18N - jTable1.setModel(new javax.swing.table.DefaultTableModel( + mega_accounts_table.setFont(new java.awt.Font("Dialog", 0, 18)); // NOI18N + mega_accounts_table.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { }, @@ -471,10 +537,10 @@ public final class SettingsDialog extends javax.swing.JDialog { "Email", "Password" } )); - jTable1.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); - jTable1.setDoubleBuffered(true); - jTable1.setRowHeight(24); - accounts_scrollpane.setViewportView(jTable1); + mega_accounts_table.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + mega_accounts_table.setDoubleBuffered(true); + mega_accounts_table.setRowHeight(24); + accounts_scrollpane.setViewportView(mega_accounts_table); accounts_label.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N accounts_label.setText("Your MEGA accounts:"); @@ -536,6 +602,34 @@ public final class SettingsDialog extends javax.swing.JDialog { } }); + encrypt_pass_checkbox.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + encrypt_pass_checkbox.setText("Encrypt passwords (on disk)"); + encrypt_pass_checkbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + encrypt_pass_checkboxActionPerformed(evt); + } + }); + + unlock_accounts_button.setBackground(new java.awt.Color(0, 153, 51)); + unlock_accounts_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + unlock_accounts_button.setForeground(new java.awt.Color(255, 255, 255)); + unlock_accounts_button.setText("Unlock"); + unlock_accounts_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + unlock_accounts_buttonActionPerformed(evt); + } + }); + + delete_all_accounts_button.setBackground(new java.awt.Color(255, 51, 0)); + delete_all_accounts_button.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + delete_all_accounts_button.setForeground(new java.awt.Color(255, 255, 255)); + delete_all_accounts_button.setText("RESET ACCOUNTS"); + delete_all_accounts_button.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + delete_all_accounts_buttonActionPerformed(evt); + } + }); + javax.swing.GroupLayout uploads_panelLayout = new javax.swing.GroupLayout(uploads_panel); uploads_panel.setLayout(uploads_panelLayout); uploads_panelLayout.setHorizontalGroup( @@ -543,10 +637,9 @@ public final class SettingsDialog extends javax.swing.JDialog { .addGroup(uploads_panelLayout.createSequentialGroup() .addContainerGap() .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(accounts_scrollpane, javax.swing.GroupLayout.DEFAULT_SIZE, 924, Short.MAX_VALUE) + .addComponent(accounts_scrollpane) .addGroup(uploads_panelLayout.createSequentialGroup() .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(accounts_label) .addGroup(uploads_panelLayout.createSequentialGroup() .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(defaut_slots_up_label) @@ -562,18 +655,30 @@ public final class SettingsDialog extends javax.swing.JDialog { .addGap(123, 123, 123) .addComponent(max_uploads_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(multi_slot_up_checkbox)) - .addGap(0, 0, Short.MAX_VALUE)) + .addGap(0, 110, Short.MAX_VALUE)) .addGroup(uploads_panelLayout.createSequentialGroup() .addComponent(remove_account_button) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(add_account_button))) + .addComponent(add_account_button)) + .addGroup(uploads_panelLayout.createSequentialGroup() + .addComponent(accounts_label) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(unlock_accounts_button) + .addGap(18, 18, 18) + .addComponent(delete_all_accounts_button) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(encrypt_pass_checkbox))) .addContainerGap()) ); uploads_panelLayout.setVerticalGroup( uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(uploads_panelLayout.createSequentialGroup() .addGap(6, 6, 6) - .addComponent(accounts_label) + .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(accounts_label) + .addComponent(encrypt_pass_checkbox) + .addComponent(unlock_accounts_button) + .addComponent(delete_all_accounts_button)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(accounts_scrollpane, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -596,12 +701,12 @@ public final class SettingsDialog extends javax.swing.JDialog { .addGroup(uploads_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(max_up_speed_label) .addComponent(max_up_speed_spinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(25, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jTabbedPane1.addTab("Uploads", uploads_panel); - status.setFont(new java.awt.Font("Dialog", 1, 18)); // NOI18N + status.setFont(new java.awt.Font("Dialog", 1, 20)); // NOI18N status.setDoubleBuffered(true); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); @@ -613,7 +718,7 @@ public final class SettingsDialog extends javax.swing.JDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jTabbedPane1) .addGroup(layout.createSequentialGroup() - .addComponent(status, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(status, javax.swing.GroupLayout.PREFERRED_SIZE, 657, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(ok_button, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) @@ -624,18 +729,13 @@ public final class SettingsDialog extends javax.swing.JDialog { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(ok_button) - .addComponent(cancel_button)) - .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(status) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 598, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(ok_button) + .addComponent(cancel_button) + .addComponent(status)) + .addContainerGap()) ); pack(); @@ -664,7 +764,6 @@ public final class SettingsDialog extends javax.swing.JDialog { private void cancel_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancel_buttonActionPerformed - swingReflectionInvoke("setVisible", this, false); }//GEN-LAST:event_cancel_buttonActionPerformed @@ -672,7 +771,6 @@ public final class SettingsDialog extends javax.swing.JDialog { try { - _settings_ok = true; insertSettingValueInDB("default_down_dir", _download_path); @@ -689,7 +787,7 @@ public final class SettingsDialog extends javax.swing.JDialog { insertSettingValueInDB("max_upload_speed", String.valueOf((int)swingReflectionInvokeAndWaitForReturn("getValue", max_up_speed_spinner))); - final DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); + final DefaultTableModel model = (DefaultTableModel)mega_accounts_table.getModel(); swingReflectionInvoke("setText", status, "Checking your MEGA accounts, please wait..."); @@ -701,9 +799,14 @@ public final class SettingsDialog extends javax.swing.JDialog { swingReflectionInvoke("setEnabled", add_account_button, false); - swingReflectionInvoke("setEnabled", jTable1, false); + swingReflectionInvoke("setEnabled", delete_all_accounts_button, false); - final SettingsDialog dialog = this; + swingReflectionInvoke("setEnabled", mega_accounts_table, false); + + swingReflectionInvoke("setEnabled", encrypt_pass_checkbox, false); + + + final SettingsDialog tthis = this; THREAD_POOL.execute(new Runnable(){ @Override @@ -721,16 +824,27 @@ public final class SettingsDialog extends javax.swing.JDialog { MegaAPI ma; - if(dialog._main_panel.getMega_accounts().get(email) == null){ + if(_main_panel.getMega_accounts().get(email) == null){ ma = new MegaAPI(); try { ma.login(email, pass); - dialog._main_panel.getMega_active_accounts().put(email, ma); + _main_panel.getMega_active_accounts().put(email, ma); + + String password=pass, password_aes=MiscTools.Bin2BASE64(MiscTools.i32a2bin(ma.getPassword_aes())), user_hash=ma.getUser_hash(); + + if(_main_panel.getMega_master_pass() != null) { + + password = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(pass.getBytes(), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(MiscTools.i32a2bin(ma.getPassword_aes()), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(ma.getUser_hash().getBytes(), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + } - DBTools.insertMegaAccount(email, pass, Bin2BASE64(i32a2bin(ma.getPassword_aes())), ma.getUser_hash()); + DBTools.insertMegaAccount(email, password, password_aes, user_hash); } catch(Exception ex) { @@ -740,18 +854,46 @@ public final class SettingsDialog extends javax.swing.JDialog { } else { - HashMap mega_account_data = (HashMap)dialog._main_panel.getMega_accounts().get(email); + HashMap mega_account_data = (HashMap)_main_panel.getMega_accounts().get(email); - if(!mega_account_data.get("password").equals(pass)) { + String password; + + password = (String)mega_account_data.get("password"); + + if(_main_panel.getMega_master_pass() != null) { + + try { + + password = new String(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin(password), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } catch (Exception ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if(!password.equals(pass)) { ma = new MegaAPI(); try { ma.login(email, pass); - dialog._main_panel.getMega_active_accounts().put(email, ma); + _main_panel.getMega_active_accounts().put(email, ma); + + password = pass; + + String password_aes=MiscTools.Bin2BASE64(MiscTools.i32a2bin(ma.getPassword_aes())), user_hash=ma.getUser_hash(); + + if(_main_panel.getMega_master_pass() != null) { + + password = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(pass.getBytes(), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(MiscTools.i32a2bin(ma.getPassword_aes()), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(ma.getUser_hash().getBytes(), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + } - DBTools.insertMegaAccount(email, pass, Bin2BASE64(i32a2bin(ma.getPassword_aes())), ma.getUser_hash()); + DBTools.insertMegaAccount(email, password, password_aes, user_hash); } catch (Exception ex) { @@ -773,24 +915,28 @@ public final class SettingsDialog extends javax.swing.JDialog { email_error_s+=s+"\n"; } - swingReflectionInvoke("setText", dialog.status, ""); + swingReflectionInvoke("setText", status, ""); - JOptionPane.showMessageDialog(dialog, "There were errors with some accounts. Please, check them:\n\n"+email_error_s); + JOptionPane.showMessageDialog(tthis, "There were errors with some accounts. Please, check them:\n\n"+email_error_s, "Error", JOptionPane.ERROR_MESSAGE); - swingReflectionInvoke("setEnabled", dialog.ok_button, true); + swingReflectionInvoke("setEnabled", ok_button, true); - swingReflectionInvoke("setEnabled", dialog.cancel_button, true); + swingReflectionInvoke("setEnabled", cancel_button, true); - swingReflectionInvoke("setEnabled", dialog.remove_account_button, true); + swingReflectionInvoke("setEnabled", remove_account_button, true); - swingReflectionInvoke("setEnabled", dialog.add_account_button, true); + swingReflectionInvoke("setEnabled", add_account_button, true); - swingReflectionInvoke("setEnabled", dialog.jTable1, true); + swingReflectionInvoke("setEnabled", mega_accounts_table, true); + + swingReflectionInvoke("setEnabled", delete_all_accounts_button, true); + + swingReflectionInvoke("setEnabled", encrypt_pass_checkbox, true); } else { - swingReflectionInvoke("setVisible", dialog, false); + swingReflectionInvoke("setVisible", tthis, false); } - + } }); } catch (SQLException ex) { @@ -812,24 +958,23 @@ public final class SettingsDialog extends javax.swing.JDialog { }//GEN-LAST:event_multi_slot_down_checkboxStateChanged private void remove_account_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_remove_account_buttonActionPerformed - + + DefaultTableModel model = (DefaultTableModel)mega_accounts_table.getModel(); - DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); - - int selected = jTable1.getSelectedRow(); + int selected = mega_accounts_table.getSelectedRow(); while(selected >= 0) { - String email = (String)model.getValueAt(jTable1.convertRowIndexToModel(selected),0); + String email = (String)model.getValueAt(mega_accounts_table.convertRowIndexToModel(selected),0); _deleted_accounts.add(email); - model.removeRow(jTable1.convertRowIndexToModel(selected)); + model.removeRow(mega_accounts_table.convertRowIndexToModel(selected)); - selected = jTable1.getSelectedRow(); + selected = mega_accounts_table.getSelectedRow(); } - jTable1.clearSelection(); + mega_accounts_table.clearSelection(); if(model.getRowCount() == 0) { @@ -839,12 +984,11 @@ public final class SettingsDialog extends javax.swing.JDialog { private void add_account_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_add_account_buttonActionPerformed - - DefaultTableModel model = (DefaultTableModel)jTable1.getModel(); + DefaultTableModel model = (DefaultTableModel)mega_accounts_table.getModel(); model.addRow(new Object[]{"",""}); - jTable1.clearSelection(); + mega_accounts_table.clearSelection(); swingReflectionInvoke("setEnabled", ok_button, true); }//GEN-LAST:event_add_account_buttonActionPerformed @@ -888,8 +1032,201 @@ public final class SettingsDialog extends javax.swing.JDialog { } }//GEN-LAST:event_limit_upload_speed_checkboxStateChanged - - + private void encrypt_pass_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_encrypt_pass_checkboxActionPerformed + + SetMegaMasterPasswordDialog dialog = new SetMegaMasterPasswordDialog((Frame)getParent(),true); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + byte[] old_mega_master_pass = _main_panel.getMega_master_pass(); + + if(dialog.isPass_ok()) { + + try { + + if(dialog.getNew_pass() != null && dialog.getNew_pass().length > 0) { + + _main_panel.setMega_master_pass_hash(dialog.getNew_pass_hash()); + + _main_panel.setMega_master_pass(dialog.getNew_pass()); + + } else { + + _main_panel.setMega_master_pass_hash(null); + + _main_panel.setMega_master_pass(null); + } + + dialog.deletePass(); + + insertSettingValueInDB("mega_master_pass_hash", _main_panel.getMega_master_pass_hash()); + + for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { + + HashMap data = (HashMap)pair.getValue(); + + String email, password, password_aes, user_hash; + + email = (String)pair.getKey(); + + if(old_mega_master_pass != null) { + + password = new String(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)data.get("password")) , old_mega_master_pass, CryptTools.AES_ZERO_IV)); + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)data.get("password_aes")), old_mega_master_pass, CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)data.get("user_hash")), old_mega_master_pass, CryptTools.AES_ZERO_IV)); + + } else { + + password = (String)data.get("password"); + + password_aes = (String)data.get("password_aes"); + + user_hash = (String)data.get("user_hash"); + } + + if(_main_panel.getMega_master_pass() != null) { + + password = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(password.getBytes(), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + password_aes = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(MiscTools.BASE642Bin(password_aes), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + user_hash = MiscTools.Bin2BASE64(CryptTools.aes_cbc_encrypt_pkcs7(MiscTools.BASE642Bin(user_hash), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + } + + data.put("password", password); + + data.put("password_aes", password_aes); + + data.put("user_hash", user_hash); + + DBTools.insertMegaAccount(email, password, password_aes, user_hash); + } + + } catch (Exception ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + } + + encrypt_pass_checkbox.setSelected((_main_panel.getMega_master_pass_hash() != null)); + + dialog.dispose(); + + }//GEN-LAST:event_encrypt_pass_checkboxActionPerformed + + private void unlock_accounts_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unlock_accounts_buttonActionPerformed + + GetMegaMasterPasswordDialog dialog = new GetMegaMasterPasswordDialog((Frame)getParent(),true, _main_panel.getMega_master_pass_hash()); + + swingReflectionInvokeAndWait("setLocationRelativeTo", dialog, this); + + swingReflectionInvokeAndWait("setVisible", dialog, true); + + if(dialog.isPass_ok()) { + + _main_panel.setMega_master_pass(dialog.getPass()); + + dialog.deletePass(); + + DefaultTableModel model = (DefaultTableModel)mega_accounts_table.getModel(); + + swingReflectionInvoke("setEnabled", encrypt_pass_checkbox, true); + + swingReflectionInvoke("setEnabled", mega_accounts_table, true); + + swingReflectionInvoke("setEnabled", remove_account_button, true); + + swingReflectionInvoke("setEnabled", add_account_button, true); + + swingReflectionInvoke("setVisible", unlock_accounts_button, false); + + swingReflectionInvoke("setEnabled", delete_all_accounts_button, true); + + for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { + + HashMap data = (HashMap)pair.getValue(); + + String pass = null; + + try { + + pass = new String(CryptTools.aes_cbc_decrypt_pkcs7(MiscTools.BASE642Bin((String)data.get("password")), _main_panel.getMega_master_pass(), CryptTools.AES_ZERO_IV)); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } catch (Exception ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + + String[] new_row_data = {(String)pair.getKey(), pass}; + + model.addRow(new_row_data); + } + + } + + _remember_master_pass = dialog.getRemember_checkbox().isSelected(); + + dialog.dispose(); + }//GEN-LAST:event_unlock_accounts_buttonActionPerformed + + private void delete_all_accounts_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_delete_all_accounts_buttonActionPerformed + + Object[] options = {"No", + "Yes"}; + + int n = showOptionDialog(this, + "MEGA master password will be reset and all your MEGA accounts will be removed. (This can't be undone)\n\nDo you want to continue?", + "Warning!", YES_NO_CANCEL_OPTION, javax.swing.JOptionPane.WARNING_MESSAGE, + null, + options, + options[0]); + + if(n == 1) { + + try { + swingReflectionInvoke("setEnabled", encrypt_pass_checkbox, true); + + swingReflectionInvoke("setEnabled", mega_accounts_table, true); + + swingReflectionInvoke("setEnabled", remove_account_button, true); + + swingReflectionInvoke("setEnabled", add_account_button, true); + + swingReflectionInvoke("setVisible", unlock_accounts_button, false); + + swingReflectionInvoke("setVisible", delete_all_accounts_button, true); + + for (HashMap.Entry pair : _main_panel.getMega_accounts().entrySet()) { + + try { + DBTools.deleteMegaAccount((String) pair.getKey()); + } catch (SQLException ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + } + + _main_panel.setMega_master_pass_hash(null); + + insertSettingValueInDB("mega_master_pass_hash", _main_panel.getMega_master_pass_hash()); + + encrypt_pass_checkbox.setSelected(false); + + _main_panel.getMega_accounts().clear(); + + _main_panel.getMega_active_accounts().clear(); + + } catch (SQLException ex) { + Logger.getLogger(SettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + } + + }//GEN-LAST:event_delete_all_accounts_buttonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel accounts_label; private javax.swing.JScrollPane accounts_scrollpane; @@ -901,10 +1238,11 @@ public final class SettingsDialog extends javax.swing.JDialog { private javax.swing.JSpinner default_slots_down_spinner; private javax.swing.JSpinner default_slots_up; private javax.swing.JLabel defaut_slots_up_label; + private javax.swing.JButton delete_all_accounts_button; private javax.swing.JLabel down_dir_label; private javax.swing.JPanel downloads_panel; + private javax.swing.JCheckBox encrypt_pass_checkbox; private javax.swing.JTabbedPane jTabbedPane1; - private javax.swing.JTable jTable1; private javax.swing.JCheckBox limit_download_speed_checkbox; private javax.swing.JCheckBox limit_upload_speed_checkbox; private javax.swing.JLabel max_down_speed_label; @@ -915,11 +1253,13 @@ public final class SettingsDialog extends javax.swing.JDialog { private javax.swing.JSpinner max_up_speed_spinner; private javax.swing.JLabel max_uploads_label; private javax.swing.JSpinner max_uploads_spinner; + private javax.swing.JTable mega_accounts_table; private javax.swing.JCheckBox multi_slot_down_checkbox; private javax.swing.JCheckBox multi_slot_up_checkbox; private javax.swing.JButton ok_button; private javax.swing.JButton remove_account_button; private javax.swing.JLabel status; + private javax.swing.JButton unlock_accounts_button; private javax.swing.JPanel uploads_panel; private javax.swing.JCheckBox verify_file_down_checkbox; // End of variables declaration//GEN-END:variables diff --git a/src/megabasterd/StreamerDialog.java b/src/megabasterd/StreamerDialog.java index 5a9708d7a..76e7b41f8 100644 --- a/src/megabasterd/StreamerDialog.java +++ b/src/megabasterd/StreamerDialog.java @@ -165,7 +165,7 @@ public final class StreamerDialog extends javax.swing.JDialog implements Clipboa if(error) { - JOptionPane.showMessageDialog(tthis, "Please, paste a mega/megacrypter link!"); + JOptionPane.showMessageDialog(tthis, "Please, paste a mega/megacrypter link!", "Error", JOptionPane.ERROR_MESSAGE); swingReflectionInvoke("setText", original_link_textfield, ""); diff --git a/src/megabasterd/TransferenceManager.java b/src/megabasterd/TransferenceManager.java index db76bfc48..fec716e8c 100644 --- a/src/megabasterd/TransferenceManager.java +++ b/src/megabasterd/TransferenceManager.java @@ -291,7 +291,7 @@ abstract public class TransferenceManager implements Runnable, SecureNotifiable { if(!isPreprocessing_transferences() && !getTransference_preprocess_queue().isEmpty()) { - this.setPreprocessing_transferences(true); + setPreprocessing_transferences(true); THREAD_POOL.execute(new Runnable(){ @Override diff --git a/src/megabasterd/lock_medium.png b/src/megabasterd/lock_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..e7793d620b205273ae7150a077ec629718b9f493 GIT binary patch literal 7465 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 n{eDc4(f8aF#_0O(4(|U4ppWf{hq