/// <summary> /// Create a new AuthenticatorListitem /// </summary> /// <param name="auth">authenticator</param> /// <param name="index">index of item</param> public AuthenticatorListitem(WinAuthAuthenticator auth, int index) { Authenticator = auth; LastUpdate = DateTime.MinValue; Index = index; DisplayUntil = DateTime.MinValue; }
/// <summary> /// Default constructor /// </summary> public ConfigChangedEventArgs(string propertyName, WinAuthAuthenticator authenticator = null, WinAuthAuthenticatorChangedEventArgs acargs = null) : base() { PropertyName = propertyName; Authenticator = authenticator; AuthenticatorChangedEventArgs = acargs; }
/// <summary> /// Callback called by Form to initaite press of hotkey /// </summary> /// <param name="kea">KeyboardHookEventArgs event args</param> public void KeyCallback(KeyboardHookEventArgs kea) { // key and modifers match? WinAuthAuthenticator match = null; foreach (var auth in m_hooked) { if ((Keys)auth.HotKey.Key == kea.Key && auth.HotKey.Modifiers == kea.Modifiers) { match = auth; break; } } if (match == null) { return; } kea.Authenticator = match; // call user events if (KeyPressed != null) { KeyPressed(this, kea); } }
/// <summary> /// Clone this authenticator /// </summary> /// <returns></returns> public object Clone() { WinAuthAuthenticator clone = this.MemberwiseClone() as WinAuthAuthenticator; clone.Id = Guid.NewGuid(); clone.OnWinAuthAuthenticatorChanged = null; clone.AuthenticatorData = (this.AuthenticatorData != null ? this.AuthenticatorData.Clone() as Authenticator : null); return(clone); }
/// <summary> /// Set up the hook into Windows /// </summary> protected void Hook() { for (int i = 0; i < m_hooked.Count; i++) { WinAuthAuthenticator auth = m_hooked[i]; Keys key = (Keys)auth.HotKey.Key; WinAPI.KeyModifiers modifier = auth.HotKey.Modifiers; if (WinAPI.RegisterHotKey(m_form.Handle, i + 1, modifier | WinAPI.KeyModifiers.NoRepeat, key) == false) { // the MOD_NOREPEAT flag is not support in XP or 2003 and we should get a fail, so we call again without it WinAPI.RegisterHotKey(m_form.Handle, i + 1, modifier, key); } } }
/// <summary> /// Process the pressed hotkey by performing the appropriate operation /// </summary> /// <param name="auth">Authenticator</param> private void HandleHotkey(WinAuthAuthenticator auth) { // get the code string code = null; try { code = auth.CurrentCode; } catch (EncrpytedSecretDataException) { // if the authenticator is current protected we display the password window, get the code, and reprotect it // with a bit of window jiggling to make sure we get focus and then put it back // save the current window var fgwindow = WinAPI.GetForegroundWindow(); Screen screen = Screen.FromHandle(fgwindow); IntPtr activewindow = IntPtr.Zero; if (this.Visible == true) { activewindow = WinAPI.SetActiveWindow(this.Handle); BringToFront(); } var item = authenticatorList.Items.Cast<AuthenticatorListitem>().Where(i => i.Authenticator == auth).FirstOrDefault(); code = authenticatorList.GetItemCode(item, screen); // restore active window if (activewindow != IntPtr.Zero) { WinAPI.SetActiveWindow(activewindow); } WinAPI.SetForegroundWindow(fgwindow); } if (code != null) { // default to sending the code to the current window KeyboardSender keysend = new KeyboardSender(auth.HotKey.Window); string command = null; if (auth.HotKey.Action == HotKey.HotKeyActions.Notify) { if (auth.CopyOnCode) { auth.CopyCodeToClipboard(this, code); } if (code.Length > 5) { code = code.Insert(code.Length / 2, " "); } notifyIcon.ShowBalloonTip(10000, auth.Name, code, ToolTipIcon.Info); } if (auth.HotKey.Action == HotKey.HotKeyActions.Copy) { command = "{COPY}"; } else if (auth.HotKey.Action == HotKey.HotKeyActions.Advanced) { command = auth.HotKey.Advanced; } else if (auth.HotKey.Action == HotKey.HotKeyActions.Inject) { command = "{CODE}"; } if (command != null) { keysend.SendKeys(this, command, code); } } }
/// <summary> /// Load the authenticators into the display list /// </summary> /// <param name="added">authenticator we just added</param> private void loadAuthenticatorList(WinAuthAuthenticator added = null) { // set up list authenticatorList.Items.Clear(); int index = 0; foreach (var auth in Config) { var ali = new AuthenticatorListitem(auth, index); if (added != null && added == auth && auth.AutoRefresh == false && !(auth.AuthenticatorData is HOTPAuthenticator)) { ali.LastUpdate = DateTime.Now; ali.DisplayUntil = DateTime.Now.AddSeconds(10); } authenticatorList.Items.Add(ali); index++; } authenticatorList.Visible = (authenticatorList.Items.Count != 0); }
/// <summary> /// Create a new HotKeyLauncher object /// </summary> /// <param name="form">owning Form</param> /// <param name="auth">Authenticator</param> public HotKeyLauncher(WinAuthForm form, WinAuthAuthenticator auth) { Started = DateTime.Now; Form = form; Authenticator = auth; }
/// <summary> /// Click on a choice of new authenticator /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void addAuthenticatorMenu_Click(object sender, EventArgs e) { ToolStripItem menuitem = (ToolStripItem)sender; RegisteredAuthenticator registeredauth = menuitem.Tag as RegisteredAuthenticator; if (registeredauth != null) { // add the new authenticator WinAuthAuthenticator winauthauthenticator = new WinAuthAuthenticator(); bool added = false; if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.BattleNet) { int existing = 0; string name; do { name = "Battle.net" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; // create the Battle.net authenticator AddBattleNetAuthenticator form = new AddBattleNetAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.Trion) { // create the Trion authenticator int existing = 0; string name; do { name = "Trion" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; AddTrionAuthenticator form = new AddTrionAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.Steam) { // create the authenticator int existing = 0; string name; do { name = "Steam" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; AddSteamAuthenticator form = new AddSteamAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.Google) { // create the Google authenticator // add the new authenticator int existing = 0; string name; do { name = "Google" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; AddGoogleAuthenticator form = new AddGoogleAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.GuildWars) { // create the GW2 authenticator int existing = 0; string name; do { name = "GuildWars" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; AddGuildWarsAuthenticator form = new AddGuildWarsAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.Microsoft) { // create the Microsoft authenticator int existing = 0; string name; do { name = "Microsoft" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; AddMicrosoftAuthenticator form = new AddMicrosoftAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else if (registeredauth.AuthenticatorType == RegisteredAuthenticator.AuthenticatorTypes.RFC6238_TIME) { // create the Google authenticator // add the new authenticator int existing = 0; string name; do { name = "Authenticator" + (existing != 0 ? " (" + existing + ")" : string.Empty); existing++; } while (authenticatorList.Items.Cast<AuthenticatorListitem>().Where(a => a.Authenticator.Name == name).Count() != 0); winauthauthenticator.Name = name; winauthauthenticator.AutoRefresh = false; winauthauthenticator.Skin = "WinAuthIcon.png"; AddAuthenticator form = new AddAuthenticator(); form.Authenticator = winauthauthenticator; added = (form.ShowDialog(this) == System.Windows.Forms.DialogResult.OK); } else { throw new NotImplementedException(strings.AuthenticatorNotImplemented + ": " + registeredauth.AuthenticatorType.ToString()); } if (added == true) { // save off any new authenticators as a backup WinAuthHelper.SaveToRegistry(this.Config, winauthauthenticator); // first time we prompt for protection if (this.Config.Count == 0) { 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; } } } this.Config.Add(winauthauthenticator); SaveConfig(true); loadAuthenticatorList(winauthauthenticator); // reset UI setAutoSize(); introLabel.Visible = (this.Config.Count == 0); // reset hotkeeys HookHotkeys(); } } }
/// <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); } }
/// <summary> /// Save a PGP encrypted version of an authenticator into the registry for recovery /// </summary> /// <param name="wa">WinAuthAuthenticator instance</param> public static void SaveToRegistry(WinAuthConfig config, WinAuthAuthenticator wa) { if (config == null || wa == null || wa.AuthenticatorData == null) { return; } using (SHA256 sha = new SHA256Managed()) { // get a hash based on the authenticator key string authkey = Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(wa.AuthenticatorData.SecretData))); // save the PGP encrypted key using (EncodedStringWriter sw = new EncodedStringWriter(Encoding.UTF8)) { XmlWriterSettings xmlsettings = new XmlWriterSettings(); xmlsettings.Indent = true; using (XmlWriter xw = XmlWriter.Create(sw, xmlsettings)) { xw.WriteStartElement("WinAuth"); xw.WriteAttributeString("version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(2)); wa.WriteXmlString(xw); xw.WriteEndElement(); } string pgpkey = string.IsNullOrEmpty(config.PGPKey) == false ? config.PGPKey : WinAuthHelper.WINAUTH_PGP_PUBLICKEY; config.WriteSetting(WINAUTHREGKEY_BACKUP + "\\" + authkey, PGPEncrypt(sw.ToString(), pgpkey)); } } }
/// <summary> /// Display a new Notification for a Trading confirmation /// </summary> /// <param name="auth"></param> /// <param name="title"></param> /// <param name="message"></param> /// <param name="extraHeight"></param> public void ShowNotification(WinAuthAuthenticator auth, string title, string message, bool openOnClick, int extraHeight) { var notify = new Notification(title, message, 10000); if (extraHeight != 0) { notify.Height += extraHeight; } notify.Tag = auth; if (openOnClick == true) { notify.OnNotificationClicked += Notify_Click; } notify.Show(); }
/// <summary> /// Default constructor /// </summary> public AuthenticatorListDoubleClickEventArgs(WinAuthAuthenticator auth) : base() { Authenticator = auth; }