WHAT'S NEW?
Loading...

OTP generation on MVC - C#

As my first post in this new blog I want to make something a little bit "different". For this reason, this post is about "one time password" (OTP) generation mixed with a little bit of Model View Controller (MVC). The idea for this post is to make an overview (with a little bit of code, of course!) over an authenticator that Google uses.
Most of you already would know about OTP, but if not, your are in the right place. A one-time password (OTP) is a password that is valid for only one login session or transaction. OTPs avoid a number of shortcomings that are associated with traditional (static) passwords. The most important shortcoming that is addressed by OTPs is that, in contrast to static passwords, they are not vulnerable to replay attacks. This means that a potential intruder who manages to record an OTP that was already used to log into a service or to conduct a transaction will not be able to abuse it, since it will be no longer valid. On the downside, OTPs are difficult for human beings to memorize. Therefore they require additional technology to work.
My application is basically a login form which uses username and an OTP provided by the server to login users. The OTP last 30 seconds, after this time, it will expire and the user will need to generate a new one. Now I am going to explain the different parts of my code to make easier your comprehension. First I start with a screenshot of the different program parts and the arquitecture:
  1. Controllers: Encryption.cs is the engine of the app and who generates the OTP password from the user id and a random number. LoginController.cs is the second most important part and is dedicated on handle the user inputs and the management of the information between the Login view and the model.
  2. Models: just User.cs class is needed to handle the user information (login, otp, otp creation date, logged)
  3. Views: UserGetOtp.aspx is the first view where the user writes his login name; UserLogin.aspx is shown to finish the login process with the OTP provided. Finally, after a succesful log process, the Account.aspx view, from the Private Area is shown.

Here bellow you can see the core of the application, the OTP generation method. This algorithm is explained in the point 5.4 of the RFC4226 (see references at the bottom):


public static string GeneratePasswordByUserAndIteration(string userId, long iterationNumber, int digits = 6)
{
    //Here the system converts the iteration number to a byte[]
    byte[] iterationNumberByte = BitConverter.GetBytes(iterationNumber);
    //To BigEndian (MSB LSB)
    if (BitConverter.IsLittleEndian) Array.Reverse(iterationNumberByte);

    //Hash the userId by HMAC-SHA-1 (Hashed Message Authentication Code)
    byte[] userIdByte = Encoding.ASCII.GetBytes(userId);
    HMACSHA1 userIdHMAC = new HMACSHA1(userIdByte, true);
    byte[] hash = userIdHMAC.ComputeHash(iterationNumberByte); //Hashing a message with a secret key

    //RFC4226 http://tools.ietf.org/html/rfc4226#section-5.4
    int offset = hash[hash.Length - 1] & 0xf; //0xf = 15d
    int binary =
    ( (hash[offset] & 0x7f) << 24)      //0x7f = 127d
    | ((hash[offset + 1] & 0xff) << 16) //0xff = 255d
    | ((hash[offset + 2] & 0xff) << 8)
    | (hash[offset + 3] & 0xff);

    int password = binary % (int)Math.Pow(10, digits); // Shrink: 6 digits
    return password.ToString(new string('0', digits));
}
The iteration number is created in the "GetPassword" method call, using the Unix datetime, which is the first of January of 1970. Simply way to create a random variable:
public static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public static string GetPassword(string userId)
{
            // We get the "Unix Time" by sub from 1/1/1970 00:00:00 and now
    long iteration = (long)(DateTime.UtcNow - UNIX_EPOCH).TotalSeconds / 30;
    return GeneratePasswordByUserAndIteration(userId, iteration);
}
Finally I will show you the LogInController.cs most important methods. GetOtp (string user) and Access (string user, int otp). The first one, checks the user credentials (the login in our case) and generates a new User.cs model with this information and the OTP generated. The second one just checks the login and the otp against the model previously generated and then it checks if the password has not expired.
public ActionResult GetOTP(string user)
{
    if (!System.String.IsNullOrEmpty(user))
    {
        int otpass;
        bool conversion = Int32.TryParse(Encryption.GetPassword(user), out otpass);
        if (conversion)
        {
            //Save user credentials.
            userModel = new User();
            userModel.Login = user;
            userModel.Otp = otpass;
            userModel.OtpCrtDate = DateTime.Now;
            ViewData["user"] = userModel.Login;
            ViewData["status"] = "OTP: " + otpass + " remains active just 30 seconds from now.";
            return View("UserLogin", userModel);
        }
        else
        {
            ViewData["status"] = "Sorry, an error was found while creating your password. Try again, please.";
            return View("UserGetOtp");
        }
    }
    else return LoginFailed();
}

public ActionResult Access(string user, int otp)
{
    if (otp == userModel.Otp && user == userModel.Login)
    {
        TimeSpan timeSub = DateTime.Now - userModel.OtpCrtDate;
        if (timeSub.TotalSeconds < 30.0 || userModel.Logged)
        {
            //LogIn successful
            userModel.Logged = true;
            return View("../PrivateArea/Account", userModel);
        }
        else
        {
            ViewData["status"] = "Sorry but your OTP is very old. Get a new one.";
            return View("UserLogin", userModel);
        }

    }
    return LoginFailed();
}
 I enclose here bellow some references that I used to develop this app. Thanks for reading!
---
References:
  1. codeproject.com: Implementing Two Factor Authentication in ASP.NET MVC with Google Authenticator
  2. RFC4226: HOTP An HMAC-Based One-Time Password Algorithm
  3. RFC6238: TOTP Time-Based One-Time Password Algorithm

0 comments:

Post a Comment