Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

OTP (One Time Password) Demystified

4.87/5 (37 votes)
26 May 2013CPOL8 min read 246.8K   8.9K  
This article shows how an OTP generator works.

Introduction

At the beginning of 2004, I was working with a small team of Gemplus on the EAP-SIM authentication protocol. As we were a bit ahead of the market, our team was reassigned to work with a team of Verisign on a new authentication method: OTP or One Time Password.

At this time, the existing one time password was a token from RSA that was using a clock to synchronize the passwords.

The lab of Versign came with a very simple but I should say very smart concept. The OTP that you may be using with your bank or Google was born.

This is this algorithm and authentication method I describe in the following two articles. In this article, I present a complete code of the OTP generator. It is very similar to the Javacard Applet I wrote in 2004 when I started to work on this concept with the Versign labs.

The One Time Password Generator

This OTP is based on the very popular algorithm HMAC SHA. The HMAC SHA is an algorithm generally used to perform authentication by challenge response. It is not an encryption algorithm but a hashing algorithm that transforms a set of bytes to another set of bytes. This algorithm is not reversible which means that you cannot use the result to go back to the source.

A HMAC SHA uses a key to transform an input array of bytes. The key is the secret that must never be accessible to a hacker and the input is the challenge. This means that OTP is a challenge response authentication.

The secret key must be 20 bytes at least; the challenge is usually a counter of 8 bytes which leaves quite some time before the value is exhausted.

The algorithm takes the 20 bytes key and the 8 bytes counter to create a 8 digits number. This means that there will obviously be duplicates during the life time of the OTP generator but this doesn't matter as no duplicate can occur consecutively and an OTP is only valid for a couple of minutes.

Why is the OTP a very strong authentication method?

There are few reasons why this is a very strong method.

  • The key is 20 digits
  • A password is a couple counter/password, only valid once and a very short time
  • The algorithm that generates each password is not reversible
  • With an OTP token, the key is hardware protected
  • If the OTP is received on your phone, the key always stays at the server

Those few characteristics make the OTP a strong authentication protocol. The weakness in an authentication is usually the human factor. It is difficult to remember many complex passwords, so users often use the same one all across the internet and not really a strong one. With an OTP, you don't have to remember a password, the most you would have to remember would be PIN code (4 to 8 digits) if the OTP token is PIN protected. In the case of an OTP sent by a mobile phone, it is protected by your phone security. A PIN is short but you can't generally try it more than 3 times before the token is locked.

The weakness of an OTP if there is one, is the media used to generate or receive the OTP. If the user loses it, then the authentication could be compromised. A possible solution would be to protect this device with a biometric credential, making it virtually totally safe.

The code of the OTP generator follows:

C#
public class OTP
{
    public const int SECRET_LENGTH = 20;
    private const string
	MSG_SECRETLENGTH = "Secret must be at least 20 bytes",
	MSG_COUNTER_MINVALUE = "Counter min value is 1";

    public OTP()
    {
    }

    private static int[] dd = new int[10] { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 }; 

    private byte[] secretKey = new byte[SECRET_LENGTH] 
    {
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
	0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43
    };

    private ulong counter = 0x0000000000000001;

    private static int checksum(int Code_Digits) 
    {
	int d1 = (Code_Digits/1000000) % 10;
	int d2 = (Code_Digits/100000) % 10;
	int d3 = (Code_Digits/10000) % 10;
	int d4 = (Code_Digits/1000) % 10;
	int d5 = (Code_Digits/100) % 10;
	int d6 = (Code_Digits/10) % 10;
	int d7 = Code_Digits % 10;
	return (10 - ((dd[d1]+d2+dd[d3]+d4+dd[d5]+d6+dd[d7]) % 10) ) % 10;
    }

    /// <summary>
    /// Formats the OTP. This is the OTP algorithm.
    /// </summary>
    /// <param name="hmac">HMAC value</param>
    /// <returns>8 digits OTP</returns>
    private static string FormatOTP(byte[] hmac)
    {
	int offset =  hmac[19] & 0xf ;
	int bin_code = (hmac[offset]   & 0x7f) << 24
		| (hmac[offset+1] & 0xff) << 16
		| (hmac[offset+2] & 0xff) <<  8
		| (hmac[offset+3] & 0xff) ;
	int Code_Digits = bin_code % 10000000;
	int csum = checksum(Code_Digits);
	int OTP = Code_Digits * 10 + csum;

	return string.Format("{0:d08}", OTP);
    }

    public byte[] CounterArray
    {
	get
	{
	    return BitConverter.GetBytes(counter);
	}

	set
	{
	    counter = BitConverter.ToUInt64(value, 0);
	}
    }

    /// <summary>
    /// Sets the OTP secret
    /// </summary>
    public byte[] Secret
    {
	set
	{
	    if (value.Length < SECRET_LENGTH)
	    {
		throw new Exception(MSG_SECRETLENGTH);
	    }

	    secretKey = value;
	}
    }

    /// <summary>
    /// Gets the current OTP value
    /// </summary>
    /// <returns>8 digits OTP</returns>
    public string GetCurrentOTP()
    {
	HmacSha1 hmacSha1 = new HmacSha1();

	hmacSha1.Init(secretKey);
	hmacSha1.Update(CounterArray);
		
	byte[] hmac_result = hmacSha1.Final();

	return FormatOTP(hmac_result);
    }

