private static Uri BuildUri(string label, string passwordType, string algorithmName, int passwordLength, byte[] secret, string additionalQueryArguments) { string base32Secret = OnetimePasswordSecret.ToBase32(secret); var uriString = $"{Globals.OtpScheme}://{passwordType}/{label}?secret={Uri.EscapeDataString(base32Secret)}&algorithm={Uri.EscapeDataString(algorithmName)}&digits={passwordLength}"; if (!String.IsNullOrEmpty(additionalQueryArguments)) { uriString += "&" + additionalQueryArguments; } return(new Uri(uriString, UriKind.Absolute)); }
public static OnetimePasswordAccount FromOtpUrl(Uri url) { if (url == null) { throw new ArgumentNullException(nameof(url)); } if (String.Compare(url.Scheme, Globals.OtpScheme, StringComparison.OrdinalIgnoreCase) != 0) { throw new ArgumentException($"Url authority is not {Globals.OtpScheme}.", nameof(url)); } if (url.Segments.Length < 2) { throw new ArgumentException("Url does not contain sufficient segments for an otp url.", nameof(url)); } var passwordType = url.Authority; if (String.Compare(passwordType, Globals.HotpAuthority, StringComparison.OrdinalIgnoreCase) != 0 && String.Compare(passwordType, Globals.TotpAuthority, StringComparison.OrdinalIgnoreCase) != 0) { throw new ArgumentException("Url contains an unknown password type.", nameof(url)); } var metadata = new Dictionary <string, string>(); byte[] secret = null; var label = Uri.UnescapeDataString(url.Segments[1]); if (label.Contains(":")) { metadata["issuer"] = label.Substring(0, label.IndexOf(":")); } var queryParts = url.Query.Substring(1).Split('&'); var passwordGenerator = (String.Compare(passwordType, Globals.HotpAuthority, StringComparison.OrdinalIgnoreCase) == 0 ? (IOnetimePasswordGenerator) new CounterBasedPasswordGenerator() : (IOnetimePasswordGenerator) new TimeBasedPasswordGenerator()); foreach (var queryPart in queryParts) { var queryOption = queryPart.Split('='); var key = Uri.UnescapeDataString(queryOption[0]); var value = Uri.UnescapeDataString(queryOption[1]); switch (key.ToUpperInvariant()) { case "ALGORITHM": passwordGenerator.HashAlgorithm = HasherFromName(value) ?? passwordGenerator.HashAlgorithm; break; case "DIGITS": passwordGenerator.PasswordLength = Convert.ToInt32(value); break; case "COUNTER": ((CounterBasedPasswordGenerator)passwordGenerator).Counter = Convert.ToInt64(value); break; case "PERIOD": case "INTERVAL": ((TimeBasedPasswordGenerator)passwordGenerator).TimeInterval = TimeSpan.FromSeconds(Convert.ToInt64(value)); break; case "SECRET": secret = OnetimePasswordSecret.FromBase32(value); passwordGenerator.SetSecret(secret); break; default: metadata[key] = value; break; } } var retVal = new OnetimePasswordAccount() { Label = label, Secret = secret, Metadata = metadata, Issuer = metadata.ContainsKey("Issuer") ? metadata["Issuer"] : null, PasswordGenerator = passwordGenerator }; return(retVal); }