Two-Factor Authentication with Time-based One-Time Passwords (TOTP)

Time-based One-Time Passwords (TOTP) is a widely used method for enhancing the security of user authentication. It is a two-factor authentication method that requires both something you know (your password) and something you have (a device that generates the TOTP) to access an account. TOTP is an extension of the well-known HOTP (HMAC-based One-Time Passwords) algorithm, which uses the current time as the moving factor.

TOTP algorithm relies on a shared secret key between the user’s device (e.g. mobile phone) and the server. The secret key is used to generate a unique one-time password (OTP) that changes based on the current time. The OTP is valid for a certain period of time, usually around 30-60 seconds, and becomes invalid after that.

The basic process of TOTP algorithm is as follows:

  1. The user’s device generates a timestamp (usually the number of seconds since the epoch).
  2. The user’s device generates an HMAC (Hash-based Message Authentication Code) of the timestamp using the shared secret key.
  3. The user’s device extracts a 4-byte binary string from the HMAC’s digest.
  4. The user’s device converts the 4-byte binary string to an integer.
  5. The user’s device uses the last 6 digits of the integer as the OTP.

SHA1 Algorithm

import hmac
import hashlib
import base64
import struct
import time

def generate_totp(secret, interval=30):
    # Get the current time in seconds since the epoch
    timestamp = int(time.time() / interval)

    # Encode the timestamp as a big-endian 8-byte binary string
    timestamp_bytes = struct.pack(">q", timestamp)

    # Generate the HMAC-SHA1 of the timestamp using the secret
    hmac_sha1 = hmac.new(secret, timestamp_bytes, hashlib.sha1)

    # Extract the 4-byte binary string from the HMAC's digest
    hmac_binary = hmac_sha1.digest()[-4:]

    # Convert the 4-byte binary string to an integer
    code = struct.unpack(">L", hmac_binary)[0]

    # Use the last 6 digits of the code as the TOTP
    return str(code % 10 ** 6).zfill(6)

# example usage
secret = b'your_secret_key'
print(generate_totp(secret))

This example uses the hmac and hashlib libraries to generate the HMAC-SHA1 of the timestamp using the secret. It then extracts the last 4 bytes of the digest and converts them to an integer. The last 6 digits of the integer are used as the TOTP. The interval parameter determines the time period for which the TOTP is valid.

The most important thing that should be considered when implementing TOTP is the security of the shared secret key. The shared secret key should be stored in a secure way, such as storing it in a secure element or a HSM. Also, it’s highly recommended to use a stronger algorithm like SHA256 or SHA512 rather than SHA1 which is less secure.

SHA256 or SHA512

import hmac
import hashlib
import base64
import struct
import time

def generate_totp(secret, algorithm='sha256', interval=30):
    # Get the current time in seconds since the epoch
    timestamp = int(time.time() / interval)

    # Encode the timestamp as a big-endian 8-byte binary string
    timestamp_bytes = struct.pack(">q", timestamp)

    # Choose the algorithm
    if algorithm == 'sha256':
        hmac_algo = hashlib.sha256
    elif algorithm == 'sha512':
        hmac_algo = hashlib.sha512
    else:
        raise ValueError("Invalid algorithm")

    # Generate the HMAC of the timestamp using the secret and chosen algorithm
    hmac_algo = hmac.new(secret, timestamp_bytes, hmac_algo)

    # Extract the 4-byte binary string from the HMAC's digest
    hmac_binary = hmac_algo.digest()[-4:]

    # Convert the 4-byte binary string to an integer
    code = struct.unpack(">L", hmac_binary)[0]

    # Use the last 6 digits of the code as the TOTP
    return str(code % 10 ** 6).zfill(6)

# example usage
secret = b'your_secret_key'
print(generate_totp(secret, algorithm='sha256'))
print(generate_totp(secret, algorithm='sha512'))

The algorithm parameter determines the algorithm to be used (sha256 or sha512).

Another important aspect to consider when implementing TOTP is to use a library that provides a tested and secure implementation of the TOTP algorithm, such as pyotp or python-otp.