    /// <summary>
    /// Gets the next OTP value
    /// </summary>
    /// <returns>8 digits OTP</returns>
    public string GetNextOTP()	
    {
	// increment the counter
	++counter;

	return GetCurrentOTP();
    }

    /// <summary>
    /// Gets/sets the counter value
    /// </summary>
    public ulong Counter
    {
	get
	{
	    return counter;
	}

	set
	{
	    counter = value;
	}
    }
}

The methods FormatOTP() and checksum() are the heart of the OTP algorithm. Those methods transform the result of the hmacsha into an 8 digits OTP.

The attached code also contains an implementation of the HMAC SHA algorithm. It is of course possible to use the standard hmacsha of the .NET Framework but the code I provide in fact used a demo in a prototype of smart card that was running a .NET CLR. At the time I wrote this code, the cryptography namespace was not yet implemented by the card.

This way, you can also see how a hmacsha algorithm is implemented.

The OTP Server and Authentication Protocol

  There are usually 2 ways to perform an authentication with an OTP. I'm going to describe the real case of an authentication to an online banking site. I just want to be explicit with something. You cannot use what I'm going to describe in this post to hack into a banking site! On the contrary after reading this you should understand why using an OTP as a second factor authentication is extremely secure.

The OTP by itself is already very secure for at least the 2 following reasons:

  • You can't play it twice
  • You can't go back to the source.

The second characteristic is very important in term of security. An OTP depends on 2 parameters:

  • A secret key
  • A counter

Even if a hacker intercepts millions of OTP the algorithm is not reversible which means that even if you know the key you can't go back to the counter that was used to generate the OTP. So without the key and the counter, it is virtually impossible even with millions of OTP to find a pattern to guess the key and the current counter value.

Like many security protocols, the strength of the OTP is given by the quality of the cryptography algorithm used, in this case HMACSHA1 which is a proven challenge response algorithm. An other HMAC algorithm can be used in place of HMACSHA as encryption algorithm have to become stronger when CPU power is increasing. This can be done by increasing the size of the key or by redesigning the algorithm itself.

OTP are usually used to perform authentication or to verify a transaction with a credit card. In the case of a transaction an OTP is sent to the mobile phone of the user, for an authentication if is possible to use either a secure token or to request an OTP to be send to the user phone.

Using an OTP sent to a phone

This is usually the authentication method used when a transaction is verified with an OTP. The bank system sends you an OTP and you then have few minutes to enter this OTP. This mechanism doesn't need any synchronization process as the OTP is originally generated by the server and send to a third party device. The server expects that you type the correct OTP within generally 2 mns. If you fail to do it, you just ask a new OTP and then enter it within the given time.

When a system supports both authentication methods, it means that the back-end has 2 different keys and counters; one pair for the OTP token and one pair for the OTP transmitted by SMS.

Using an OTP token

The original product I worked on when we implemented one of the first versions of the OTP in a Javacard was using an OTP token with a screen or a mobile phone with a card applet to generate the OTP. In this model both the server and the authentication token have to generate an OTP that must be synchronized.<o:p>

The process is the following: The user generates an OTP with his token, type it and press OK. The server receives the OTP generated by the token, it increments the counter and generates a new OTP. <o:p>

This is where there is a possible synchronization issue.
<o:p> 
Synchronization issues<o:p>

If the user enters the correct OTP, then the server when it increments the counter and calculate the OTP, the authentication will be successful.<o:p>

Now there could be few scenarios that could lead to a desynchronization of the server counter and the authentication mechanism won't work. In some cases it could be possible to resynchronize automatically the counter but in some cases the user would have to resynchronize the server counter using a specific procedure.<o:p>

Few scenarios of desynchronization could arise:<o:p> 

  1. The user accidentally press the generate button of his token and doesn't perform an authentication.
    In this case the counter of the token would be ahead of the server counter by few steps.<o:p>
  2. The user enters an OTP without generating it from the token. In this case the counter of the server would be ahead of the token counter.<o:p>
  3. The user generates an OTP with the token but types a wrong OTP.<o:p> 

If the OTP given by the user doesn't match the one of the server, the server can try to auto-resynchronize itself by trying few counters around the expected counter. In our server we would use 10 values around the nominal counter value. If the synchronization cannot be done, the server would retain the current counter value in order not desynchronize the server further.<o:p>

However the server would have to implement a strategy to inform that the server and token are totally desynchronized and a manual synchronization must be performed.

Manual synchronization process<o:p>

The server can propose a manual synchronization process to the user. The OTP numbers are only 8 digits generated by the hash of 8 bytes counter and formatting a 20 bytes result. This means that it is possible to get twice the same OTP for 2 different counter values. So attempting synchronization with only one OTP value is not reliable. A manual resynchronization process needs the user to enter 2 consecutive OTP, and then the server can try to find the requested sequence as the probability to get the same sequence of 2 OTPs for different counter values is extremely low if not zero.

Getting the Source

You can get the source code of the project from the ZIP files attached to the article or you can follow it on github where it will be updated regularly as this is a public repository.

Points of Interest

OTP is a popular and quite simple authentication method; I hope those articles will help you understand how it works behind the scenes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)