/// <summary> /// Import a v2 authenticator from an existing file name /// </summary> /// <param name="authenticatorFile">name of v2 xml file</param> private void importAuthenticatorFromV2(string authenticatorFile) { bool retry = false; string password = null; bool needPassword = false; bool invalidPassword = false; do { try { WinAuthConfig config = WinAuthHelper.LoadConfig(this, authenticatorFile, password); if (config.Count == 0) { return; } // get the actual authenticator and ensure it is synced WinAuthAuthenticator importedAuthenticator = config[0]; importedAuthenticator.Sync(); // make sure there isn't a name clash int rename = 0; string importedName = importedAuthenticator.Name; while (this.Config.Where(a => a.Name == importedName).Count() != 0) { importedName = importedAuthenticator.Name + " (" + (++rename) + ")"; } importedAuthenticator.Name = importedName; // save off any new authenticators as a backup WinAuthHelper.SaveToRegistry(this.Config, importedAuthenticator); // first time we prompt for protection and set out main settings from imported config if (this.Config.Count == 0) { this.Config.StartWithWindows = config.StartWithWindows; this.Config.UseTrayIcon = config.UseTrayIcon; this.Config.AlwaysOnTop = config.AlwaysOnTop; ChangePasswordForm form = new ChangePasswordForm(); form.PasswordType = Authenticator.PasswordTypes.Explicit; if (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) { this.Config.Yubi = form.Yubikey; this.Config.PasswordType = form.PasswordType; if ((this.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0 && string.IsNullOrEmpty(form.Password) == false) { this.Config.Password = form.Password; } } } // add to main list this.Config.Add(importedAuthenticator); SaveConfig(true); loadAuthenticatorList(importedAuthenticator); // reset UI setAutoSize(); introLabel.Visible = (this.Config.Count == 0); // reset hotkeys HookHotkeys(); needPassword = false; retry = false; } catch (EncrpytedSecretDataException) { needPassword = true; invalidPassword = false; } catch (BadYubiKeyException) { needPassword = true; invalidPassword = false; } catch (BadPasswordException) { needPassword = true; invalidPassword = true; } catch (Exception ex) { if (ErrorDialog(this, strings.UnknownError + ": " + ex.Message, ex, MessageBoxButtons.RetryCancel) == System.Windows.Forms.DialogResult.Cancel) { return; } needPassword = false; invalidPassword = false; retry = true; } if (needPassword == true) { GetPasswordForm form = new GetPasswordForm(); form.InvalidPassword = invalidPassword; var result = form.ShowDialog(this); if (result == DialogResult.Cancel) { return; } password = form.Password; retry = true; } } while (retry == true); }
/// <summary> /// Click the Export menu /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void exportOptionsMenuItem_Click(object sender, EventArgs e) { // confirm current password if ((this.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0) { bool invalidPassword = false; while (true) { GetPasswordForm checkform = new GetPasswordForm(); checkform.InvalidPassword = invalidPassword; var result = checkform.ShowDialog(this); if (result == DialogResult.Cancel) { return; } if (this.Config.IsPassword(checkform.Password) == true) { break; } invalidPassword = true; } } ExportForm exportform = new ExportForm(); if (exportform.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) { WinAuthHelper.ExportAuthenticators(this, this.Config, exportform.ExportFile, exportform.Password, exportform.PGPKey); } }
/// <summary> /// Process the context menu item /// </summary> /// <param name="menuitem"></param> private void ProcessMenu(ToolStripItem menuitem) { var item = this.CurrentItem; var auth = item.Authenticator; // check and perform each menu if (menuitem.Name == "setPasswordMenuItem") { // check if the authentcated is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { // show the new password form SetPasswordForm form = new SetPasswordForm(); if (form.ShowDialog(this.Parent as Form) != DialogResult.OK) { return; } // set the encrpytion string password = form.Password; if (string.IsNullOrEmpty(password) == false) { auth.AuthenticatorData.SetEncryption(Authenticator.PasswordTypes.Explicit, password); // can't have autorefresh on auth.AutoRefresh = false; item.UnprotectCount = 0; item.DisplayUntil = DateTime.MinValue; item.LastUpdate = DateTime.MinValue; item.LastCode = null; } else { auth.AuthenticatorData.SetEncryption(Authenticator.PasswordTypes.None); } // make sure authenticator is saved auth.MarkChanged(); RefreshCurrentItem(); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "showCodeMenuItem") { // check if the authentcated is still protected if (UnprotectAuthenticator(item) == DialogResult.Cancel) { return; } // reduce unprotect count if already displayed if (item.DisplayUntil != DateTime.MinValue) { ProtectAuthenticator(item); } item.LastCode = auth.CurrentCode; item.LastUpdate = DateTime.Now; item.DisplayUntil = DateTime.Now.AddSeconds(10); RefreshCurrentItem(); } else if (menuitem.Name == "copyCodeMenuItem") { // check if the authentcated is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { auth.CopyCodeToClipboard(this.Parent as Form, item.LastCode, true); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "autoRefreshMenuItem") { auth.AutoRefresh = !auth.AutoRefresh; item.LastUpdate = DateTime.Now; item.DisplayUntil = DateTime.MinValue; RefreshCurrentItem(); } else if (menuitem.Name == "shortcutKeyMenuItem") { DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { SetShortcutKeyForm form = new SetShortcutKeyForm(); form.Hotkey = auth.HotKey; if (form.ShowDialog(this.Parent as Form) != DialogResult.OK) { return; } auth.HotKey = form.Hotkey; } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "copyOnCodeMenuItem") { auth.CopyOnCode = !auth.CopyOnCode; } else if (menuitem.Name == "showRestoreCodeMenuItem") { // check if the authentcated is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { if (wasprotected != DialogResult.OK) { // confirm current main password var mainform = this.Parent as WinAuthForm; if ((mainform.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0) { bool invalidPassword = false; while (true) { GetPasswordForm checkform = new GetPasswordForm(); checkform.InvalidPassword = invalidPassword; var result = checkform.ShowDialog(this); if (result == DialogResult.Cancel) { return; } if (mainform.Config.IsPassword(checkform.Password) == true) { break; } invalidPassword = true; } } } // show the serial and restore code for Battle.net authenticator ShowRestoreCodeForm form = new ShowRestoreCodeForm(); form.CurrentAuthenticator = auth; form.ShowDialog(this.Parent as Form); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "showGoogleSecretMenuItem") { // check if the authentcated is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { if (wasprotected != DialogResult.OK) { // confirm current main password var mainform = this.Parent as WinAuthForm; if ((mainform.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0) { bool invalidPassword = false; while (true) { GetPasswordForm checkform = new GetPasswordForm(); checkform.InvalidPassword = invalidPassword; var result = checkform.ShowDialog(this); if (result == DialogResult.Cancel) { return; } if (mainform.Config.IsPassword(checkform.Password) == true) { break; } invalidPassword = true; } } } // show the secret key for Google authenticator ShowSecretKeyForm form = new ShowSecretKeyForm(); form.CurrentAuthenticator = auth; form.ShowDialog(this.Parent as Form); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "showTrionSecretMenuItem") { // check if the authenticator is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { // show the secret key for Trion authenticator ShowTrionSecretForm form = new ShowTrionSecretForm(); form.CurrentAuthenticator = auth; form.ShowDialog(this.Parent as Form); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "showSteamSecretMenuItem") { // check if the authenticator is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { if (wasprotected != DialogResult.OK) { // confirm current main password var mainform = this.Parent as WinAuthForm; if ((mainform.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0) { bool invalidPassword = false; while (true) { GetPasswordForm checkform = new GetPasswordForm(); checkform.InvalidPassword = invalidPassword; var result = checkform.ShowDialog(this); if (result == DialogResult.Cancel) { return; } if (mainform.Config.IsPassword(checkform.Password) == true) { break; } invalidPassword = true; } } } // show the secret key for Google authenticator ShowSteamSecretForm form = new ShowSteamSecretForm(); form.CurrentAuthenticator = auth; form.ShowDialog(this.Parent as Form); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "showSteamTradesMenuItem") { // check if the authenticator is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } try { // show the Steam trades dialog ShowSteamTradesForm form = new ShowSteamTradesForm(); form.Authenticator = auth; form.ShowDialog(this.Parent as Form); } finally { if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name == "deleteMenuItem") { if (WinAuthForm.ConfirmDialog(this.Parent as Form, strings.DeleteAuthenticatorWarning, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2) == DialogResult.Yes) { int index = item.Index; this.Items.Remove(item); ItemRemoved(this, new AuthenticatorListItemRemovedEventArgs(item)); if (index >= this.Items.Count) { index = this.Items.Count - 1; } this.CurrentItem = (this.Items.Count != 0 ? this.Items[index] as AuthenticatorListitem : null); // reset the correct indexes of our items for (int i = 0; i < this.Items.Count; i++) { (this.Items[i] as AuthenticatorListitem).Index = i; } } } else if (menuitem.Name == "renameMenuItem") { int y = (this.ItemHeight * item.Index) - (this.TopIndex * this.ItemHeight) + 8; RenameTextbox.Location = new Point(64, y); RenameTextbox.Text = auth.Name; RenameTextbox.Tag = item; RenameTextbox.Visible = true; RenameTextbox.Focus(); } else if (menuitem.Name == "syncMenuItem") { // check if the authentcated is still protected DialogResult wasprotected = UnprotectAuthenticator(item); if (wasprotected == DialogResult.Cancel) { return; } Cursor cursor = Cursor.Current; try { Cursor.Current = Cursors.WaitCursor; auth.Sync(); RefreshItem(item); } finally { Cursor.Current = cursor; if (wasprotected == DialogResult.OK) { ProtectAuthenticator(item); } } } else if (menuitem.Name.StartsWith("iconMenuItem_") == true) { if (menuitem.Tag is string && string.Compare((string)menuitem.Tag, "OTHER") == 0) { do { // other..choose an image file OpenFileDialog ofd = new OpenFileDialog(); ofd.AddExtension = true; ofd.CheckFileExists = true; ofd.DefaultExt = "png"; ofd.InitialDirectory = Directory.GetCurrentDirectory(); ofd.FileName = string.Empty; ofd.Filter = "PNG Image Files (*.png)|*.png|GIF Image Files (*.gif)|*.gif|All Files (*.*)|*.*"; ofd.RestoreDirectory = true; ofd.ShowReadOnly = false; ofd.Title = strings.LoadIconImage + " (png or gif @ 48x48)"; DialogResult result = ofd.ShowDialog(this); if (result != System.Windows.Forms.DialogResult.OK) { return; } try { // get the image and create an icon if not already the right size using (Bitmap iconimage = (Bitmap)Image.FromFile(ofd.FileName)) { if (iconimage.Width != ICON_WIDTH || iconimage.Height != ICON_HEIGHT) { using (Bitmap scaled = new Bitmap(ICON_WIDTH, ICON_HEIGHT)) { using (Graphics g = Graphics.FromImage(scaled)) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; g.DrawImage(iconimage, new Rectangle(0, 0, ICON_WIDTH, ICON_HEIGHT)); } auth.Icon = scaled; } } else { auth.Icon = iconimage; } RefreshCurrentItem(); } } catch (Exception ex) { if (MessageBox.Show(this.Parent as Form, string.Format(strings.ErrorLoadingIconFile, ex.Message), WinAuthMain.APPLICATION_NAME, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes) { continue; } } break; } while (true); } else { auth.Skin = (((string)menuitem.Tag).Length != 0 ? (string)menuitem.Tag : null); RefreshCurrentItem(); } } }
/// <summary> /// Click the Change Password item of the Options menu /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void changePasswordOptionsMenuItem_Click(object sender, EventArgs e) { // confirm current password if ((this.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0) { bool invalidPassword = false; while (true) { GetPasswordForm checkform = new GetPasswordForm(); checkform.InvalidPassword = invalidPassword; var result = checkform.ShowDialog(this); if (result == DialogResult.Cancel) { return; } if (this.Config.IsPassword(checkform.Password) == true) { break; } invalidPassword = true; } } ChangePasswordForm form = new ChangePasswordForm(); form.PasswordType = this.Config.PasswordType; if (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) { bool retry; var retrypasswordtype = this.Config.PasswordType; do { retry = false; this.Config.PasswordType = form.PasswordType; if ((this.Config.PasswordType & (Authenticator.PasswordTypes.YubiKeySlot1 | Authenticator.PasswordTypes.YubiKeySlot2)) != 0 && form.Yubikey != null) { this.Config.Yubi = form.Yubikey; } if ((this.Config.PasswordType & Authenticator.PasswordTypes.Explicit) != 0 && string.IsNullOrEmpty(form.Password) == false) { this.Config.Password = form.Password; } try { SaveConfig(true); } catch (InvalidEncryptionException) { var result = WinAuthForm.ConfirmDialog(this, "Decryption test failed. Retry?", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { retry = true; continue; } this.Config.PasswordType = retrypasswordtype; } catch (ChallengeResponseException) { var result = WinAuthForm.ConfirmDialog(this, "YubiKey Challenge/Response failed. Retry?", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { retry = true; continue; } this.Config.PasswordType = retrypasswordtype; } } while (retry); } }
/// <summary> /// Import a file containing authenticators in the KeyUriFormat. The file might be plain text, encrypted zip or encrypted pgp. /// </summary> /// <param name="parent">parent Form</param> /// <param name="file">file name to import</param> /// <returns>list of imported authenticators</returns> public static List<WinAuthAuthenticator> ImportAuthenticators(Form parent, string file) { List<WinAuthAuthenticator> authenticators = new List<WinAuthAuthenticator>(); string password = null; string pgpKey = null; StringBuilder lines = new StringBuilder(); bool retry; do { retry = false; lines.Length = 0; // open the zip file if (string.Compare(Path.GetExtension(file), ".zip", true) == 0) { using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { ZipFile zip = null; try { zip = new ZipFile(fs); if (string.IsNullOrEmpty(password) == false) { zip.Password = password; } byte[] buffer = new byte[4096]; foreach (ZipEntry entry in zip) { if (entry.IsFile == false || string.Compare(Path.GetExtension(entry.Name), ".txt", true) != 0) { continue; } // read file out Stream zs = zip.GetInputStream(entry); using (var ms = new MemoryStream()) { StreamUtils.Copy(zs, ms, buffer); // get as string and append ms.Seek(0, SeekOrigin.Begin); using (var sr = new StreamReader(ms)) { lines.Append(sr.ReadToEnd()).Append(Environment.NewLine); } } } } catch (ZipException ex) { if (ex.Message.IndexOf("password") != -1) { // already have a password if (string.IsNullOrEmpty(password) == false) { WinAuthForm.ErrorDialog(parent, strings.InvalidPassword, ex.InnerException, MessageBoxButtons.OK); } // need password GetPasswordForm form = new GetPasswordForm(); if (form.ShowDialog(parent) == DialogResult.Cancel) { return null; } password = form.Password; retry = true; continue; } throw; } finally { if (zip != null) { zip.IsStreamOwner = true; zip.Close(); } } } } else if (string.Compare(Path.GetExtension(file), ".pgp", true) == 0) { string encoded = File.ReadAllText(file); if (string.IsNullOrEmpty(pgpKey) == true) { // need password GetPGPKeyForm form = new GetPGPKeyForm(); if (form.ShowDialog(parent) == DialogResult.Cancel) { return null; } pgpKey = form.PGPKey; password = form.Password; retry = true; continue; } try { string line = PGPDecrypt(encoded, pgpKey, password); lines.Append(line); } catch (Exception ex) { WinAuthForm.ErrorDialog(parent, strings.InvalidPassword, ex.InnerException, MessageBoxButtons.OK); pgpKey = null; password = null; retry = true; continue; } } else // read a plain text file { lines.Append(File.ReadAllText(file)); } } while (retry); int linenumber = 0; try { using (var sr = new StringReader(lines.ToString())) { string line; while ((line = sr.ReadLine()) != null) { linenumber++; // ignore blank lines or comments line = line.Trim(); if (line.Length == 0 || line.IndexOf("#") == 0) { continue; } // parse and validate URI var uri = new Uri(line); // we only support "otpauth" if (uri.Scheme != "otpauth") { throw new ApplicationException("Import only supports otpauth://"); } // we only support totp (not hotp) if (uri.Host != "totp" && uri.Host != "hotp") { throw new ApplicationException("Import only supports otpauth://totp/ or otpauth://hotp/"); } // get the label and optional issuer string issuer = string.Empty; string label = (string.IsNullOrEmpty(uri.LocalPath) == false ? uri.LocalPath.Substring(1) : string.Empty); // skip past initial / int p = label.IndexOf(":"); if (p != -1) { issuer = label.Substring(0, p); label = label.Substring(p + 1); } // + aren't decoded label = label.Replace("+", " "); var query = HttpUtility.ParseQueryString(uri.Query); string secret = query["secret"]; if (string.IsNullOrEmpty(secret) == true) { throw new ApplicationException("Authenticator does not contain secret"); } string counter = query["counter"]; if (uri.Host == "hotp" && string.IsNullOrEmpty(counter) == true) { throw new ApplicationException("HOTP authenticator should have a counter"); } WinAuthAuthenticator importedAuthenticator = new WinAuthAuthenticator(); importedAuthenticator.AutoRefresh = false; // Authenticator auth; if (string.Compare(issuer, "BattleNet", true) == 0) { string serial = query["serial"]; if (string.IsNullOrEmpty(serial) == true) { throw new ApplicationException("Battle.net Authenticator does not have a serial"); } serial = serial.ToUpper(); if (Regex.IsMatch(serial, @"^[A-Z]{2}-?[\d]{4}-?[\d]{4}-?[\d]{4}$") == false) { throw new ApplicationException("Invalid serial for Battle.net Authenticator"); } auth = new BattleNetAuthenticator(); //char[] decoded = Base32.getInstance().Decode(secret).Select(c => Convert.ToChar(c)).ToArray(); // this is hex string values //string hex = new string(decoded); //((BattleNetAuthenticator)auth).SecretKey = Authenticator.StringToByteArray(hex); ((BattleNetAuthenticator)auth).SecretKey = Base32.getInstance().Decode(secret); ((BattleNetAuthenticator)auth).Serial = serial; issuer = string.Empty; } else if (string.Compare(issuer, "Steam", true) == 0) { auth = new SteamAuthenticator(); ((SteamAuthenticator)auth).SecretKey = Base32.getInstance().Decode(secret); ((SteamAuthenticator)auth).Serial = string.Empty; ((SteamAuthenticator)auth).DeviceId = string.Empty; ((SteamAuthenticator)auth).RevocationCode = string.Empty; issuer = string.Empty; } else if (uri.Host == "hotp") { auth = new HOTPAuthenticator(); ((HOTPAuthenticator)auth).SecretKey = Base32.getInstance().Decode(secret); ((HOTPAuthenticator)auth).Counter = int.Parse(counter); if (string.IsNullOrEmpty(issuer) == false) { auth.Issuer = issuer; } } else // if (string.Compare(issuer, "Google", true) == 0) { auth = new GoogleAuthenticator(); ((GoogleAuthenticator)auth).Enroll(secret); if (string.Compare(issuer, "Google", true) == 0) { issuer = string.Empty; } else if (string.IsNullOrEmpty(issuer) == false) { auth.Issuer = issuer; } } // int digits = 0; int.TryParse(query["digits"], out digits); if (digits != 0) { auth.CodeDigits = digits; } // if (label.Length != 0) { importedAuthenticator.Name = (issuer.Length != 0 ? issuer + " (" + label + ")" : label); } else if (issuer.Length != 0) { importedAuthenticator.Name = issuer; } else { importedAuthenticator.Name = "Imported"; } // importedAuthenticator.AuthenticatorData = auth; // set the icon string icon = query["icon"]; if (string.IsNullOrEmpty(icon) == false) { if (icon.StartsWith("base64:") == true) { string b64 = Convert.ToBase64String(Base32.getInstance().Decode(icon.Substring(7))); importedAuthenticator.Skin = "base64:" + b64; } else { importedAuthenticator.Skin = icon + "Icon.png"; } } // sync importedAuthenticator.Sync(); authenticators.Add(importedAuthenticator); } } return authenticators; } catch (UriFormatException ex) { throw new ImportException(string.Format(strings.ImportInvalidUri, linenumber), ex); } catch (Exception ex) { throw new ImportException(string.Format(strings.ImportError, linenumber, ex.Message), ex); } }