5using System.Threading.Tasks;
43 [CollectionName(
"ExternalCredentials")]
46 [Index(
"Type",
"Endpoint")]
47 [Index(
"Endpoint",
"Type")]
53 private byte[] secret;
54 private string account;
55 private string issuer;
57 private string description;
59 private long? counter;
60 private int? nrDigits =
null;
61 private int? timeStepSeconds =
null;
93 if (!
string.IsNullOrEmpty(this.label))
109 return this.description;
114 if (!
string.IsNullOrEmpty(this.description))
117 this.description = value;
135 if (!
string.IsNullOrEmpty(this.image))
156 if (!(this.secret is
null))
177 if (!
string.IsNullOrEmpty(this.issuer))
198 if (!
string.IsNullOrEmpty(this.account))
201 this.account = value;
214 return this.hashFunction;
219 if (this.hashFunction.HasValue)
222 this.hashFunction = value;
240 if (this.nrDigits.HasValue)
243 this.nrDigits = value;
261 if (this.counter.HasValue)
264 this.counter = value;
282 if (this.timeStepSeconds.HasValue)
285 this.timeStepSeconds = value;
314 if (!(approvedSources is
null))
315 throw new NotSupportedException(
"Changing approved sources not permitted.");
317 approvedSources = ApprovedSources;
320 private static void AssertAllowed()
322 if (!(approvedSources is
null))
350 if (!Uri.TryCreate(OtpAuthUri, UriKind.Absolute, out Uri ParsedUri))
367 if (!OtpAuthUri.Scheme.Equals(
"otpauth", StringComparison.OrdinalIgnoreCase))
371 string Label = OtpAuthUri.AbsolutePath;
383 switch (OtpAuthUri.Authority.ToLower())
397 if (
Label.StartsWith(
'/'))
400 i =
Label.IndexOf(
':');
407 Key = OtpAuthUri.Query;
408 if (Key.StartsWith(
'?'))
411 foreach (
string Part
in Key.Split(
'&'))
413 i = Part.IndexOf(
'=');
416 Key = WebUtility.UrlDecode(Part);
417 Value =
string.Empty;
421 Key = WebUtility.UrlDecode(Part[..i]);
422 Value = WebUtility.UrlDecode(Part[(i + 1)..]);
425 switch (Key.ToLower())
444 switch (Value.ToUpper())
470 Log.
Warning(
"Unsupported OTP Auth URI hash algorithm: " + Value,
471 new KeyValuePair<string, object>(
"Algorithm", Value));
477 if (!
int.
TryParse(Value, out i) || i < 6 || i > 8)
487 if (!
long.
TryParse(Value, out
long l) || l < 0)
497 if (!
int.
TryParse(Value, out i) || i <= 0)
508 Log.
Warning(
"Unsupported OTP Auth URI parameter: " + Key,
509 new KeyValuePair<string, object>(
"Key", Key),
510 new KeyValuePair<string, object>(
"Value", Value));
560 throw new InvalidOperationException(
"Counter not set for HOTP credential.");
565 return Code.ToString(
"D" + this.nrDigits.ToString());
572 return Code.ToString(
"D" + this.nrDigits.ToString());
575 return Encoding.UTF8.GetString(this.
Secret);
578 throw new InvalidOperationException(
"Unknown credential algorithm type.");
593 if (!this.timeStepSeconds.HasValue || !
this.timeStepSeconds.HasValue)
596 DateTime Now = DateTime.UtcNow;
600 return Next.Subtract(Now);
617 return Math.Ceiling(
Next.Value.TotalSeconds).ToString() +
" s";
627 public static Task<ExternalCredential>
CreateAsync(
string OtpAuthUri)
630 ??
throw new ArgumentException(
"Invalid OTP Auth URI.", nameof(OtpAuthUri));
688 if (
string.IsNullOrEmpty(
Label))
690 else if (
string.IsNullOrEmpty(
Issuer) &&
string.IsNullOrEmpty(
Account))
692 i =
Label.IndexOf(
':');
702 if (
string.IsNullOrEmpty(EndPoint))
707 string Suffix =
string.Empty;
713 while (!(Result is
null))
716 Suffix =
" (" + i.ToString() +
")";
743 Result.Endpoint = EndPoint + Suffix;
749 Result.label =
Label;
752 Result.image =
Image;
780 public static async Task<ZipFile>
ExportAsync(
string CredentialFileName,
783 StringBuilder sb =
new StringBuilder();
785 Settings.Encoding = Encoding.UTF8;
787 using XmlWriter w = XmlWriter.Create(sb, Settings);
789 w.WriteStartDocument();
790 w.WriteStartElement(
"Credentials",
"http://waher.se/schema/Credentials.xsd");
794 w.WriteStartElement(
"Credential");
795 w.WriteAttributeString(
"type", Credential.
Type.ToString());
796 w.WriteAttributeString(
"endpoint", Credential.
Endpoint);
797 w.WriteAttributeString(
"label", Credential.
Label);
798 w.WriteAttributeString(
"description", Credential.
Description);
799 w.WriteAttributeString(
"issuer", Credential.
Issuer);
800 w.WriteAttributeString(
"account", Credential.
Account);
802 if (Credential.hashFunction.HasValue)
803 w.WriteAttributeString(
"hashFunction", Credential.hashFunction.Value.ToString());
805 if (Credential.nrDigits.HasValue)
806 w.WriteAttributeString(
"nrDigits", Credential.nrDigits.Value.ToString());
808 if (Credential.counter.HasValue)
809 w.WriteAttributeString(
"counter", Credential.counter.Value.ToString());
811 if (Credential.timeStepSeconds.HasValue)
812 w.WriteAttributeString(
"timeStepSeconds", Credential.timeStepSeconds.Value.ToString());
814 if (!
string.IsNullOrEmpty(Credential.
Image))
815 w.WriteAttributeString(
"image", Credential.
Image);
817 w.WriteAttributeString(
"secret", Convert.ToBase64String(Credential.
Secret));
822 w.WriteEndDocument();
825 if (
string.IsNullOrEmpty(CredentialFileName))
826 CredentialFileName =
"Credentials.xml";
828 string Xml = sb.ToString();
829 byte[] Bin = Encoding.UTF8.GetBytes(Xml);
Static class that does BASE32 encoding and decoding as defined in RFC4648: https://datatracker....
static byte[] Decode(string Base32)
Converts a Base32-encoded string to its binary representation.
Helps with common XML-related tasks.
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Static class for creating ZIP files.
static Task CreateZipFile(string SourceFileName, string OutputFileName)
Creates a ZIP file containing a single file.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
This filter selects objects that conform to all child-filters provided.
This filter selects objects that have a named field equal to a given value.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Static class of application-wide semaphores that can be used to order access to editable objects.
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Static class containing methods that can be used to make sure calls are made from appropriate locatio...
static void CallFromSource(params string[] Sources)
Makes sure the call is made from one of the listed sources.
Contains OTP secret information for an OTP endpoint.
TimeSpan? Next
When current pass code changes.
string Current
Gets the next password for the endpoint.
string[] EncryptedProperties
Array of properties that are encrypted.
string Description
Optional description of the endpoint.
string Label
Optional name of the endpoint.
static ExternalCredential TryParse(string OtpAuthUri)
Tries to parse an OTP Auth URI (otpauth://).
int TimeStepSeconds
Time step in seconds.
static Task< ExternalCredential > CreateAsync(ExternalCredential Parsed, CredentialAlgorithm Type, string EndPoint, HashFunction? HashFunction, byte[] Secret, string Issuer, string Account, string Label, string Description, long? Counter, int? NrDigits, int? TimeStepSeconds)
Creates a credential.
byte[] Secret
Endpoint secret.
static async Task< ZipFile > ExportAsync(string CredentialFileName, string Password)
Exports credentials to a password-protected ZIP file.
string NextLabel
Label for when current pass code changes.
static void SetAllowedSources(ICallStackCheck[] ApprovedSources)
If access to sensitive methods is only accessible from a set of approved sources.
string Account
Endpoint secret.
static Task< ExternalCredential > CreateAsync(string OtpAuthUri)
Creates a credential.
static ExternalCredential TryParse(Uri OtpAuthUri)
Tries to parse an OTP Auth URI (otpauth://).
string Issuer
Issuer of credential.
static Task< ZipFile > ExportAsync(string Password)
Exports credentials to a password-protected ZIP file.
HashFunction? HashFunction
Hash function used for the endpoint.
int NrDigits
Number of digits.
string Image
Optional image URI of the endpoint.
static async Task< ExternalCredential > CreateAsync(ExternalCredential Parsed, CredentialAlgorithm Type, string EndPoint, HashFunction? HashFunction, byte[] Secret, string Issuer, string Account, string Label, string Description, long? Counter, int? NrDigits, int? TimeStepSeconds, string Image)
Creates a credential.
static async Task< IEnumerable< ExternalCredential > > GetCredentials()
Gets stored credentials.
CredentialAlgorithm Type
Credential Algorithm type.
Implements the HOTP calculator algorithm, as defined in RFC 4226: https://datatracker....
int Compute(long Counter)
Calculates the expected one-time-password for the given counter value.
const int DefaultNrDigits
Default number of digits (6).
const HashFunction DefaultHashFunction
Default Hash Function (SHA-1)
Implements the TOTP calculator algorithm, as defined in RFC 6238: https://datatracker....
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
const long DefaultT0
Default time when starting counting steps (0).
int Compute()
Calculates the expected one-time-password for the given counter value.
static long CalcCounter(DateTime Timestamp, int TimeStepSeconds, long T0)
Calculates the counter number for use with the HOTP algorithm.
const int DefaultTimeStepSeconds
Default time-step, in seconds (30).
Interface for objects containing encrypted properties. Mark the properties that are encrypted with th...
Interface for call stack checks.
ZipEncryption
Enumeration containing ZIP Encryption methods.
TypeNameSerialization
How the type name should be serialized.
CredentialAlgorithm
Specifies the type of external credential algorithm to use.
HashFunction
Hash method enumeration.