public static Authenticator FromOtpAuthUri(string uri) { var uriMatch = Regex.Match(Uri.UnescapeDataString(uri), @"^otpauth:\/\/([a-z]+)\/(.*)\?(.*)$"); if (!uriMatch.Success) { throw new ArgumentException("URI is not valid"); } var type = uriMatch.Groups[1].Value switch { "totp" => AuthenticatorType.Totp, "hotp" => AuthenticatorType.Hotp, _ => throw new ArgumentException("Unknown type") }; // Get the issuer and username if possible var issuerUsername = uriMatch.Groups[2].Value; var issuerUsernameMatch = Regex.Match(issuerUsername, @"^(.*):(.*)$"); var queryString = uriMatch.Groups[3].Value; var args = Regex.Matches(queryString, "([^?=&]+)(=([^&]*))?") .ToDictionary(x => x.Groups[1].Value, x => x.Groups[3].Value); string issuer; string username; if (issuerUsernameMatch.Success) { issuer = issuerUsernameMatch.Groups[1].Value; username = issuerUsernameMatch.Groups[2].Value; } else { if (args.ContainsKey("issuer")) { issuer = args["issuer"]; username = issuerUsername; } else { issuer = uriMatch.Groups[2].Value; username = null; } } var algorithm = DefaultAlgorithm; if (args.ContainsKey("algorithm")) { algorithm = args["algorithm"].ToUpper() switch { "SHA1" => OtpHashMode.Sha1, "SHA256" => OtpHashMode.Sha256, "SHA512" => OtpHashMode.Sha512, _ => throw new ArgumentException("Unknown algorithm") } } ; var digits = DefaultDigits; if (args.ContainsKey("digits") && !Int32.TryParse(args["digits"], out digits)) { throw new ArgumentException("Digits parameter cannot be parsed."); } var period = DefaultPeriod; if (args.ContainsKey("period") && !Int32.TryParse(args["period"], out period)) { throw new ArgumentException("Period parameter cannot be parsed."); } var counter = 0; if (type == AuthenticatorType.Hotp && args.ContainsKey("counter") && !Int32.TryParse(args["counter"], out counter)) { throw new ArgumentException("Counter parameter cannot be parsed."); } if (counter < 0) { throw new ArgumentException("Counter cannot be negative."); } if (!args.ContainsKey("secret")) { throw new ArgumentException("Secret parameter is required."); } var secret = CleanSecret(args["secret"], type); var auth = new Authenticator { Secret = secret, Issuer = issuer.Trim().Truncate(IssuerMaxLength), Username = username?.Trim().Truncate(UsernameMaxLength), Icon = Shared.Data.Icon.FindServiceKeyByName(issuer), Type = type, Algorithm = algorithm, Digits = digits, Period = period, Counter = counter }; if (!auth.IsValid()) { throw new ArgumentException("Authenticator is not valid."); } return(auth); }
public static Authenticator FromOtpAuthMigrationAuthenticator(OtpAuthMigration.Authenticator input) { string issuer; string username; // Google Auth may not have an issuer, just use the username instead if (String.IsNullOrEmpty(input.Issuer)) { issuer = input.Username.Trim().Truncate(IssuerMaxLength); username = null; } else { issuer = input.Issuer.Trim().Truncate(IssuerMaxLength); // For some odd reason the username field always follows a '[issuer]: [username]' format username = input.Username.Replace($"{input.Issuer}: ", "").Trim().Truncate(UsernameMaxLength); } var type = input.Type switch { OtpAuthMigration.Type.Totp => AuthenticatorType.Totp, OtpAuthMigration.Type.Hotp => AuthenticatorType.Hotp, _ => throw new ArgumentException() }; var algorithm = input.Algorithm switch { OtpAuthMigration.Algorithm.Sha1 => OtpHashMode.Sha1, _ => throw new ArgumentException() }; string secret; try { secret = Base32Encoding.ToString(input.Secret); secret = CleanSecret(secret, type); } catch { throw new ArgumentException(); } var auth = new Authenticator { Issuer = issuer, Username = username, Algorithm = algorithm, Type = type, Secret = secret, Counter = input.Counter, Digits = DefaultDigits, Period = DefaultPeriod, Icon = Shared.Data.Icon.FindServiceKeyByName(issuer) }; if (!auth.IsValid()) { throw new ArgumentException(); } return(auth); }