Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
SHA1 and TOTP
#14
It's not a pure QB64 solution but I managed to hack this together for you from this C TOTP implementation. The only thing really 'missing' is a base32 converter, but that should be very easy to write in QB64 (and it looked like you already wrote that).

This is an example of using it from QB64 (I tested it with this website). To use it in your program you'll copy the `Declare Library` section into your code and then you can call the defined functions, I provided options to either have the C code get the time or for QB64 to provide the time (as Unix time):

Code: (Select All)

Declare Library "totp"
    Function getTOTPAtTime~&(hmacKey As String, keyLength As _Unsigned Long, timeStep As _Unsigned Long, currentTime As _Unsigned _Integer64)
    Function getTOTP~&(hmacKey As String, BYVAL keyLength As _Unsigned Long, BYVAL timeStep As _Unsigned Long)
End Declare

' The unencoded key
key$ = "Hello!" + CHR$(&HDE) + CHR$(&HAD) + CHR$(&HBE) + CHR$(&HEF)

Do
    Locate 2, 2
    ' Use $Right to add the leading zeros
    Print "Code: "; Right$("000000" + _Trim$(Str$(getTOTP(key$, LEN(key$), 30))), 6);
    _Limit 2
Loop Until Inkey$ <> ""

And copy this C code into a file named `totp.h` and place it next to your `.bas` file. This C code is very messy but it works fine for me:

Code: (Select All)
// MIT License

// Copyright (c) 2019 Weravech

#include <string.h>
#include <inttypes.h>
#include "time.h"

void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep);
uint32_t getCodeFromTimestamp(uint32_t timeStamp);

// Callable functions from QB64
uint32_t getTOTPAtTime(char *hmacKey, uint32_t keyLength, uint32_t timeStep, time_t timeStamp)
{
    TOTP((uint8_t *)hmacKey, keyLength, timeStep);
    return getCodeFromTimestamp(timeStamp);
}

uint32_t getTOTP(char *hmacKey, uint32_t keyLength, uint32_t timeStep)
{
    return getTOTPAtTime(hmacKey, keyLength, timeStep, time(NULL));
}

#define HASH_LENGTH 20
#define BLOCK_LENGTH 64

union _buffer {
  uint8_t b[BLOCK_LENGTH];
  uint32_t w[BLOCK_LENGTH/4];
} buffer;
union _state {
  uint8_t b[HASH_LENGTH];
  uint32_t w[HASH_LENGTH/4];
} state;

uint8_t bufferOffset;
uint32_t byteCount;
uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];

void init(void);
void initHmac(const uint8_t* secret, uint8_t secretLength);
uint8_t* result(void);
uint8_t* resultHmac(void);
void write(uint8_t);
void writeArray(uint8_t *buffer, uint8_t size);
uint32_t getCodeFromSteps(uint32_t steps);

// TOTP.c
uint8_t* _hmacKey;
uint8_t _keyLength;
uint8_t _timeZoneOffset;
uint32_t _timeStep;

// Init the library with the private key, its length and the timeStep duration
void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep) {
    _hmacKey = hmacKey;
    _keyLength = keyLength;
    _timeStep = timeStep;
}

// Generate a code, using the timestamp provided
uint32_t getCodeFromTimestamp(uint32_t timeStamp) {
    uint32_t steps = timeStamp / _timeStep;
    return getCodeFromSteps(steps);
}

// Generate a code, using the number of steps provided
uint32_t getCodeFromSteps(uint32_t steps) {
    // STEP 0, map the number of steps in a 8-bytes array (counter value)
    uint8_t _byteArray[8];
    _byteArray[0] = 0x00;
    _byteArray[1] = 0x00;
    _byteArray[2] = 0x00;
    _byteArray[3] = 0x00;
    _byteArray[4] = (uint8_t)((steps >> 24) & 0xFF);
    _byteArray[5] = (uint8_t)((steps >> 16) & 0xFF);
    _byteArray[6] = (uint8_t)((steps >> 8) & 0XFF);
    _byteArray[7] = (uint8_t)((steps & 0XFF));

    // STEP 1, get the HMAC-SHA1 hash from counter and key
    initHmac(_hmacKey, _keyLength);
    writeArray(_byteArray, 8);
    uint8_t* _hash = resultHmac();

    // STEP 2, apply dynamic truncation to obtain a 4-bytes string
    uint32_t _truncatedHash = 0;
    uint8_t _offset = _hash[20 - 1] & 0xF;
    uint8_t j;
    for (j = 0; j < 4; ++j) {
        _truncatedHash <<= 8;
        _truncatedHash  |= _hash[_offset + j];
    }

    // STEP 3, compute the OTP value
    _truncatedHash &= 0x7FFFFFFF;    //Disabled
    _truncatedHash %= 1000000;

    return _truncatedHash;
}
// sha1.c


#define SHA1_K0 0x5a827999
#define SHA1_K20 0x6ed9eba1
#define SHA1_K40 0x8f1bbcdc
#define SHA1_K60 0xca62c1d6

uint8_t sha1InitState[] = {
  0x01,0x23,0x45,0x67, // H0
  0x89,0xab,0xcd,0xef, // H1
  0xfe,0xdc,0xba,0x98, // H2
  0x76,0x54,0x32,0x10, // H3
  0xf0,0xe1,0xd2,0xc3  // H4
};

void init(void) {
  memcpy(state.b,sha1InitState,HASH_LENGTH);
  byteCount = 0;
  bufferOffset = 0;
}

uint32_t rol32(uint32_t number, uint8_t bits) {
  return ((number << bits) | (uint32_t)(number >> (32-bits)));
}

