/// <summary> /// Form loaded event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ShowSteamSecretForm_Load(object sender, EventArgs e) { this.revocationcodeField.SecretMode = true; SteamAuthenticator authenticator = CurrentAuthenticator.AuthenticatorData as SteamAuthenticator; this.revocationcodeField.Text = authenticator.RevocationCode; }
/// <summary> /// Import the selected SDA account /// </summary> /// <returns>true if successful</returns> private bool ImportSDA() { var entry = importSDAList.SelectedItem as ImportedSDAEntry; if (entry == null) { WinAuthForm.ErrorDialog(this, "Please load and select a Steam account"); return(false); } var auth = new SteamAuthenticator(); var token = JObject.Parse(entry.json); foreach (var prop in token.Root.Children().ToList()) { var child = token.SelectToken(prop.Path); var lkey = prop.Path.ToLower(); if (lkey == "fully_enrolled" || lkey == "session") { prop.Remove(); } else if (lkey == "device_id") { auth.DeviceId = child.Value <string>(); prop.Remove(); } else if (lkey == "serial_number") { auth.Serial = child.Value <string>(); } else if (lkey == "account_name") { if (nameField.Text.Length == 0) { nameField.Text = "Steam (" + child.Value <string>() + ")"; } } else if (lkey == "shared_secret") { auth.SecretKey = Convert.FromBase64String(child.Value <string>()); } } auth.SteamData = token.ToString(Formatting.None); Authenticator.AuthenticatorData = auth; return(true); }
/// <summary> /// Form loaded event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ShowSteamSecretForm_Load(object sender, EventArgs e) { this.revocationcodeField.SecretMode = true; this.deviceidField.SecretMode = true; this.steamdataField.SecretMode = false; SteamAuthenticator authenticator = CurrentAuthenticator.AuthenticatorData as SteamAuthenticator; this.deviceidField.Text = authenticator.DeviceId; if (string.IsNullOrEmpty(authenticator.SteamData) == false && authenticator.SteamData[0] == '{') { this.revocationcodeField.Text = JObject.Parse(authenticator.SteamData).SelectToken("revocation_code").Value <string>(); if (authenticator.SteamData.IndexOf("shared_secret") != -1) { this.steamdataField.SecretMode = true; this.steamdataField.Text = authenticator.SteamData; this.steamdataField.ForeColor = SystemColors.ControlText; } } else { this.revocationcodeField.Text = authenticator.SteamData; } }
/// <summary> /// Create a new SteamClient /// </summary> public SteamClient(SteamAuthenticator auth, string session = null) { this.Authenticator = auth; this.Session = new SteamSession(session); if (this.Session.Confirmations != null) { if (this.IsLoggedIn() == false) { this.Session.Confirmations = null; } else { Task.Factory.StartNew(() => { Refresh(); PollConfirmations(this.Session.Confirmations); }); } } }
/// <summary> /// Verify and create the authenticator if needed /// </summary> /// <returns>true is successful</returns> private bool verifyAuthenticator(string privatekey) { if (string.IsNullOrEmpty(privatekey) == true) { return(false); } this.Authenticator.Name = nameField.Text; int digits = (this.Authenticator.AuthenticatorData != null ? this.Authenticator.AuthenticatorData.CodeDigits : GoogleAuthenticator.DEFAULT_CODE_DIGITS); if (string.IsNullOrEmpty(digitsField.Text) == true || int.TryParse(digitsField.Text, out digits) == false || digits <= 0) { return(false); } WinAuth.Authenticator.HMACTypes hmac = WinAuth.Authenticator.HMACTypes.SHA1; #if NETFX_3 try { hmac = (WinAuth.Authenticator.HMACTypes)Enum.Parse(typeof(WinAuth.Authenticator.HMACTypes), (string)hashField.SelectedItem, true); } catch (Exception) { } #else Enum.TryParse <WinAuth.Authenticator.HMACTypes>((string)hashField.SelectedItem, out hmac); #endif string authtype = timeBasedRadio.Checked == true ? TOTP : HOTP; int period = 0; if (string.IsNullOrEmpty(intervalField.Text) == true || int.TryParse(intervalField.Text, out period) == false || period <= 0) { return(false); } long counter = 0; // if this is a URL, pull it down Uri uri; Match match; if (Regex.IsMatch(privatekey, "https?://.*") == true && Uri.TryCreate(privatekey, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "Cannot load QR code image from " + privatekey, ex); return(false); } } else if ((match = Regex.Match(privatekey, @"data:image/([^;]+);base64,(.*)", RegexOptions.IgnoreCase)).Success == true) { byte[] imagedata = Convert.FromBase64String(match.Groups[2].Value); using (MemoryStream ms = new MemoryStream(imagedata)) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(ms)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } else if (IsValidFile(privatekey) == true) { // assume this is the image file using (Bitmap bitmap = (Bitmap)Bitmap.FromFile(privatekey)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = result.Text; } } } string issuer = null; string serial = null; // check for otpauth://, e.g. "otpauth://totp/[email protected]?secret=IHZJDKAEEC774BMUK3GX6SA" match = Regex.Match(privatekey, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { authtype = match.Groups[1].Value.ToLower(); string label = match.Groups[2].Value; int p = label.IndexOf(":"); if (p != -1) { issuer = label.Substring(0, p); label = label.Substring(p + 1); } NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); privatekey = qs["secret"] ?? privatekey; int querydigits; if (int.TryParse(qs["digits"], out querydigits) && querydigits != 0) { digits = querydigits; } if (qs["counter"] != null) { long.TryParse(qs["counter"], out counter); } issuer = qs["issuer"]; if (string.IsNullOrEmpty(issuer) == false) { label = issuer + (string.IsNullOrEmpty(label) == false ? " (" + label + ")" : string.Empty); } serial = qs["serial"]; if (string.IsNullOrEmpty(label) == false) { this.Authenticator.Name = this.nameField.Text = label; } string periods = qs["period"]; if (string.IsNullOrEmpty(periods) == false) { int.TryParse(periods, out period); } if (qs["algorithm"] != null) { #if NETFX_3 try { hmac = (WinAuth.Authenticator.HMACTypes)Enum.Parse(typeof(WinAuth.Authenticator.HMACTypes), qs["algorithm"], true); hashField.SelectedItem = hmac.ToString(); } catch (Exception) { } #else if (Enum.TryParse <WinAuth.Authenticator.HMACTypes>(qs["algorithm"], true, out hmac) == true) { hashField.SelectedItem = hmac.ToString(); } #endif } } // just get the hex chars privatekey = Regex.Replace(privatekey, @"[^0-9a-z]", "", RegexOptions.IgnoreCase); if (privatekey.Length == 0) { WinAuthForm.ErrorDialog(this.Owner, "The secret code is not valid"); return(false); } try { Authenticator auth; if (authtype == TOTP) { if (string.Compare(issuer, "BattleNet", true) == 0) { 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(); ((BattleNetAuthenticator)auth).SecretKey = Base32.getInstance().Decode(privatekey); ((BattleNetAuthenticator)auth).Serial = serial; issuer = string.Empty; } else if (issuer == "Steam") { auth = new SteamAuthenticator(); ((SteamAuthenticator)auth).SecretKey = Base32.getInstance().Decode(privatekey); ((SteamAuthenticator)auth).Serial = string.Empty; ((SteamAuthenticator)auth).DeviceId = string.Empty; //((SteamAuthenticator)auth).RevocationCode = string.Empty; ((SteamAuthenticator)auth).SteamData = string.Empty; this.Authenticator.Skin = null; issuer = string.Empty; } else { auth = new GoogleAuthenticator(); ((GoogleAuthenticator)auth).Enroll(privatekey); } timer.Enabled = true; codeProgress.Visible = true; timeBasedRadio.Checked = true; } else if (authtype == HOTP) { auth = new HOTPAuthenticator(); if (counterField.Text.Trim().Length != 0) { long.TryParse(counterField.Text.Trim(), out counter); } ((HOTPAuthenticator)auth).Enroll(privatekey, counter); // start with the next code timer.Enabled = false; codeProgress.Visible = false; counterBasedRadio.Checked = true; } else { WinAuthForm.ErrorDialog(this.Owner, "Only TOTP or HOTP authenticators are supported"); return(false); } auth.HMACType = hmac; auth.CodeDigits = digits; auth.Period = period; this.Authenticator.AuthenticatorData = auth; if (digits > 5) { codeField.SpaceOut = digits / 2; } else { codeField.SpaceOut = 0; } //string key = Base32.getInstance().Encode(this.Authenticator.AuthenticatorData.SecretKey); this.codeField.Text = auth.CurrentCode; codeProgress.Maximum = period; if (!(auth is HOTPAuthenticator) && auth.ServerTimeDiff == 0L && SyncErrorWarned == false) { SyncErrorWarned = true; MessageBox.Show(this, string.Format(strings.AuthenticatorSyncError, "Google"), WinAuthMain.APPLICATION_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception irre) { WinAuthForm.ErrorDialog(this.Owner, "Unable to create the authenticator. The secret code is probably invalid.", irre); return(false); } return(true); }
/// <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> /// Import an authenticator from the uuid and steamguard files /// </summary> /// <returns>true if successful</returns> private bool ImportSteamGuard() { var uuid = importUuid.Text.Trim(); if (uuid.Length == 0) { WinAuthForm.ErrorDialog(this, "Please enter the contents of the steam.uuid.xml file or your DeviceId"); return(false); } var steamguard = importSteamguard.Text.Trim(); if (steamguard.Length == 0) { WinAuthForm.ErrorDialog(this, "Please enter the contents of your SteamGuard file"); return(false); } // check the deviceid string deviceId; if (uuid.IndexOf("?xml") != -1) { try { var doc = new XmlDocument(); doc.LoadXml(uuid); var node = doc.SelectSingleNode("//string[@name='uuidKey']"); if (node == null) { WinAuthForm.ErrorDialog(this, "Cannot find uuidKey in xml"); return(false); } deviceId = node.InnerText; } catch (Exception ex) { WinAuthForm.ErrorDialog(this, "Invalid uuid xml: " + ex.Message); return(false); } } else { deviceId = uuid; } if (string.IsNullOrEmpty(deviceId) || Regex.IsMatch(deviceId, @"android:[0-9abcdef-]+", RegexOptions.Singleline | RegexOptions.IgnoreCase) == false) { WinAuthForm.ErrorDialog(this, "Invalid deviceid, expecting \"android:NNNN...\""); return(false); } // check the steamguard byte[] secret; string serial; try { var json = JObject.Parse(steamguard); var node = json.SelectToken("shared_secret"); if (node == null) { throw new ApplicationException("no shared_secret"); } secret = Convert.FromBase64String(node.Value <string>()); node = json.SelectToken("serial_number"); if (node == null) { throw new ApplicationException("no serial_number"); } serial = node.Value <string>(); } catch (Exception ex) { WinAuthForm.ErrorDialog(this, "Invalid SteamGuard JSON contents: " + ex.Message); return(false); } var auth = new SteamAuthenticator(); auth.SecretKey = secret; auth.Serial = serial; auth.SteamData = steamguard; auth.DeviceId = deviceId; Authenticator.AuthenticatorData = auth; return(true); }
/// <summary> /// Import the selected SDA account /// </summary> /// <returns>true if successful</returns> private bool ImportSDA() { var entry = this.importSDAList.SelectedItem as ImportedSDAEntry; if (entry == null) { WinAuthForm.ErrorDialog(this, "Please load and select a Steam account"); return false; } SteamAuthenticator auth = new SteamAuthenticator(); var token = JObject.Parse(entry.json); foreach (var prop in token.Root.Children().ToList()) { var child = token.SelectToken(prop.Path); string lkey = prop.Path.ToLower(); if (lkey == "fully_enrolled" || lkey == "session") { prop.Remove(); } else if (lkey == "device_id") { auth.DeviceId = child.Value<string>(); prop.Remove(); } else if (lkey == "serial_number") { auth.Serial = child.Value<string>(); } else if (lkey == "account_name") { if (this.nameField.Text.Length == 0) { this.nameField.Text = "Steam (" + child.Value<string>() + ")"; } } else if (lkey == "shared_secret") { auth.SecretKey = Convert.FromBase64String(child.Value<string>()); } } auth.SteamData = token.ToString(Newtonsoft.Json.Formatting.None); this.Authenticator.AuthenticatorData = auth; return true; }
/// <summary> /// Import an authenticator from the uuid and steamguard files /// </summary> /// <returns>true if successful</returns> private bool ImportSteamGuard() { string uuid = importUuid.Text.Trim(); if (uuid.Length == 0) { WinAuthForm.ErrorDialog(this, "Please enter the contents of the steam.uuid.xml file or your DeviceId"); return false; } string steamguard = this.importSteamguard.Text.Trim(); if (steamguard.Length == 0) { WinAuthForm.ErrorDialog(this, "Please enter the contents of your SteamGuard file"); return false; } // check the deviceid string deviceId; if (uuid.IndexOf("?xml") != -1) { try { XmlDocument doc = new XmlDocument(); doc.LoadXml(uuid); var node = doc.SelectSingleNode("//string[@name='uuidKey']"); if (node == null) { WinAuthForm.ErrorDialog(this, "Cannot find uuidKey in xml"); return false; } deviceId = node.InnerText; } catch (Exception ex) { WinAuthForm.ErrorDialog(this, "Invalid uuid xml: " + ex.Message); return false; } } else { deviceId = uuid; } if (string.IsNullOrEmpty(deviceId) || Regex.IsMatch(deviceId, @"android:[0-9abcdef-]+", RegexOptions.Singleline | RegexOptions.IgnoreCase) == false) { WinAuthForm.ErrorDialog(this, "Invalid deviceid, expecting \"android:NNNN...\""); return false; } // check the steamguard byte[] secret; string serial; try { var json = JObject.Parse(steamguard); var node = json.SelectToken("shared_secret"); if (node == null) { throw new ApplicationException("no shared_secret"); } secret = Convert.FromBase64String(node.Value<string>()); node = json.SelectToken("serial_number"); if (node == null) { throw new ApplicationException("no serial_number"); } serial = node.Value<string>(); } catch (Exception ex) { WinAuthForm.ErrorDialog(this, "Invalid SteamGuard JSON contents: " + ex.Message); return false; } SteamAuthenticator auth = new SteamAuthenticator(); auth.SecretKey = secret; auth.Serial = serial; auth.SteamData = steamguard; auth.DeviceId = deviceId; this.Authenticator.AuthenticatorData = auth; return true; }
/// <summary> /// Verify and create the authenticator if needed /// </summary> /// <returns>true is successful</returns> private bool verifyAuthenticator(string privatekey) { if (string.IsNullOrEmpty(privatekey) == true) { return false; } this.Authenticator.Name = nameField.Text; int digits = (this.Authenticator.AuthenticatorData != null ? this.Authenticator.AuthenticatorData.CodeDigits : GoogleAuthenticator.DEFAULT_CODE_DIGITS); string authtype = timeBasedRadio.Checked == true ? TOTP : HOTP; long counter = 0; // if this is a URL, pull it down Uri uri; Match match; if (Regex.IsMatch(privatekey, "https?://.*") == true && Uri.TryCreate(privatekey, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "Cannot load QR code image from " + privatekey, ex); return false; } } else if ((match = Regex.Match(privatekey, @"data:image/([^;]+);base64,(.*)", RegexOptions.IgnoreCase)).Success == true) { byte[] imagedata = Convert.FromBase64String(match.Groups[2].Value); using (MemoryStream ms = new MemoryStream(imagedata)) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(ms)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } else if (IsValidFile(privatekey) == true) { // assume this is the image file using (Bitmap bitmap = (Bitmap)Bitmap.FromFile(privatekey)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = result.Text; } } } string issuer = null; string serial = null; // check for otpauth://, e.g. "otpauth://totp/[email protected]?secret=IHZJDKAEEC774BMUK3GX6SA" match = Regex.Match(privatekey, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { authtype = match.Groups[1].Value.ToLower(); string label = match.Groups[2].Value; int p = label.IndexOf(":"); if (p != -1) { issuer = label.Substring(0, p); label = label.Substring(p + 1); } NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); privatekey = qs["secret"] ?? privatekey; int querydigits; if (int.TryParse(qs["digits"], out querydigits) && querydigits != 0) { digits = querydigits; } if (qs["counter"] != null) { long.TryParse(qs["counter"], out counter); } issuer = qs["issuer"]; if (string.IsNullOrEmpty(issuer) == false) { label = issuer + (string.IsNullOrEmpty(label) == false ? " (" + label + ")" : string.Empty); } serial = qs["serial"]; if (string.IsNullOrEmpty(label) == false) { this.Authenticator.Name = this.nameField.Text = label; } } // just get the hex chars privatekey = Regex.Replace(privatekey, @"[^0-9a-z]", "", RegexOptions.IgnoreCase); if (privatekey.Length == 0) { WinAuthForm.ErrorDialog(this.Owner, "The secret code is not valid"); return false; } try { Authenticator auth; if (authtype == TOTP) { if (string.Compare(issuer, "BattleNet", true) == 0) { 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(); ((BattleNetAuthenticator)auth).SecretKey = Base32.getInstance().Decode(privatekey); ((BattleNetAuthenticator)auth).Serial = serial; issuer = string.Empty; } else if (issuer == "Steam") { auth = new SteamAuthenticator(); ((SteamAuthenticator)auth).SecretKey = Base32.getInstance().Decode(privatekey); ((SteamAuthenticator)auth).Serial = string.Empty; ((SteamAuthenticator)auth).DeviceId = string.Empty; //((SteamAuthenticator)auth).RevocationCode = string.Empty; ((SteamAuthenticator)auth).SteamData = string.Empty; this.Authenticator.Skin = null; issuer = string.Empty; } else { auth = new GoogleAuthenticator(); ((GoogleAuthenticator)auth).Enroll(privatekey); } timer.Enabled = true; codeProgress.Visible = true; timeBasedRadio.Checked = true; } else if (authtype == HOTP) { auth = new HOTPAuthenticator(); if (counterField.Text.Trim().Length != 0) { long.TryParse(counterField.Text.Trim(), out counter); } ((HOTPAuthenticator)auth).Enroll(privatekey, counter); // start with the next code timer.Enabled = false; codeProgress.Visible = false; counterBasedRadio.Checked = true; } else { WinAuthForm.ErrorDialog(this.Owner, "Only TOTP or HOTP authenticators are supported"); return false; } auth.CodeDigits = digits; this.Authenticator.AuthenticatorData = auth; if (digits > 5) { codeField.SpaceOut = digits / 2; } else { codeField.SpaceOut = 0; } //string key = Base32.getInstance().Encode(this.Authenticator.AuthenticatorData.SecretKey); this.codeField.Text = auth.CurrentCode; if (!(auth is HOTPAuthenticator) && auth.ServerTimeDiff == 0L && SyncErrorWarned == false) { SyncErrorWarned = true; MessageBox.Show(this, string.Format(strings.AuthenticatorSyncError, "Google"), WinAuthMain.APPLICATION_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception irre) { WinAuthForm.ErrorDialog(this.Owner, "Unable to create the authenticator. The secret code is probably invalid.", irre); return false; } return true; }