/// <summary> /// Read the saved Xml to load the hotkey /// </summary> /// <param name="reader">XmlReader</param> public void ReadXml(XmlReader reader) { reader.MoveToContent(); if (reader.IsEmptyElement) { reader.Read(); return; } reader.Read(); while (reader.EOF == false) { if (reader.IsStartElement()) { switch (reader.Name) { case "modifiers": Modifiers = (WinAPI.KeyModifiers)BitConverter.ToInt32( Authenticator.StringToByteArray(reader.ReadElementContentAsString()), 0); break; case "key": Key = (WinAPI.VirtualKeyCode)BitConverter.ToUInt16( Authenticator.StringToByteArray(reader.ReadElementContentAsString()), 0); break; case "action": Action = (HotKeyActions)Enum.Parse(typeof(HotKeyActions), reader.ReadElementContentAsString(), true); break; case "window": Window = reader.ReadElementContentAsString(); break; case "advanced": Advanced = reader.ReadElementContentAsString(); break; default: reader.Skip(); break; } } else { reader.Read(); break; } } }
/// <summary> /// Create a new HotKeySequence from a loaded string /// </summary> /// <param name="data"></param> public HoyKeySequence(string data) { if (string.IsNullOrEmpty(data) == false) { Match match = Regex.Match(data, @"([0-9a-fA-F]{8})([0-9a-fA-F]{4})\t([^\t]*)\t(Y|N)(.*)", RegexOptions.Multiline); if (match.Success == true) { Modifiers = (WinAPI.KeyModifiers)BitConverter.ToInt32(Authenticator.StringToByteArray(match.Groups[1].Value), 0); HotKey = (WinAPI.VirtualKeyCode)BitConverter.ToUInt16(Authenticator.StringToByteArray(match.Groups[2].Value), 0); WindowTitle = match.Groups[3].Value; Advanced = (match.Groups[4].Value == "Y"); if (Advanced == true) { AdvancedScript = match.Groups[5].Value; } } } }
protected bool ReadXmlInternal(XmlReader reader, string password = null) { bool changed = false; decimal version; if (decimal.TryParse(reader.GetAttribute("version"), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out version) == true) { Version = version; if (version > WinAuthConfig.CURRENTVERSION) { // ensure we don't overwrite a newer config throw new WinAuthInvalidNewerConfigException(string.Format(strings.ConfigIsNewer, version)); } } string encrypted = reader.GetAttribute("encrypted"); this.PasswordType = Authenticator.DecodePasswordTypes(encrypted); if (this.PasswordType != Authenticator.PasswordTypes.None) { // read the encrypted text from the node string data = reader.ReadElementContentAsString(); // decrypt YubiKey yubi = null; if ((this.PasswordType & (Authenticator.PasswordTypes.YubiKeySlot1 | Authenticator.PasswordTypes.YubiKeySlot2)) != 0 /* && this.Yubi == null */) { yubi = YubiKey.CreateInstance(); } data = Authenticator.DecryptSequence(data, this.PasswordType, password, yubi); using (MemoryStream ms = new MemoryStream(Authenticator.StringToByteArray(data))) { reader = XmlReader.Create(ms); changed = ReadXml(reader, password); } this.PasswordType = Authenticator.DecodePasswordTypes(encrypted); this.Password = password; this.Yubi = yubi; return(changed); } reader.MoveToContent(); if (reader.IsEmptyElement) { reader.Read(); return(changed); } bool defaultAutoRefresh = true; bool defaultAllowCopy = false; bool defaultCopyOnCode = false; bool defaultHideSerial = true; string defaultSkin = null; reader.Read(); while (reader.EOF == false) { if (reader.IsStartElement()) { switch (reader.Name) { case "config": changed = ReadXmlInternal(reader, password) || changed; break; // 3.2 has new layout case "data": { encrypted = reader.GetAttribute("encrypted"); this.PasswordType = Authenticator.DecodePasswordTypes(encrypted); if (this.PasswordType != Authenticator.PasswordTypes.None) { string md5 = reader.GetAttribute("md5"); // read the encrypted text from the node string data = reader.ReadElementContentAsString(); using (var hasher = new MD5CryptoServiceProvider()) { hasher.ComputeHash(Authenticator.StringToByteArray(data)); } // decrypt YubiKey yubi = null; if ((this.PasswordType & (Authenticator.PasswordTypes.YubiKeySlot1 | Authenticator.PasswordTypes.YubiKeySlot2)) != 0 /* && this.Yubi == null */) { yubi = YubiKey.CreateInstance(); } data = Authenticator.DecryptSequence(data, this.PasswordType, password, yubi); byte[] plain = Authenticator.StringToByteArray(data); using (MemoryStream ms = new MemoryStream(plain)) { var datareader = XmlReader.Create(ms); changed = ReadXmlInternal(datareader, password) || changed; } this.PasswordType = Authenticator.DecodePasswordTypes(encrypted); this.Password = password; this.Yubi = yubi; } } break; case "alwaysontop": _alwaysOnTop = reader.ReadElementContentAsBoolean(); break; case "usetrayicon": _useTrayIcon = reader.ReadElementContentAsBoolean(); break; case "notifyaction": string s = reader.ReadElementContentAsString(); if (string.IsNullOrEmpty(s) == false) { try { _notifyAction = (NotifyActions)Enum.Parse(typeof(NotifyActions), s, true); } catch (Exception) { } } break; case "startwithwindows": _startWithWindows = reader.ReadElementContentAsBoolean(); break; case "autosize": _autoSize = reader.ReadElementContentAsBoolean(); break; case "left": _position.X = reader.ReadElementContentAsInt(); break; case "top": _position.Y = reader.ReadElementContentAsInt(); break; case "width": _width = reader.ReadElementContentAsInt(); break; case "height": _height = reader.ReadElementContentAsInt(); break; case "shadowtype": _shadowType = reader.ReadElementContentAsString(); break; case "pgpkey": _pgpKey = reader.ReadElementContentAsString(); break; case "settings": XmlSerializer serializer = new XmlSerializer(typeof(setting[]), new XmlRootAttribute() { ElementName = "settings" }); _settings = ((setting[])serializer.Deserialize(reader)).ToDictionary(e => e.Key, e => e.Value); break; // previous setting used as defaults for new case "autorefresh": defaultAutoRefresh = reader.ReadElementContentAsBoolean(); break; case "allowcopy": defaultAllowCopy = reader.ReadElementContentAsBoolean(); break; case "copyoncode": defaultCopyOnCode = reader.ReadElementContentAsBoolean(); break; case "hideserial": defaultHideSerial = reader.ReadElementContentAsBoolean(); break; case "skin": defaultSkin = reader.ReadElementContentAsString(); break; case "WinAuthAuthenticator": var wa = new WinAuthAuthenticator(); changed = wa.ReadXml(reader, password) || changed; this.Add(wa); if (this.CurrentAuthenticator == null) { this.CurrentAuthenticator = wa; } break; // for old 2.x configs case "authenticator": var waold = new WinAuthAuthenticator(); waold.AuthenticatorData = Authenticator.ReadXmlv2(reader, password); if (waold.AuthenticatorData is BattleNetAuthenticator) { waold.Name = "Battle.net"; } else if (waold.AuthenticatorData is GuildWarsAuthenticator) { waold.Name = "GuildWars 2"; } else if (waold.AuthenticatorData is GuildWarsAuthenticator) { waold.Name = "Authenticator"; } this.Add(waold); this.CurrentAuthenticator = waold; waold.AutoRefresh = defaultAutoRefresh; waold.AllowCopy = defaultAllowCopy; waold.CopyOnCode = defaultCopyOnCode; waold.HideSerial = defaultHideSerial; break; // old 2.x auto login script case "autologin": var hks = new HoyKeySequence(); hks.ReadXml(reader, password); if (hks.HotKey != 0) { if (this.CurrentAuthenticator.HotKey == null) { this.CurrentAuthenticator.HotKey = new HotKey(); } HotKey hotkey = this.CurrentAuthenticator.HotKey; hotkey.Action = HotKey.HotKeyActions.Inject; hotkey.Key = hks.HotKey; hotkey.Modifiers = hks.Modifiers; if (hks.WindowTitleRegex == true && string.IsNullOrEmpty(hks.WindowTitle) == false) { hotkey.Window = "/" + Regex.Escape(hks.WindowTitle); } else if (string.IsNullOrEmpty(hks.WindowTitle) == false) { hotkey.Window = hks.WindowTitle; } else if (string.IsNullOrEmpty(hks.ProcessName) == false) { hotkey.Window = hks.ProcessName; } if (hks.Advanced == true) { hotkey.Action = HotKey.HotKeyActions.Advanced; hotkey.Advanced = hks.AdvancedScript; } } break; default: reader.Skip(); break; } } else { reader.Read(); break; } } return(changed); }
/// <summary> /// Write the data as xml into an XmlWriter /// </summary> /// <param name="writer">XmlWriter to write config</param> public void WriteXmlString(XmlWriter writer, bool includeFilename = false, bool includeSettings = true) { writer.WriteStartDocument(true); // if (includeFilename == true && string.IsNullOrEmpty(this.Filename) == false) { writer.WriteComment(this.Filename); } // writer.WriteStartElement("WinAuth"); writer.WriteAttributeString("version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(2)); // writer.WriteStartElement("alwaysontop"); writer.WriteValue(this.AlwaysOnTop); writer.WriteEndElement(); // writer.WriteStartElement("usetrayicon"); writer.WriteValue(this.UseTrayIcon); writer.WriteEndElement(); // writer.WriteStartElement("notifyaction"); writer.WriteValue(Enum.GetName(typeof(NotifyActions), this.NotifyAction)); writer.WriteEndElement(); // writer.WriteStartElement("startwithwindows"); writer.WriteValue(this.StartWithWindows); writer.WriteEndElement(); // writer.WriteStartElement("autosize"); writer.WriteValue(this.AutoSize); writer.WriteEndElement(); // if (this.Position.IsEmpty == false) { writer.WriteStartElement("left"); writer.WriteValue(this.Position.X); writer.WriteEndElement(); writer.WriteStartElement("top"); writer.WriteValue(this.Position.Y); writer.WriteEndElement(); } // writer.WriteStartElement("width"); writer.WriteValue(this.Width); writer.WriteEndElement(); // writer.WriteStartElement("height"); writer.WriteValue(this.Height); writer.WriteEndElement(); // if (string.IsNullOrEmpty(this.ShadowType) == false) { writer.WriteStartElement("shadowtype"); writer.WriteValue(this.ShadowType); writer.WriteEndElement(); } // if (string.IsNullOrEmpty(this.PGPKey) == false) { writer.WriteStartElement("pgpkey"); writer.WriteCData(this.PGPKey); writer.WriteEndElement(); } if (PasswordType != Authenticator.PasswordTypes.None) { writer.WriteStartElement("data"); StringBuilder encryptedTypes = new StringBuilder(); if ((PasswordType & Authenticator.PasswordTypes.Explicit) != 0) { encryptedTypes.Append("y"); } if ((PasswordType & Authenticator.PasswordTypes.User) != 0) { encryptedTypes.Append("u"); } if ((PasswordType & Authenticator.PasswordTypes.Machine) != 0) { encryptedTypes.Append("m"); } if ((PasswordType & Authenticator.PasswordTypes.YubiKeySlot1) != 0) { encryptedTypes.Append("a"); } if ((PasswordType & Authenticator.PasswordTypes.YubiKeySlot2) != 0) { encryptedTypes.Append("b"); } writer.WriteAttributeString("encrypted", encryptedTypes.ToString()); byte[] data; using (MemoryStream ms = new MemoryStream()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.Encoding = Encoding.UTF8; using (XmlWriter encryptedwriter = XmlWriter.Create(ms, settings)) { encryptedwriter.WriteStartElement("config"); foreach (WinAuthAuthenticator wa in this) { wa.WriteXmlString(encryptedwriter); } encryptedwriter.WriteEndElement(); } data = ms.ToArray(); } using (var hasher = new MD5CryptoServiceProvider()) { string encdata = Authenticator.EncryptSequence(Authenticator.ByteArrayToString(data), PasswordType, Password, this.Yubi); string enchash = Authenticator.ByteArrayToString(hasher.ComputeHash(Authenticator.StringToByteArray(encdata))); writer.WriteAttributeString("md5", enchash); writer.WriteString(encdata); } writer.WriteEndElement(); } else { foreach (WinAuthAuthenticator wa in this) { wa.WriteXmlString(writer); } } if (includeSettings == true && _settings.Count != 0) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add(string.Empty, string.Empty); XmlSerializer serializer = new XmlSerializer(typeof(setting[]), new XmlRootAttribute() { ElementName = "settings" }); serializer.Serialize(writer, _settings.Select(e => new setting { Key = e.Key, Value = e.Value }).ToArray(), ns); } // close WinAuth writer.WriteEndElement(); // end document writer.WriteEndDocument(); }
/// <summary> /// Enroll the authenticator with the server /// </summary> public bool Enroll(EnrollState state) { // clear error state.Error = null; try { var data = new NameValueCollection(); var cookies = state.Cookies = state.Cookies ?? new CookieContainer(); string response; if (string.IsNullOrEmpty(state.OAuthToken) == true) { // get session if (cookies.Count == 0) { cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("mobileClientVersion", "3067969+%282.1.3%29")); cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("mobileClient", "android")); cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("steamid", "")); cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("steamLogin", "")); cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("Steam_Language", "english")); cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("dob", "")); NameValueCollection headers = new NameValueCollection(); headers.Add("X-Requested-With", "com.valvesoftware.android.steam.community"); response = Request("https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", "GET", null, cookies, headers); } // get the user's RSA key data.Add("username", state.Username); response = Request(COMMUNITY_BASE + "/mobilelogin/getrsakey", "POST", data, cookies); var rsaresponse = JObject.Parse(response); if (rsaresponse.SelectToken("success").Value <bool>() != true) { throw new InvalidEnrollResponseException("Cannot get steam information for user: "******"publickey_exp").Value <string>()); p.Modulus = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value <string>()); rsa.ImportParameters(p); encryptedPassword = rsa.Encrypt(passwordBytes, false); } // login request data = new NameValueCollection(); data.Add("password", Convert.ToBase64String(encryptedPassword)); data.Add("username", state.Username); data.Add("twofactorcode", ""); data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty)); data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile"); data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1")); data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters")); data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId ?? string.Empty : string.Empty)); data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value <string>()); data.Add("remember_login", "false"); data.Add("oauth_client_id", "DE45CD61"); data.Add("oauth_scope", "read_profile write_profile read_client write_client"); data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString()); response = Request(COMMUNITY_BASE + "/mobilelogin/dologin/", "POST", data, cookies); Dictionary <string, object> loginresponse = JsonConvert.DeserializeObject <Dictionary <string, object> >(response); if (loginresponse.ContainsKey("emailsteamid") == true) { state.SteamId = loginresponse["emailsteamid"] as string; } // require captcha if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true) { state.RequiresCaptcha = true; state.CaptchaId = (string)loginresponse["captcha_gid"]; state.CaptchaUrl = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId; } else { state.RequiresCaptcha = false; state.CaptchaId = null; state.CaptchaUrl = null; state.CaptchaText = null; } // require email auth if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true) { if (loginresponse.ContainsKey("emaildomain") == true) { var emaildomain = (string)loginresponse["emaildomain"]; if (string.IsNullOrEmpty(emaildomain) == false) { state.EmailDomain = emaildomain; } } state.RequiresEmailAuth = true; } else { state.EmailDomain = null; state.RequiresEmailAuth = false; } // require email auth if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true) { state.Requires2FA = true; } else { state.Requires2FA = false; } // if we didn't login, return the result if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false) { if (loginresponse.ContainsKey("oauth") == false) { state.Error = "Invalid response from Steam (No OAuth token)"; } if (loginresponse.ContainsKey("message") == true) { state.Error = (string)loginresponse["message"]; } return(false); } // get the OAuth token - is stringified json string oauth = (string)loginresponse["oauth"]; var oauthjson = JObject.Parse(oauth); state.OAuthToken = oauthjson.SelectToken("oauth_token").Value <string>(); if (oauthjson.SelectToken("steamid") != null) { state.SteamId = oauthjson.SelectToken("steamid").Value <string>(); } } // login to webapi data.Clear(); data.Add("access_token", state.OAuthToken); response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data); var sessionid = cookies.GetCookies(new Uri(COMMUNITY_BASE + "/"))["sessionid"].Value; if (state.RequiresActivation == false) { data.Clear(); data.Add("op", "has_phone"); data.Add("arg", "null"); data.Add("sessionid", sessionid); response = Request(COMMUNITY_BASE + "/steamguard/phoneajax", "POST", data, cookies); var jsonresponse = JObject.Parse(response); bool hasPhone = jsonresponse.SelectToken("has_phone").Value <Boolean>(); if (hasPhone == false) { state.OAuthToken = null; // force new login state.RequiresLogin = true; state.Cookies = null; state.Error = "Your Steam account must have a SMS-capable phone number attached. Go into Account Details of the Steam client or Steam website and click Add a Phone Number."; return(false); } //response = Request(COMMUNITY_BASE + "/steamguard/phone_checksms?bForTwoFactor=1&bRevoke2fOnCancel=", "GET", null, cookies); // add a new authenticator data.Clear(); string deviceId = BuildRandomId(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("authenticator_type", "1"); data.Add("device_identifier", deviceId); data.Add("sms_phone_id", "1"); response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data); var tfaresponse = JObject.Parse(response); if (response.IndexOf("status") == -1 && tfaresponse.SelectToken("response.status").Value <int>() == 84) { // invalid response state.OAuthToken = null; // force new login state.RequiresLogin = true; state.Cookies = null; state.Error = "Unable to send SMS. Check your phone is registered on your Steam account."; return(false); } if (response.IndexOf("shared_secret") == -1) { // invalid response state.OAuthToken = null; // force new login state.RequiresLogin = true; state.Cookies = null; state.Error = "Invalid response from Steam: " + response; return(false); } // save data into this authenticator var secret = tfaresponse.SelectToken("response.shared_secret").Value <string>(); this.SecretKey = Convert.FromBase64String(secret); this.Serial = tfaresponse.SelectToken("response.serial_number").Value <string>(); this.DeviceId = deviceId; state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value <string>(); // add the steamid into the data var steamdata = JObject.Parse(tfaresponse.SelectToken("response").ToString()); if (steamdata.SelectToken("steamid") == null) { steamdata.Add("steamid", state.SteamId); } if (steamdata.SelectToken("steamguard_scheme") == null) { steamdata.Add("steamguard_scheme", "2"); } this.SteamData = steamdata.ToString(Newtonsoft.Json.Formatting.None); // calculate server drift long servertime = tfaresponse.SelectToken("response.server_time").Value <long>() * 1000; ServerTimeDiff = servertime - CurrentTime; LastServerTime = DateTime.Now.Ticks; state.RequiresActivation = true; return(false); } // finalize adding the authenticator data.Clear(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("activation_code", state.ActivationCode); // try and authorise var retries = 0; while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES) { data.Add("authenticator_code", this.CalculateCode(false)); data.Add("authenticator_time", this.ServerTime.ToString()); response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data); var finalizeresponse = JObject.Parse(response); if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value <int>() == INVALID_ACTIVATION_CODE) { state.Error = "Invalid activation code"; return(false); } // reset our time if (response.IndexOf("server_time") != -1) { long servertime = finalizeresponse.SelectToken("response.server_time").Value <long>() * 1000; ServerTimeDiff = servertime - CurrentTime; LastServerTime = DateTime.Now.Ticks; } // check success if (finalizeresponse.SelectToken("response.success").Value <bool>() == true) { if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value <bool>() == true) { ServerTimeDiff += 30000L; retries++; continue; } state.RequiresActivation = false; break; } ServerTimeDiff += 30000L; retries++; } if (state.RequiresActivation == true) { state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later."; return(false); } // mark and successful and return key state.Success = true; state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey); // send confirmation email data.Clear(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("email_type", "2"); response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data); return(true); } catch (UnauthorisedRequestException ex) { throw new InvalidEnrollResponseException("You are not allowed to add an authenticator. Have you enabled 'community-generated content' in Family View?", ex); } catch (InvalidRequestException ex) { throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex); } }
/// <summary> /// Configure the Yubikey /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void yubiSecretUpdateButton_Click(object sender, EventArgs e) { if (yubiSecretField.Text.Trim().Length == 0) { WinAuthForm.ErrorDialog(this, "Please enter a secret phase or password"); return; } if (WinAuthForm.ConfirmDialog(this, "This will overwrite any existing data on your YubiKey.\n\nAre you sure you want to continue?", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2) != System.Windows.Forms.DialogResult.Yes) { return; } int slot = (yubiSlotToggle.Checked == true ? 2 : 1); bool press = yubiPressToggle.Checked; // bug in YubiKey 3.2.x (and below?) where using keypress doesn't always work // see http://forum.yubico.com/viewtopic.php?f=26&t=1571 if (press == true && (this.Yubikey.Info.Status.VersionMajor < 3 || (this.Yubikey.Info.Status.VersionMajor == 3 && this.Yubikey.Info.Status.VersionMinor <= 3))) { if (WinAuthForm.ConfirmDialog(this, "This is a known issue using \"Require button press\" with YubiKeys that have firmware version 3.3 and below. It can cause intermittent problems when reading the Challenge-Response. You can contact Yubico and may be able to get a free replacement.\n\nDo you want to continue anyway?", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2) != System.Windows.Forms.DialogResult.Yes) { return; } } // calculate the actual key. This is a byte version of the string, salt="mnemonic"(+user password TBD), PBKDF 2048 times, return 20byte/160bit key byte[] bytes = Encoding.UTF8.GetBytes(yubiSecretField.Text.Trim()); string salt = "mnemonic"; byte[] saltbytes = Encoding.UTF8.GetBytes(salt); Rfc2898DeriveBytes kg = new Rfc2898DeriveBytes(bytes, saltbytes, YUBIKEY_PBKDF2_ITERATIONS); byte[] key = kg.GetBytes(YUBIKEY_PBKDF2_KEYSIZE); try { this.Yubikey.SetChallengeResponse(slot, key, key.Length, press); } catch (YubKeyException ex) { WinAuthForm.ErrorDialog(this, ex.Message, ex); return; } if (press == true) { if (WinAuthForm.ConfirmDialog(this, "Your YubiKey slot will now be verified. Please click its button when it flashes." + Environment.NewLine + Environment.NewLine + "Continue?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != System.Windows.Forms.DialogResult.Yes) { WinAuthForm.ErrorDialog(this, "Your YubiKey has been updated. Please verify it before continuing."); return; } } // perform the test encryption/decryption using the yubi try { string challenge = "WinAuth"; string plain = Authenticator.ByteArrayToString(Encoding.ASCII.GetBytes(challenge)); Authenticator.PasswordTypes passwordType = (slot == 1 ? Authenticator.PasswordTypes.YubiKeySlot1 : Authenticator.PasswordTypes.YubiKeySlot2); string encrypted = Authenticator.EncryptSequence(plain, passwordType, null, this.Yubikey); plain = Authenticator.DecryptSequence(encrypted, passwordType, null, this.Yubikey); string response = Encoding.ASCII.GetString(Authenticator.StringToByteArray(plain)); if (challenge != response) { throw new ApplicationException("verification failed"); } } catch (ApplicationException ex) { WinAuthForm.ErrorDialog(this, "The YubiKey test failed. Please try configuring it again or doing it manually. (" + ex.Message + ")"); return; } YubikeySlot = slot; WinAuthForm.ErrorDialog(this, "Your YubiKey has been successfully updated."); }
public void ReadXml(XmlReader reader, string password = null) { reader.MoveToContent(); if (reader.IsEmptyElement) { reader.Read(); return; } reader.Read(); while (reader.EOF == false) { if (reader.IsStartElement()) { switch (reader.Name) { case "modifiers": Modifiers = (WinAPI.KeyModifiers)BitConverter.ToInt32(Authenticator.StringToByteArray(reader.ReadElementContentAsString()), 0); break; case "hotkey": HotKey = (WinAPI.VirtualKeyCode)BitConverter.ToUInt16(Authenticator.StringToByteArray(reader.ReadElementContentAsString()), 0); break; case "windowtitle": WindowTitle = reader.ReadElementContentAsString(); break; case "windowtitleregex": WindowTitleRegex = reader.ReadElementContentAsBoolean(); break; case "processname": ProcessName = reader.ReadElementContentAsString(); break; case "advanced": Advanced = reader.ReadElementContentAsBoolean(); break; case "script": string encrypted = reader.GetAttribute("encrypted"); string data = reader.ReadElementContentAsString(); if (string.IsNullOrEmpty(encrypted) == false) { Authenticator.PasswordTypes passwordType = Authenticator.DecodePasswordTypes(encrypted); data = Authenticator.DecryptSequence(data, passwordType, password, true); //byte[] plain = Authenticator.StringToByteArray(data); //data = Encoding.UTF8.GetString(plain, 0, plain.Length); /* * char[] encTypes = encrypted.ToCharArray(); * // we read the string in reverse order (the order they were encrypted) * for (int i = encTypes.Length - 1; i >= 0; i--) * { * char encryptedType = encTypes[i]; * switch (encryptedType) * { * case 'u': * { * // we are going to decrypt with the Windows User account key * byte[] cipher = Authenticator.StringToByteArray(data); * byte[] plain = ProtectedData.Unprotect(cipher, null, DataProtectionScope.CurrentUser); * data = Encoding.UTF8.GetString(plain, 0, plain.Length); * break; * } * case 'm': * { * // we are going to decrypt with the Windows local machine key * byte[] cipher = Authenticator.StringToByteArray(data); * byte[] plain = ProtectedData.Unprotect(cipher, null, DataProtectionScope.LocalMachine); * data = Encoding.UTF8.GetString(plain, 0, plain.Length); * break; * } * case 'y': * { * // we use an explicit password to encrypt data * if (string.IsNullOrEmpty(password) == true) * { * throw new EncryptedSecretDataException(); * } * data = Authenticator.Decrypt(data, password, true); * byte[] plain = Authenticator.StringToByteArray(data); * data = Encoding.UTF8.GetString(plain, 0, plain.Length); * break; * } * default: * break; * } * } */ } AdvancedScript = data; break; default: reader.Skip(); break; } } else { reader.Read(); break; } } }
/// <summary> /// Create a new HotKeySequence from a loaded string /// </summary> /// <param name="data">XmlNode from config</param> public HoyKeySequence(XmlNode autoLoginNode, string password, decimal version) { bool boolVal = false; XmlNode node = autoLoginNode.SelectSingleNode("modifiers"); if (node != null && node.InnerText.Length != 0) { Modifiers = (WinAPI.KeyModifiers)BitConverter.ToInt32(Authenticator.StringToByteArray(node.InnerText), 0); } node = autoLoginNode.SelectSingleNode("hotkey"); if (node != null && node.InnerText.Length != 0) { HotKey = (WinAPI.VirtualKeyCode)BitConverter.ToUInt16(Authenticator.StringToByteArray(node.InnerText), 0); } node = autoLoginNode.SelectSingleNode("windowtitle"); if (node != null && node.InnerText.Length != 0) { WindowTitle = node.InnerText; } node = autoLoginNode.SelectSingleNode("windowtitleregex"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { WindowTitleRegex = boolVal; } node = autoLoginNode.SelectSingleNode("processname"); if (node != null && node.InnerText.Length != 0) { ProcessName = node.InnerText; } node = autoLoginNode.SelectSingleNode("advanced"); if (node != null && bool.TryParse(node.InnerText, out boolVal) == true) { Advanced = boolVal; } node = autoLoginNode.SelectSingleNode("script"); if (node != null && node.InnerText.Length != 0) { string data = node.InnerText; XmlAttribute attr = node.Attributes["encrypted"]; if (attr != null && attr.InnerText.Length != 0) { char[] encTypes = attr.InnerText.ToCharArray(); // we read the string in reverse order (the order they were encrypted) for (int i = encTypes.Length - 1; i >= 0; i--) { char encryptedType = encTypes[i]; switch (encryptedType) { case 'u': { // we are going to decrypt with the Windows User account key byte[] cipher = Authenticator.StringToByteArray(data); byte[] plain = ProtectedData.Unprotect(cipher, null, DataProtectionScope.CurrentUser); data = Encoding.UTF8.GetString(plain, 0, plain.Length); break; } case 'm': { // we are going to decrypt with the Windows local machine key byte[] cipher = Authenticator.StringToByteArray(data); byte[] plain = ProtectedData.Unprotect(cipher, null, DataProtectionScope.LocalMachine); data = Encoding.UTF8.GetString(plain, 0, plain.Length); break; } case 'y': { // we use an explicit password to encrypt data if (string.IsNullOrEmpty(password) == true) { throw new EncryptedSecretDataException(); } data = Authenticator.Decrypt(data, password, (version >= (decimal)1.7)); // changed encrypted in 1.7 byte[] plain = Authenticator.StringToByteArray(data); data = Encoding.UTF8.GetString(plain, 0, plain.Length); break; } default: break; } } } AdvancedScript = data; } }
/// <summary> /// Enroll the authenticator with the server /// </summary> public bool Enroll(EnrollState state) { // clear error state.Error = null; try { var data = new NameValueCollection(); var cookies = state.Cookies = state.Cookies ?? new CookieContainer(); string response; if (string.IsNullOrEmpty(state.OAuthToken) == true) { // get session response = Request(COMMUNITY_BASE + "/login/home?goto=0", "GET", null, cookies); // get the user's RSA key data.Add("username", state.Username); response = Request(COMMUNITY_BASE + "/login/getrsakey", "POST", data, cookies); var rsaresponse = JObject.Parse(response); if (rsaresponse.SelectToken("success").Value <bool>() != true) { throw new InvalidEnrollResponseException("Cannot get steam information for user: "******"steamid").Value <string>(); // encrypt password with RSA key RNGCryptoServiceProvider random = new RNGCryptoServiceProvider(); byte[] encryptedPassword; using (var rsa = new RSACryptoServiceProvider()) { var passwordBytes = Encoding.ASCII.GetBytes(state.Password); var p = rsa.ExportParameters(false); p.Exponent = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_exp").Value <string>()); p.Modulus = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value <string>()); rsa.ImportParameters(p); encryptedPassword = rsa.Encrypt(passwordBytes, false); } // login request data = new NameValueCollection(); data.Add("password", Convert.ToBase64String(encryptedPassword)); data.Add("username", state.Username); data.Add("twofactorcode", ""); data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty)); data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile"); data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1")); data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters")); data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId : string.Empty)); data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value <string>()); data.Add("remember_login", "false"); data.Add("oauth_client_id", "DE45CD61"); data.Add("oauth_scope", "read_profile write_profile read_client write_client"); data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString()); response = Request(COMMUNITY_BASE + "/mobilelogin/dologin/", "POST", data, cookies); Dictionary <string, object> loginresponse = JsonConvert.DeserializeObject <Dictionary <string, object> >(response); // require captcha if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true) { state.RequiresCaptcha = true; state.CaptchaId = (string)loginresponse["captcha_gid"]; state.CaptchaUrl = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId; } else { state.RequiresCaptcha = false; state.CaptchaId = null; state.CaptchaUrl = null; state.CaptchaText = null; } // require email auth if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true) { if (loginresponse.ContainsKey("emaildomain") == true) { var emaildomain = (string)loginresponse["emaildomain"]; if (string.IsNullOrEmpty(emaildomain) == false) { state.EmailDomain = emaildomain; } } state.RequiresEmailAuth = true; } else { state.EmailDomain = null; state.RequiresEmailAuth = false; } // require email auth if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true) { state.Requires2FA = true; } else { state.Requires2FA = false; } // if we didn't login, return the result if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false) { if (loginresponse.ContainsKey("oauth") == false) { state.Error = "No OAuth token in response"; } if (loginresponse.ContainsKey("message") == true) { state.Error = (string)loginresponse["message"]; } return(false); } // get the OAuth token - is stringified json string oauth = (string)loginresponse["oauth"]; var oauthjson = JObject.Parse(oauth); state.OAuthToken = oauthjson.SelectToken("oauth_token").Value <string>(); } // login to webapi data.Clear(); data.Add("access_token", state.OAuthToken); response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data); if (state.RequiresActivation == false) { // add a new authenticator data.Clear(); string deviceId = BuildRandomId(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("authenticator_type", "1"); data.Add("device_identifier", deviceId); response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data); var tfaresponse = JObject.Parse(response); if (response.IndexOf("revocation_code") == -1) { // invalid response state.OAuthToken = null; // force new login state.RequiresLogin = true; state.Cookies = null; state.Error = "Invalid response from Steam: " + response; return(false); } // save data into this authenticator var secret = tfaresponse.SelectToken("response.shared_secret").Value <string>(); this.SecretKey = Convert.FromBase64String(secret); this.Serial = tfaresponse.SelectToken("response.serial_number").Value <string>(); this.DeviceId = deviceId; this.RevocationCode = state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value <string>(); // calculate server drift long servertime = tfaresponse.SelectToken("response.server_time").Value <long>() * 1000; ServerTimeDiff = servertime - CurrentTime; LastServerTime = DateTime.Now.Ticks; // send authorisation email data.Clear(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("email_type", "1"); data.Add("include_activation", "1"); response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data); state.RequiresActivation = true; return(false); } // finalize adding the authenticator data.Clear(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("activation_code", state.ActivationCode); // try and authorise ServerTimeDiff -= 40000; // start at previous interval var retries = 0; while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES) { data.Add("authenticator_code", this.CalculateCode(false)); data.Add("authenticator_time", this.ServerTime.ToString()); response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data); var finalizeresponse = JObject.Parse(response); if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value <int>() == INVALID_ACTIVATION_CODE) { state.Error = "Invalid activation code"; return(false); } // reset our time if (response.IndexOf("server_time") != -1) { long servertime = finalizeresponse.SelectToken("response.server_time").Value <long>() * 1000; ServerTimeDiff = servertime - CurrentTime; LastServerTime = DateTime.Now.Ticks; } // check success if (finalizeresponse.SelectToken("response.success").Value <bool>() == true) { if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value <bool>() == true) { ServerTimeDiff += 30000L; retries++; continue; } state.RequiresActivation = false; break; } ServerTimeDiff += 30000L; retries++; } if (state.RequiresActivation == true) { state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later."; return(false); } // mark and successful and return key state.Success = true; state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey); // send confirmation email data.Clear(); data.Add("access_token", state.OAuthToken); data.Add("steamid", state.SteamId); data.Add("email_type", "2"); response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data); return(true); } catch (InvalidRequestException ex) { throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex); } }