/// <summary> /// Perform a verification of the restore code in the background by checking it with the servers /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void VerifyRestoreCode(object sender, DoWorkEventArgs e) { Authenticator auth = e.Argument as Authenticator; // check if this authenticator is too old to be restored try { Authenticator testrestore = new Authenticator(); testrestore.Restore(auth.Serial, auth.RestoreCode); auth.RestoreCodeVerified = true; e.Result = null; } catch (InvalidRestoreCodeException) { e.Result = "This authenticator was created before the restore capability existed and so the restore code will not work.\n\n" + "You will need to remove this authenticator from your Battle.net account and create a new one."; } catch (InvalidRestoreResponseException) { // ignore the validation if servers are down } catch (Exception ex2) { e.Result = "Oops. An error (" + ex2.Message + ") occured whilst validating your restore code." + "Please log a ticket at http://code.google.com/p/winauth so we can fix this."; } }
/// <summary> /// Load the authenticator and configuration settings /// </summary> /// <param name="form">parent winform</param> /// <param name="configFile">name of configfile or null for auto</param> /// <param name="password">optional supplied password or null to prompt if necessatu</param> /// <returns>new WinAuthConfig settings</returns> public static WinAuthConfig LoadConfig(MainForm form, string configFile, string password) { WinAuthConfig config = new WinAuthConfig(); if (string.IsNullOrEmpty(configFile) == true) { configFile = GetLastFile(1); if (string.IsNullOrEmpty(configFile) == false && File.Exists(configFile) == false) { // ignore it if file does't exist configFile = null; } } if (string.IsNullOrEmpty(configFile) == true) { // do we have a file specific in the registry? string configDirectory = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), WinAuth.APPLICATION_NAME); Directory.CreateDirectory(configDirectory); // check the old 1.3 file name configFile = Path.Combine(configDirectory, CONFIG_FILE_NAME_1_3); if (File.Exists(configFile) == false) { // check for default authenticator configFile = Path.Combine(configDirectory, DEFAULT_AUTHENTICATOR_FILE_NAME); } // if no config file, just return a blank config if (File.Exists(configFile) == false) { return config; } } // if no config file when one was specified; report an error if (File.Exists(configFile) == false) { MessageBox.Show(form, "Unable to find your configuration file \"" + configFile + "\"", form.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return config; } DialogResult configloaded; do { configloaded = DialogResult.OK; try { XmlDocument doc = null; XmlNode node = null; try { doc = new XmlDocument(); doc.Load(configFile); // check and load older versions node = doc.SelectSingleNode("WinAuth"); } catch (XmlException ) { // cause by invalid format, so we try and load other type of authenticator } if (node == null) { // foreign file so we import (authenticator.xml from winauth_1.3, android BMA xml or Java rs) Authenticator auth = LoadAuthenticator(form, configFile); if (auth != null) { config.Authenticator = auth; } SetLastFile(configFile); // set this as the new last opened file return config; } // Show if BETA //if (new BetaForm().ShowDialog(form) != DialogResult.OK) //{ // return null; //} XmlAttribute versionAttr; decimal version = Authenticator.DEAFULT_CONFIG_VERSION; if ((versionAttr = node.Attributes["version"]) != null && (version = decimal.Parse(versionAttr.InnerText, System.Globalization.CultureInfo.InvariantCulture)) < (decimal)1.4) { // old version 1.3 file config = LoadConfig_1_3(form, configFile); if (string.IsNullOrEmpty(config.Filename) == true) { config.Filename = configFile; } else if (string.Compare(configFile, config.Filename, true) != 0) { // switch over from winauth.xml to authenticator.xml and remove old winauth.xml File.Delete(configFile); configFile = config.Filename; } SaveAuthenticator(form, configFile, config); SetLastFile(configFile); // set this as the new last opened file return config; } // set the filename as itself config.Filename = configFile; bool boolVal = false; node = doc.DocumentElement.SelectSingleNode("alwaysontop"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.AlwaysOnTop = boolVal; } node = doc.DocumentElement.SelectSingleNode("usetrayicon"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.UseTrayIcon = boolVal; } node = doc.DocumentElement.SelectSingleNode("startwithwindows"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.StartWithWindows = boolVal; } node = doc.DocumentElement.SelectSingleNode("autorefresh"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.AutoRefresh = boolVal; } node = doc.DocumentElement.SelectSingleNode("allowcopy"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.AllowCopy = boolVal; } node = doc.DocumentElement.SelectSingleNode("copyoncode"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.CopyOnCode = boolVal; } node = doc.DocumentElement.SelectSingleNode("hideserial"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { config.HideSerial = boolVal; } // load the authenticator(s) - may have multiple authenticators in future version XmlNodeList nodes = doc.DocumentElement.SelectNodes("authenticator"); if (nodes != null) { // get the local machine time diff long machineTimeDiff = GetMachineTimeDiff(); foreach (XmlNode authenticatorNode in nodes) { // load the data Authenticator auth = null; try { try { auth = new Authenticator(); auth.Load(authenticatorNode, password, version); config.Authenticator = auth; } catch (EncrpytedSecretDataException) { PasswordForm passwordForm = new PasswordForm(); int retries = 0; do { passwordForm.Password = string.Empty; DialogResult result = passwordForm.ShowDialog(form); if (result != System.Windows.Forms.DialogResult.OK) { break; } try { auth = new Authenticator(); auth.Load(authenticatorNode, passwordForm.Password, version); config.Authenticator = auth; break; } catch (BadPasswordException) { MessageBox.Show(form, "Invalid password", "Load Authenticator", MessageBoxButtons.OK, MessageBoxIcon.Error); if (retries++ >= MAX_PASSWORD_RETRIES - 1) { break; } } } while (true); } // adjust the time diff from the local machine if (auth != null && machineTimeDiff != 0) { auth.ServerTimeDiff = machineTimeDiff; } } catch (InvalidUserDecryptionException) { MessageBox.Show(form, "The authenticator was encrypted using a different Windows User account.", "Load Authenticator", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (InvalidMachineDecryptionException) { MessageBox.Show(form, "The authenticator was encrypted using a different Windows computer.", "Load Authenticator", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (InvalidConfigDataException) { MessageBox.Show(form, "The authenticator data in " + configFile + " is not valid", "Load Authenticator", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { MessageBox.Show(form, "Unable to load authenticator from " + configFile + ": " + ex.Message, "Load Authenticator", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } // get the autologin node after we have gotten the pasword node = doc.DocumentElement.SelectSingleNode("autologin"); if (node != null && node.InnerText.Length != 0 && config.Authenticator != null) { config.AutoLogin = new HoyKeySequence(node, config.Authenticator.Password, version); } } catch (Exception ex) { configloaded = MessageBox.Show(form, "An error occured while loading your configuration file \"" + configFile + "\": " + ex.Message + "\n\nIt may be corrupted or in use by another application.", form.Text, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error); if (configloaded == DialogResult.Abort) { return null; } } } while (configloaded == DialogResult.Retry); SetLastFile(configFile); // set this as the new last opened file return config; }
/// <summary> /// Import and authenticator file of different formats /// </summary> /// <param name="configFile">filename to load</param> /// <param name="password">optional password</param> /// <returns>new Authenticator</returns> public static Authenticator ImportAuthenticator(string configFile, string password) { using (FileStream fs = new FileStream(configFile, FileMode.Open)) { Authenticator auth = new Authenticator(); // read the file string ext = Path.GetExtension(configFile).ToLower(); if (ext == ".xml") { // load ours or the Android XML file auth.Load(fs, Authenticator.FileFormat.WinAuth, password); } else { // load the Java MIDP recordfile auth.Load(fs, Authenticator.FileFormat.Midp, password); } return auth; } }
/// <summary> /// Write data into the XmlWriter /// </summary> /// <param name="writer">XmlWriter to write to</param> public void WriteXmlString(XmlWriter writer, Authenticator.PasswordTypes passwordType, string password) { writer.WriteStartElement("autologin"); writer.WriteStartElement("modifiers"); writer.WriteString(Authenticator.ByteArrayToString(BitConverter.GetBytes((int)Modifiers))); writer.WriteEndElement(); // writer.WriteStartElement("hotkey"); writer.WriteString(Authenticator.ByteArrayToString(BitConverter.GetBytes((ushort)HotKey))); writer.WriteEndElement(); // writer.WriteStartElement("windowtitle"); writer.WriteCData(WindowTitle ?? string.Empty); writer.WriteEndElement(); // writer.WriteStartElement("processname"); writer.WriteCData(ProcessName ?? string.Empty); writer.WriteEndElement(); // writer.WriteStartElement("windowtitleregex"); writer.WriteValue(WindowTitleRegex); writer.WriteEndElement(); // writer.WriteStartElement("advanced"); writer.WriteValue(Advanced); writer.WriteEndElement(); // writer.WriteStartElement("script"); string script = AdvancedScript.Replace("\n", string.Empty); StringBuilder passwordTypeAttribue = new StringBuilder(); if ((passwordType & Authenticator.PasswordTypes.Explicit) != 0) { byte[] plain = Encoding.UTF8.GetBytes(script); script = Authenticator.ByteArrayToString(plain); script = Authenticator.Encrypt(script, password); passwordTypeAttribue.Append("y"); } if ((passwordType & Authenticator.PasswordTypes.User) != 0) { byte[] plain = Encoding.UTF8.GetBytes(script); byte[] cipher = ProtectedData.Protect(plain, null, DataProtectionScope.CurrentUser); script = Authenticator.ByteArrayToString(cipher); passwordTypeAttribue.Append("u"); } if ((passwordType & Authenticator.PasswordTypes.Machine) != 0) { // we encrypt the data using the Local Machine account key byte[] plain = Encoding.UTF8.GetBytes(script); byte[] cipher = ProtectedData.Protect(plain, null, DataProtectionScope.LocalMachine); script = Authenticator.ByteArrayToString(cipher); passwordTypeAttribue.Append("m"); } writer.WriteAttributeString("encrypted", passwordTypeAttribue.ToString()); writer.WriteCData(script); writer.WriteEndElement(); writer.WriteEndElement(); }
/// <summary> /// Enroll a new Authenticator /// </summary> /// <returns></returns> private bool Enroll() { // already have one? if (Authenticator != null) { DialogResult warning = MessageBox.Show(this, "WARNING: You already have an authenticator registered.\n\n" + "You will NOT be able to access your Battle.net account if you continue and this authenticator is overwritten.\n\n" + "Do you still want to create a new authenticator?", "New Authenticator", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2); if (warning != System.Windows.Forms.DialogResult.Yes) { return false; } } // initialise the new authenticator Authenticator authenticator = null; try { // get the current country code string region = (RegionInfo.CurrentRegion != null ? RegionInfo.CurrentRegion.TwoLetterISORegionName : null); EnrollForm enrollform = new EnrollForm(); enrollform.CurrentRegion = region; if (enrollform.ShowDialog(this) != System.Windows.Forms.DialogResult.OK) { return false; } region = enrollform.CurrentRegion; // create and enroll a new authenticator authenticator = new Authenticator(); authenticator.Enroll(region); } catch (Exception ex) { MessageBox.Show(this, "There was an error registering your authenticator:\n\n" + ex.Message + "\n\nThis may be because the Battle.net servers are unavailable. Please try again later.", "New Authenticator", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } // prompt now done if (MessageBox.Show(this, "Your authenticator has been created and registered.\n\n" + "You will now be prompted to save it to a file on your computer before you can add it to your account.\n\n" + "1. Choose a file to save your new authenticator.\n" + "2. Select the encrpytion option.\n" + "3. Add your authenticator to your Battle.net account.\n\n" + "Click \"OK\" to save your authenticator.", "New Registered Authenticator", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) != DialogResult.OK) { return false; } // remember old config WinAuthConfig oldconfig = Config; // set the new authenticator Config = Config.Clone() as WinAuthConfig; Config.Authenticator = authenticator; Config.AutoLogin = null; // clear autologin // save config data if (SaveAuthenticator(null) == false) { // restore authenticator Config = oldconfig; return false; } // refresh this machine time diff based on this new authenticator WinAuthHelper.SetMachineTimeDiff(authenticator.ServerTimeDiff); // unhook and rehook hotkey HookHotkey(this.Config); // set filename and window title notifyIcon.Text = this.Text = WinAuth.APPLICATION_TITLE + " - " + Path.GetFileNameWithoutExtension(Config.Filename); // prompt to backup InitializedForm initForm = new InitializedForm(); initForm.Authenticator = Config.Authenticator; if (initForm.ShowDialog(this) == System.Windows.Forms.DialogResult.Yes) { BackupData(); } // show the new code ShowCode(); return true; }
/// <summary> /// Click the OK button to restore the authenticator /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnOk_Click_1(object sender, EventArgs e) { string serial = (serial1Field.Text + serial2Field.Text + serial3Field.Text + serial4Field.Text).Trim().ToUpper(); if (serial.Length != 14) { MessageBox.Show(this, "Invalid serial number.\n\n - must be something like US-1234-1234-1234.", WinAuth.APPLICATION_NAME, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } string restorecode = restoreCodeField.Text.Trim().ToUpper(); if (restorecode.Length != 10) { MessageBox.Show(this, "Invalid restore code.\n\n - must contain 10 letters and numbers.", WinAuth.APPLICATION_NAME, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } // restore the authenticator try { Authenticator auth = new Authenticator(); auth.Restore(serial, restorecode); this.Authenticator = auth; MessageBox.Show(this, "Your authenticator has been restored.\n\nYou will now be prompted where to save your new authenticator and choose your encryption level.", WinAuth.APPLICATION_NAME, MessageBoxButtons.OK, MessageBoxIcon.Information); // ok to continue DialogResult = System.Windows.Forms.DialogResult.OK; } catch (InvalidRestoreResponseException re) { MessageBox.Show(this, re.Message, WinAuth.APPLICATION_NAME, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } }