コード例 #1
0
        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);
        }
コード例 #2
0
        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);
        }