When using TOTP, it’s also important to keep in mind that the implementation should be compliant with the security regulations and standards.

TOTP algorithm in a Django REST framework application

Here’s an example of how you could use the TOTP algorithm in a Django REST framework application to generate one-time passwords (OTPs) for users that change every 60 seconds:

  1. Create a new Django app called otp and add it to the INSTALLED_APPS list in the project’s settings.py.

  2. Create a model for storing the user’s secret key and the last time an OTP was generated.

from django.db import models
from django.contrib.auth.models import User

class UserOTP(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    secret = models.CharField(max_length=100)
    last_generated = models.DateTimeField(auto_now=True)
  1. Create a serializer for the UserOTP model.
from rest_framework import serializers
from .models import UserOTP

class UserOTPSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserOTP
        fields = ('user', 'secret', 'last_generated')
  1. Create a ViewSet for handling the OTP generation
from rest_framework import viewsets
from .models import UserOTP
from .serializers import UserOTPSerializer
import hmac
import hashlib
import base64
import struct
import time

class UserOTPViewSet(viewsets.ModelViewSet):
    queryset = UserOTP.objects.all()
    serializer_class = UserOTPSerializer

    def generate_totp(self,secret,interval = 60):
        timestamp = int(time.time() / interval)
        timestamp_bytes = struct.pack(">q", timestamp)
        hmac_sha256 = hmac.new(secret, timestamp_bytes, hashlib.sha256)
        hmac_binary = hmac_sha256.digest()[-4:]
        code = struct.unpack(">L", hmac_binary)[0]
        return str(code % 10 ** 6).zfill(6)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        secret = serializer.validated_data['secret']
        otp = self.generate_totp(secret.encode())
        serializer.save(otp=otp)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

This is just an example on how you could use the TOTP algorithm in a Django REST framework application to generate one-time passwords that change every 60 seconds.

You also should consider adding a way to send the OTP to the user, i.e. via email, SMS, etc.

Please keep in mind that this is just an example and it’s not recommended to use this implementation in a real-world scenario without proper testing and review, also you should make sure that your code is complying with the security standards and best practices.

You should also consider adding additional security measures such as rate limiting on the OTP generation endpoint and logging all the OTP generation requests.

You can also consider adding a way to invalidate/revoke OTPs if needed, for example, after a successful login or if the user requests a new OTP.
You also could consider using a library such as pyotp or python-otp to handle the OTP generation and validation, as they provide a tested and secure implementation of the TOTP algorithm.

In general, implementing two-factor authentication is a complex task and require a thorough understanding of security best practices, and should be done by experts in the field.

In summary, TOTP is a strong method for enhancing the security of user authentication and it’s widely used in many systems. It is a simple, yet effective method for providing two-factor authentication by requiring both something you know (your password) and something you have (a device that generates the TOTP). However, it’s important to keep in mind that the security of the shared secret key and the compliance with the security regulations and standards should be considered when implementing TOTP.

Explore More Python Posts

Python's Match Statement: Examples & Tips

Learn Python's match statement with examples. Simplify branching logic and enhance code readability. A must-know for Python developers!

Read More
Using Twitter API with Python: Getting Tweets & Insights

Learn how to use the Twitter API with Python to get tweet information and insights. Extract valuable data for businesses and researchers with ease.

Read More
Accessing Facebook Data with Python: Examples for Post Likes, Views, and Profile Details

Learn how to access Facebook data using Python and the Facebook API. Get post likes, views, and comments, and retrieve profile details.

Read More
Python Design Patterns: Examples and Best Practices

Learn about Python design patterns with examples and discover best practices for writing maintainable and scalable code.

Read More
How to Use the YouTube API with Python: A Step-by-Step Guide

Learn how to access and retrieve information from YouTube using Python and the YouTube API. Get code examples and step-by-step instructions for impor…

Read More
Top 3 Python Frameworks for Web Development

Discover the most popular and efficient Python frameworks for building dynamic web applications. Get started today!

Read More