HOTP, TOTP, OTP Auth URI, and Base32 implementation in C# targeting .NET standard 2.0, compliant with:
It has been verified against test vectors supplied in the RFCs. The interface includes support for multiple algorithms:
It supports variable code lengths (6 - 10) and an adjustable period, or time step, (1 second - 1 hour) for TOTP.
The reason for creating this library was to fetch multiple HOTP counters or a TOTP time range in a single call.
A parser for OTP Auth URIs (otpauth://) is also included that conforms to the documentation found at: https://github.com/google/google-authenticator/wiki/Key-Uri-Format.
This implementation also includes a Base32 encoder and decoder. It is compliant with RFC 4648, using the standard alphabet from section 6, and has been tested against the test vectors from section 10.
It's free. Enjoy!
OtpCore provides HotpAuthenticator
and TotpAuthenticator
classes which can be instantiated from a
OTP Auth URI using the GetAuthenticator()
method in the Hotp
and Totp
classes.
// Create from string
var uriString = "otpauth://hotp/NOBODY:petrsnd@gmail.com?issuer=NOBODY&secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&algorithm=SHA1&digits=6&counter=0";
var authenticator = Hotp.GetAuthenticator(uriString);
// Create from Uri object
var uri = new Uri(uriString);
authenticator = Hotp.GetAuthenticator(uri);
// Create from scratch by supplying parameters
var secret = Encoding.ASCII.GetBytes("12345678901234567890");
var account = "bob@example.corp";
var issuer = "Example";
var counter = 0;
var otpAuthUri = new OtpAuthUri(OtpType.Hotp, secret, account, issuer, counter); // issuer is optional, digits defaults to 6
authenticator = Hotp.GetAuthenticator(otpAuthUri);
// Get a code or a sequence of codes
var code = authenticator.GetCode();
var sequence = authenticator.GetSequence(3);
// Increment or set the counter
authenticator.IncrementCounter();
authenticator.SetCounter(3);
// Revert back to a string for storage with updated counter in URI
// The URI is left unchanged unless IncrementCounter() or SetCounter() are called
uriString = authenticator.ToString();
// Create from string
var uriString = "otpauth://totp/NOBODY:petrsnd@gmail.com?issuer=NOBODY&secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&algorithm=SHA256&digits=8";
var authenticator = Totp.GetAuthenticator(uriString);
// Create from Uri object
var uri = new Uri(uriString);
authenticator = Totp.GetAuthenticator(uri);
// Create from scratch by supplying parameters
var secret = Encoding.ASCII.GetBytes("12345678901234567890");
var account = "bob@example.corp";
var issuer = "Example";
// issuer is optional, digits defaults to 6, period defaults to 30
var otpAuthUri = new OtpAuthUri(OtpType.Totp, secret, account, issuer);
authenticator = Totp.GetAuthenticator(otpAuthUri);
// Get a code or a range of codes
var code = authenticator.GetCode();
var range = authenticator.GetRange(TimeSpan.FromSeconds(90));
// No counter to manage with TOTP. Yay!!!
// Revert back to a string for storage if it was created from scratch
uriString = authenticator.ToString();
OtpCore may also be used as a static utility library by only calling the static methods for HOTP and TOTP. When used this way, no object tracks counters or store secrets.
Fetch a simple code.
// Hotp
var secret = Encoding.ASCII.GetBytes("12345678901234567890");
long counter = 0;
int digits = 6;
var hotpCode = Hotp.GetHotpCode(secret, counter, OtpHmacAlgorithm.HmacSha1, digits));
// Totp
int period = 30;
digits = 8;
long unixTime = 1111111111; // 2005-03-18 01:58:31 +0:00
var totpCode = Totp.GetTotpCode(secret, unixTime, period, OtpHmacAlgorithm.HmacSha1, digits);
var timeFuture = DateTimeOffset.Parse("2033-05-18 03:33:20 -7:00"); // future DateTimeOffset
totpCode = Totp.GetTotpCode(secret, timeFuture, period, OtpHmacAlgorithm.HmacSha1, digits);
totpCode = Totp.GetTotpCode(secret, DateTimeOffset.Now, period, OtpHmacAlgorithm.HmacSha1, digits); // Now
Fetch multiple codes.
// Hotp
var secret = Encoding.ASCII.GetBytes("12345678901234567890");
long counter = 0;
int sequenceLength = 5; // next 5 codes
int digits = 6;
var hotpValues = Hotp.GetHotpSequence(secret, counter, sequenceLength, OtpHmacAlgorithm.HmacSha1, digits));
// Totp
int period = 30;
digits = 8;
var range = TimeSpan.FromSeconds(120); // two minutes worth of codes
var totpValues = Totp.GetTotpRange(secret, DateTimeOffset.Now, range, period, OtpHmacAlgorithm.HmacSha1, digits);
You may just want a simple Base32 encoder/decoder, because it isn't supplied in the .NET SDK.
var buffer = Encoding.ASCII.GetBytes("12345678901234567890");
var encoded = Utilities.Base32Encode(buffer); // GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
var decoded = Utilities.Base32Decode(encoded);