One Time Passwords in C#

TOTP - Time-Based One-Time PasswordRecently I was working on a project where time-based one-time password algorithm might come in handy. You know the one - you have token that displays 6-digit number and you enter it after your user name and password. It used to be restricted to hardware (e.g. RSA) but these days Google Authenticator is probably the best known.

While rolling something on your own is always possibility, following the standard is always better because all tough questions have been answered by people smarter than you. In this case all things needed were covered in RFC 6238 (Time-Based One-Time Password Algorithm) and RFC 4226 (An HMAC-Based One-Time Password Algorithm).

While specifications do grant you some freedom in algorithm choice and number of digits you wish to generate, looking at other services implementing the same algorithm, 6-digit SHA-1 based code seems to be unwritten rule. Also universal seems the rule to use (unpadded) Base32 encoding of a secret key. Any implementation of one-time password algorithm has obey these rules if it wants to use existing infrastructure - both server side services and end-user applications (e.g. Google Authenticator or Pebble one).

With my OneTimePassword implementation, basic code generation would looks something like this:

var otp = new OneTimePassword("jbsw y3dp ehpk 3pxp");
txtCode.Text = otp.GetCode().ToString("000000"); //to generate new code

If you are on server side, verification would look just slightly different:

var otp = new OneTimePassword("jbsw y3dp ehpk 3pxp");
var isValid = otp.IsCodeValid(code); //to verify one that user entered

If you want to generate a new secret key for end-user:

var otp = new OneTimePassword();
var secret = otp.GetBase32Secret();

Pretty much all basic scenarios are covered and then some. Sample with full code is available for download.

PS: OneTimePassword class supports many more things than ones mentioned here. You can use it in HOTP (counter) mode with TimeStep=0; you can generate your own keys; validate codes; use SHA-256 and SHA-512; other digit lengths... Play with it and see.

16 thoughts to “One Time Passwords in C#”

  1. Thanks for the code sample. Very useful and is working a treat. Unfortunately I guess I still don’t fully understand it otherwise I would know the answer to the following;

    How can I determine the date/time/length of time a code is valid for?

    ie: Upon validation of a code I would like return the following;

    “The Code 123456 is valid until 13/12/2015 11:45:15 AM”

    1. As code is generated based on TimeStep – almost universally 30 seconds.So you know it is officially valid until the end of that 30 second period. However, due to time differences, servers usually accept 2 or 3 codes around that time (they do the check multiple times). So you exact “expiry” time will be dependent on server and its configuration. That is why the most of programs just show only the current code, refreshing it every 30 seconds, and not bother with showing expiry time at all.

  2. I added a test to check how well it verifies for intervals. Could you please have a look?

    Below is the code. I have often noticed that code remains valid even after interval is expired.

    private async void Form_Load(object sender, System.EventArgs e)
    {
    otp = new OneTimePassword(txtSecret.Text);
    tmrUpdate_Tick(null, null);

    for (int timeStep = 30; timeStep timeStep) && isCodeValid;
    Debug.WriteLine($”ValidCode: {isCodeValid}, {sp.Elapsed}{(wrongResult ? $”, Not good {timeStep}” : “”)}”);
    }

    Debug.WriteLine($”TimeStep: {timeStep}, Key: {secret}, Ended: {DateTime.Now.ToString(“hh:mm:ss.fff”)}”);
    }
    }

    Random _random = new Random();
    char GetRandomLetter()
    {
    // This method returns a random lowercase letter.
    // … Between ‘a’ and ‘z’ inclusize.
    int num = _random.Next(0, 26); // Zero to 25
    char let = (char)(‘a’ + num);
    return let;
    }

    string GetRandomSecretKey()
    {
    var firstSet = new string(new char[] { GetRandomLetter(), GetRandomLetter(), GetRandomLetter(), GetRandomLetter() });
    var secondSet = new string(new char[] { GetRandomLetter(), GetRandomLetter(), GetRandomLetter(), GetRandomLetter() });
    var thridSet = new string(new char[] { GetRandomLetter(), GetRandomLetter(), GetRandomLetter(), GetRandomLetter() });
    var fourthSet = new string(new char[] { GetRandomLetter(), GetRandomLetter(), GetRandomLetter(), GetRandomLetter() });
    return string.Join(” “, firstSet, secondSet, thridSet, fourthSet);
    }

  3. There is a 10 seconds difference in timming, the program changes the auth. key 10 seconds earlier than the authenticatior app.

    1. Are your times synchronized on both your computer and mobile device? Code is dependent only on time and done based on the standard so, assuming times are the same, only difference can come from one application refreshing screen a bit earlier. Do note that regardless of this, all sites supporting two-factor authentication allow for time drift of at least one counter value (30 seconds by default). So 10 second difference is inconsequential.

  4. I have one more error and that is when invoking OneTimePassword(secret), some string combination work but some other throw an error ‘Secret is not valid Base32 string’ .

    1. Please define “some string combinations”. Example would be useful.
      Do however note that my code works only with base-32 encoded strings – not with any random string you throw at it.

  5. Hi there,
    looks very good.

    What about licencing of the code? can it be used for commercial use?

Leave a Reply to Josip Medved Cancel reply

Your email address will not be published. Required fields are marked *