void hashBlock() {
  uint8_t i;
  uint32_t a,b,c,d,e,t;

  a=state.w[0];
  b=state.w[1];
  c=state.w[2];
  d=state.w[3];
  e=state.w[4];
  for (i=0; i<80; i++) {
    if (i>=16) {
      t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];
      buffer.w[i&15] = rol32(t,1);
    }
    if (i<20) {
      t = (d ^ (b & (c ^ d))) + SHA1_K0;
    } else if (i<40) {
      t = (b ^ c ^ d) + SHA1_K20;
    } else if (i<60) {
      t = ((b & c) | (d & (b | c))) + SHA1_K40;
    } else {
      t = (b ^ c ^ d) + SHA1_K60;
    }
    t+=rol32(a,5) + e + buffer.w[i&15];
    e=d;
    d=c;
    c=rol32(b,30);
    b=a;
    a=t;
  }
  state.w[0] += a;
  state.w[1] += b;
  state.w[2] += c;
  state.w[3] += d;
  state.w[4] += e;
}

void addUncounted(uint8_t data) {
  buffer.b[bufferOffset ^ 3] = data;
  bufferOffset++;
  if (bufferOffset == BLOCK_LENGTH) {
    hashBlock();
    bufferOffset = 0;
  }
}

void write(uint8_t data) {
  ++byteCount;
  addUncounted(data);

  return;
}

void writeArray(uint8_t *buffer, uint8_t size){
    while (size--) {
        write(*buffer++);
    }
}

void pad() {
  // Implement SHA-1 padding (fips180-2 ˜5.1.1)

  // Pad with 0x80 followed by 0x00 until the end of the block
  addUncounted(0x80);
  while (bufferOffset != 56) addUncounted(0x00);

  // Append length in the last 8 bytes
  addUncounted(0); // We're only using 32 bit lengths
  addUncounted(0); // But SHA-1 supports 64 bit lengths
  addUncounted(0); // So zero pad the top bits
  addUncounted(byteCount >> 29); // Shifting to multiply by 8
  addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
  addUncounted(byteCount >> 13); // byte.
  addUncounted(byteCount >> 5);
  addUncounted(byteCount << 3);
}

uint8_t* result(void) {
  // Pad to complete the last block
  pad();

  // Swap byte order back
  uint8_t i;
  for (i=0; i<5; i++) {
    uint32_t a,b;
    a=state.w[i];
    b=a<<24;
    b|=(a<<8) & 0x00ff0000;
    b|=(a>>8) & 0x0000ff00;
    b|=a>>24;
    state.w[i]=b;
  }

  // Return pointer to hash (20 characters)
  return state.b;
}

#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c

void initHmac(const uint8_t* key, uint8_t keyLength) {
  uint8_t i;
  memset(keyBuffer,0,BLOCK_LENGTH);
  if (keyLength > BLOCK_LENGTH) {
    // Hash long keys
    init();
    for (;keyLength--;) write(*key++);
    memcpy(keyBuffer,result(),HASH_LENGTH);
  } else {
    // Block length keys are used as is
    memcpy(keyBuffer,key,keyLength);
  }
  // Start inner hash
  init();
  for (i=0; i<BLOCK_LENGTH; i++) {
    write(keyBuffer[i] ^ HMAC_IPAD);
  }
}

uint8_t* resultHmac(void) {
  uint8_t i;
  // Complete inner hash
  memcpy(innerHash,result(),HASH_LENGTH);
  // Calculate outer hash
  init();
  for (i=0; i<BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);
  for (i=0; i<HASH_LENGTH; i++) write(innerHash[i]);
  return result();
}
Note that the C code does not give an option to change the digit length of the code. I believe that detail is hardcoded into `getCodeFromSteps` where it does a MOD 1000000 at the end. Assuming I'm correct on that it should be easy to support longer digit codes if needed by modifying the C code to let you pass in that value.
Reply


Messages In This Thread
SHA1 and TOTP - by Ra7eN - 08-21-2024, 01:35 AM
RE: SHA1 and TOTP - by TerryRitchie - 08-21-2024, 01:48 AM
RE: SHA1 and TOTP - by Ra7eN - 08-21-2024, 02:13 AM
RE: SHA1 and TOTP - by SMcNeill - 08-21-2024, 03:47 AM
RE: SHA1 and TOTP - by RhoSigma - 08-21-2024, 10:44 AM
RE: SHA1 and TOTP - by DSMan195276 - 08-21-2024, 04:19 AM
RE: SHA1 and TOTP - by Ra7eN - 08-21-2024, 05:14 PM
RE: SHA1 and TOTP - by Ra7eN - 08-21-2024, 09:44 PM
RE: SHA1 and TOTP - by Ra7eN - 08-22-2024, 12:01 AM
RE: SHA1 and TOTP - by Ra7eN - 08-22-2024, 12:09 AM
RE: SHA1 and TOTP - by Ra7eN - 08-22-2024, 12:25 AM
RE: SHA1 and TOTP - by SpriggsySpriggs - 08-22-2024, 11:29 AM
RE: SHA1 and TOTP - by Ra7eN - 08-22-2024, 11:37 AM
RE: SHA1 and TOTP - by DSMan195276 - 08-22-2024, 12:13 PM
RE: SHA1 and TOTP - by Ra7eN - 08-22-2024, 11:41 PM
RE: SHA1 and TOTP - by DSMan195276 - 08-23-2024, 12:32 AM
RE: SHA1 and TOTP - by Ra7eN - 08-23-2024, 12:43 AM
RE: SHA1 and TOTP - by DSMan195276 - 08-23-2024, 06:30 AM
RE: SHA1 and TOTP - by Ra7eN - 08-24-2024, 12:38 AM
RE: SHA1 and TOTP - by Kernelpanic - 08-24-2024, 08:37 PM



Users browsing this thread: 9 Guest(s)