Generating TOTP Codes

  • Be sure to checkout “Tips & Tricks”
    Dear Guest Visitor → Once you register and log-in please checkout the “Tips & Tricks” page for some very handy tips!

    /Steve.
  • BootAble – FreeDOS boot testing freeware

    To obtain direct, low-level access to a system's mass storage drives, SpinRite runs under a GRC-customized version of FreeDOS which has been modified to add compatibility with all file systems. In order to run SpinRite it must first be possible to boot FreeDOS.

    GRC's “BootAble” freeware allows anyone to easily create BIOS-bootable media in order to workout and confirm the details of getting a machine to boot FreeDOS through a BIOS. Once the means of doing that has been determined, the media created by SpinRite can be booted and run in the same way.

    The participants here, who have taken the time to share their knowledge and experience, their successes and some frustrations with booting their computers into FreeDOS, have created a valuable knowledgebase which will benefit everyone who follows.

    You may click on the image to the right to obtain your own copy of BootAble. Then use the knowledge and experience documented here to boot your computer(s) into FreeDOS. And please do not hesitate to ask questions – nowhere else can better answers be found.

    (You may permanently close this reminder with the 'X' in the upper right.)

excel

Member
Oct 7, 2020
6
0
FYI:

I have been looking for a simple program to generate TOTP codes that is not tied to other functions or subject to 3rd parties.

I ran across a github recently for a simple program to generate TOTP codes:

There is also one in golang, which I found easier to compile:

I checked a TOTP key and the codes matched the ones generated by keepassxc.

I have not decided where to store the keys - perhaps in a password vault or other separate area. keepassxc works but I find the interface unintuitive.
 
When Steve was answering the question about TOTP repetition, I used ChatGPT to throw together a quick stats tracker. This is the majority of the ChatGPT generated TOTP calculation code, as tweaked by me to actually work right. The basics of TOTP are just an assemblage of crypto primitives and URL encoding.

Java:
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base32;

// Most of this code was from ChatGPT
final class TOTPGenerator
{
  private static final String HMAC_ALGORITHM = "HmacSHA1";
  private static final int DIGITS = 6;
  private static final long TIME_STEP = 30L;  // TOTP time step in seconds
  private final static Base32 base32Codec = new Base32();  // Apache Commons utility

  private  final String totpSecret;

  public TOTPGenerator(final String totpSecret)
  {
    this.totpSecret = totpSecret;
  }

  public String generate(final long totp_time)
  {
    try
    {
      final String totp = generateTOTP(totpSecret, totp_time);
      return totp;
    }
    catch (final InvalidKeyException | NoSuchAlgorithmException e)
    {
      throw new IllegalStateException(e);
    }
  }

  public static String generateTOTP(final String secret, final long inputTimeCounter)
      throws NoSuchAlgorithmException, InvalidKeyException
  {
    long timeCounter = inputTimeCounter;
    final byte[] secretKey = base32Codec.decode(secret);
    final byte[] timeCounterBytes = new byte[8];
    for (int i = 7; i >= 0; i--)
    {
      timeCounterBytes[i] = (byte) (timeCounter & 0xFF);
      timeCounter >>>= 8;
    }

    final Mac mac = Mac.getInstance(HMAC_ALGORITHM);
    final SecretKeySpec keySpec = new SecretKeySpec(secretKey, HMAC_ALGORITHM);
    mac.init(keySpec);
    final byte[] hash = mac.doFinal(timeCounterBytes);

    final int offset = hash[hash.length - 1] & 0x0F;
    int code = ((hash[offset] & 0x7F) << 24)    | ((hash[offset + 1] & 0xFF) << 16)
                   | ((hash[offset + 2] & 0xFF) << 8) | (hash[offset + 3] & 0xFF);

    code %= (int) Math.pow(10, DIGITS);
    return String.format("%0" + DIGITS + "d", code);
  }
}