Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
TOTP 100% qb64 Yup - sorta LOL
#1
OK I cant do it any more - months of testing and I can no longer see the forest for the trees as they say.

I've been working on adding full TOTP generation natively in QB64 — no DLLs, no Python, just raw QB64. This is for a personal PWM it ahve been working on for years, I just keep adding to it LOL, and now I want an internal 2FA.  I’ve made serious progress, but I’m hitting a brick wall with final output not matching the expected 6-digit codes (e.g., not matching 287082 with known test vectors like GEZDGNBVGY3TQOJQ... at counter 0).
https://totp.danhersam.com/

Here’s what I’ve already implemented:

- Full Base32 decoding
- SHA1 hashing (based on FIPS PUB 180-1, all QB64)
- HMAC-SHA1 (manual, with XOR'd opad/ipad)
- 8-byte big-endian counter
- All code modularized in a totp.bi file
- Verified Base32 decoded output = 20 bytes (correct)
- Confirmed HMAC length = 20 bytes (SHA1 digest)
- Offset and truncation logic implemented using RFC 4226 spec
- Constants like 30 sec interval, 6-digit truncation, etc.

I’ve tested this against Google Authenticator keys and various online generators — still getting invalid results. We even tried printing hashes in hex and stepping through the logic. Everything looks structurally sound.
I used this page for help.
https://datatracker.ietf.org/doc/html/rfc4226#section-7

I’m guessing the fault is here:

    Either QB64’s string handling is off for binary bytes

    Or something’s fishy in bit shifts / unsigned math in QB64

I’ve included the .BI file if anyone wants to tinker. I’m all ears if someone knows a way QB64 internally misbehaves with binary string math or if any fixes exist I missed. Would love to see this baked into native QB64 someday — or even just confirmed if it’s impossible without external help.
currently went back to 


Code: (Select All)
SUB GENERATE_TOTP (encryptedTOTPSecret AS STRING)
    DIM decryptedSecret AS STRING
    DIM totp_code AS STRING
    DIM cmd AS STRING
    DIM F AS INTEGER

    ' Check for missing or clearly invalid secret
    IF LEN(_TRIM$(encryptedTOTPSecret)) < 10 THEN
        LWRITELN "`4Missing or invalid TOTP secret. Cannot generate code."
        PAUSE 24
        EXIT SUB
    END IF

    ' Decrypt the stored secret
    decryptedSecret = EncryptStr$(_TRIM$(encryptedTOTPSecret), vaultKey$, "d")

    ' Build the command to call the TOTP generator
    cmd = "cmd /c py\generate_totp.exe " + decryptedSecret
    SHELL _HIDE cmd

    ' Check if output file was created
    IF NOT _FILEEXISTS("totp_code.txt") THEN
        LWRITELN "`4TOTP generation failed. Output file not found."
        PAUSE 24
        EXIT SUB
    END IF

    ' Read the generated TOTP code
    F = FREEFILE
    OPEN "totp_code.txt" FOR INPUT AS #F
    LINE INPUT #F, totp_code
    totp_code = DigitsOnly$(_TRIM$(totp_code))

    CLOSE #F

    ' Sanity check output
    IF LEN(totp_code) <> 6 OR DigitsOnly$(totp_code) <> totp_code THEN
        LWRITELN "`4TOTP generation failed. Invalid output: " + totp_code
    ELSE
        _CLIPBOARD$ = totp_code
        PRINT "TOTP code generated and copied to clipboard: " + totp_code
    END IF

    ' Clean up
    KILL "totp_code.txt"
    PAUSE 24
END SUB

With python its super easy (which I converted to .exe for portability):

Code: (Select All)
import pyotp
import sys

def generate_totp(secret_key):
    totp = pyotp.TOTP(secret_key)
    return totp.now()

if __name__ == "__main__":
    secret_key = sys.argv[1]
    totp_code = generate_totp(secret_key)
    with open("totp_code.txt", "w") as f:
        f.write(totp_code)

Thanks in advance. You guys are who I come to when the engine starts smoking.

here is all the **cough** maigc , just can't seem to pull a rabbit LOL!!

Also line: 102 I have code=0 for testing. Comment out below to see it live.  You will also need the following const if you turn this into an app to test
Code: (Select All)

' -- Constants    used for totp.bi
CONST TOTP_INTERval = 30
CONST TOTP_DIGITS = 6
CONST TOTP_TIME_OFFSET = 0 ' Adjust if system time is skewed



FILE: TOTP.BI
Code: (Select All)

' genTOTP.bi - Internal TOTP generation module for QB64


' -- Helper: Base32 decoding
FUNCTION Base32Decode$ (inputStr AS STRING)
    DIM alphabet AS STRING: alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
    DIM buffer AS STRING, outputStr AS STRING
    DIM i AS INTEGER, bits AS INTEGER, value AS LONG, ch AS STRING

    FOR i = 1 TO LEN(inputStr)
        ch = MID$(UCASE$(inputStr), i, 1)
        value = INSTR(alphabet, ch) - 1
        IF value >= 0 THEN
            buffer = buffer + RIGHT$("00000" + BIN$(value AND 31), 5)

        END IF
    NEXT

    FOR i = 1 TO LEN(buffer) STEP 8
        IF i + 7 <= LEN(buffer) THEN
            outputStr = outputStr + CHR$(VAL("&B" + MID$(buffer, i, 8)))

        END IF
    NEXT
    Base32Decode$ = outputStr
END FUNCTION

FUNCTION BIN$ (value AS _UNSIGNED LONG)
    DIM result AS STRING
    DO
        result = LTRIM$(STR$(value MOD 2)) + result
        value = value \ 2
    LOOP UNTIL value = 0
    BIN$ = result
END FUNCTION



' -- Helper: Convert number to 8-byte big-endian
FUNCTION IntToBytes$ (counter AS _UNSIGNED _INTEGER64)
    DIM i AS INTEGER
    DIM b AS STRING
    DIM byteVal AS _UNSIGNED _BYTE

    FOR i = 7 TO 0 STEP -1
        byteVal = (counter \ (2 ^ (i * 8))) AND &HFF
        b = b + CHR$(byteVal)
    NEXT

    IntToBytes$ = b
END FUNCTION

FUNCTION GetUnixTime~&&
    DIM y AS INTEGER, m AS INTEGER, d AS INTEGER
    y = VAL(LEFT$(DATE$, 4))
    m = VAL(MID$(DATE$, 6, 2))
    d = VAL(RIGHT$(DATE$, 2))

    GetUnixTime~&& = DaysSinceEpoch(y, m, d) * 86400 + TIMER
END FUNCTION

FUNCTION DaysSinceEpoch~& (y, m, d)
    DIM days AS LONG, i AS INTEGER
    DIM mdays(12) AS INTEGER
    mdays(1) = 31: mdays(2) = 28: mdays(3) = 31: mdays(4) = 30
    mdays(5) = 31: mdays(6) = 30: mdays(7) = 31: mdays(8) = 31
    mdays(9) = 30: mdays(10) = 31: mdays(11) = 30: mdays(12) = 31

    days = 0
    FOR i = 1970 TO y - 1
        IF (i MOD 4 = 0 AND i MOD 100 <> 0) OR (i MOD 400 = 0) THEN
            days = days + 366
        ELSE
            days = days + 365
        END IF
    NEXT

    IF (y MOD 4 = 0 AND y MOD 100 <> 0) OR (y MOD 400 = 0) THEN
        mdays(2) = 29
    END IF

    FOR i = 1 TO m - 1
        days = days + mdays(i)
    NEXT

    days = days + (d - 1)
    DaysSinceEpoch~& = days
END FUNCTION


' -- TOTP generation
FUNCTION GenerateTOTP$ (base32Secret$)
    DIM keyStr AS STRING, counter AS _UNSIGNED _INTEGER64
    DIM hash AS STRING, offset AS INTEGER, code AS LONG
    DIM binCounter AS STRING
    DIM unixTime AS _UNSIGNED _INTEGER64
    unixTime = GetUnixTime~&&
    counter = (unixTime + TOTP_TIME_OFFSET) \ TOTP_INTERval


    keyStr = Base32Decode$(base32Secret$)
    counter = 0
    'counter = (TIMER + TOTP_TIME_OFFSET) \ TOTP_INTERval
    binCounter$ = IntToBytes$(counter)
    hash$ = HMAC_SHA1$(keyStr$, binCounter$)




    IF LEN(hash$) = 0 THEN
        GenerateTOTP$ = "ERROR"
        EXIT FUNCTION
    END IF

    offset = ASC(RIGHT$(hash$, 1)) AND &HF
    DIM segment AS STRING
    segment = MID$(hash$, offset + 1, 4)
    code = BytesToLong~&(segment) AND &H7FFFFFFF
    code = code MOD (10 ^ TOTP_DIGITS)
    GenerateTOTP$ = RIGHT$("000000" + LTRIM$(STR$(code)), TOTP_DIGITS)
END FUNCTION

' === SHA1 Function ===
' Returns binary digest of the message
FUNCTION SHA1$ (message$)
    ' Based on FIPS PUB 180-1. Credit to open-source BASIC implementations.
    DIM A AS _UNSIGNED LONG, B AS _UNSIGNED LONG, C AS _UNSIGNED LONG
    DIM D AS _UNSIGNED LONG, E AS _UNSIGNED LONG, F AS _UNSIGNED LONG
    DIM b1 AS _UNSIGNED LONG, b2 AS _UNSIGNED LONG, b3 AS _UNSIGNED LONG, b4 AS _UNSIGNED LONG
    DIM K AS _UNSIGNED LONG, TEMP AS _UNSIGNED LONG
    DIM H0 AS _UNSIGNED LONG: H0 = &H67452301
    DIM H1 AS _UNSIGNED LONG: H1 = &HEFCDAB89
    DIM H2 AS _UNSIGNED LONG: H2 = &H98BADCFE
    DIM H3 AS _UNSIGNED LONG: H3 = &H10325476
    DIM H4 AS _UNSIGNED LONG: H4 = &HC3D2E1F0
    DIM padded AS STRING, chunk AS STRING
    DIM W(80) AS _UNSIGNED LONG
    DIM i AS INTEGER, j AS INTEGER

    ' Padding
    padded$ = message$ + CHR$(&H80)
    DO WHILE (LEN(padded$) MOD 64) <> 56
        padded$ = padded$ + CHR$(0)
    LOOP

    ' Append original length in bits
    DIM bitLen AS _UNSIGNED _INTEGER64: bitLen = LEN(message$) * 8
    FOR i = 7 TO 0 STEP -1
        padded$ = padded$ + CHR$((bitLen \ (2 ^ (i * 8))) AND &HFF)
    NEXT

    ' Process each 512-bit chunk
    FOR i = 1 TO LEN(padded$) STEP 64
        chunk = MID$(padded$, i, 64)

        ' Break chunk into 16 big-endian words
        FOR j = 0 TO 15
            b1 = ASC(MID$(chunk, j * 4 + 1, 1))
            b2 = ASC(MID$(chunk, j * 4 + 2, 1))
            b3 = ASC(MID$(chunk, j * 4 + 3, 1))
            b4 = ASC(MID$(chunk, j * 4 + 4, 1))

            W(j) = _SHL(b1, 24) OR _SHL(b2, 16) OR _SHL(b3, 8) OR b4

        NEXT

        ' Extend words
        FOR j = 16 TO 79
            W(j) = _SHL(W(j - 3) XOR W(j - 8) XOR W(j - 14) XOR W(j - 16), 1) OR _
                  _SHR(W(j - 3) XOR W(j - 8) XOR W(j - 14) XOR W(j - 16), 31)
        NEXT

        A = H0: B = H1: C = H2: D = H3: E = H4

        FOR j = 0 TO 79
            SELECT CASE j
                CASE j <= 19: F = (B AND C) OR ((NOT B) AND D): K = &H5A827999
                CASE j <= 39: F = B XOR C XOR D: K = &H6ED9EBA1
                CASE j <= 59: F = (B AND C) OR (B AND D) OR (C AND D): K = &H8F1BBCDC
                CASE ELSE: F = B XOR C XOR D: K = &HCA62C1D6
            END SELECT
            TEMP = (_SHL(A, 5) OR _SHR(A, 27)) + F + E + K + W(j)
            E = D: D = C
            C = (_SHL(B, 30) OR _SHR(B, 2))
            B = A: A = TEMP
        NEXT

        H0 = (H0 + A) AND &HFFFFFFFF
        H1 = (H1 + B) AND &HFFFFFFFF
        H2 = (H2 + C) AND &HFFFFFFFF
        H3 = (H3 + D) AND &HFFFFFFFF
        H4 = (H4 + E) AND &HFFFFFFFF
    NEXT

    ' Output 20-byte binary hash
    ' Output 20-byte binary hash
    SHA1$ = ""
    FOR i = 0 TO 4
        SELECT CASE i
            CASE 0: TEMP = H0
            CASE 1: TEMP = H1
            CASE 2: TEMP = H2
            CASE 3: TEMP = H3
            CASE 4: TEMP = H4
        END SELECT
    SHA1$ = SHA1$ + CHR$((TEMP \ 2 ^ 24) AND &HFF) + _
                    CHR$((TEMP \ 2 ^ 16) AND &HFF) + _
                    CHR$((TEMP \ 2 ^ 8) AND &HFF) + _
                    CHR$(TEMP AND &HFF)
    NEXT
END FUNCTION

FUNCTION HMAC_SHA1$ (inputKey$, inputMsg$)
    CONST BLOCK_SIZE = 64
    DIM i AS INTEGER
    DIM oPad$, iPad$, keyStr$

    ' Ensure key is 20-byte binary SHA1 if too long
    keyStr$ = inputKey$
    IF LEN(keyStr$) > BLOCK_SIZE THEN keyStr$ = SHA1$(keyStr$)

    keyStr$ = keyStr$ + STRING$(BLOCK_SIZE - LEN(keyStr$), CHR$(0))

    FOR i = 1 TO BLOCK_SIZE
        oPad$ = oPad$ + CHR$(ASC(MID$(keyStr$, i, 1)) XOR &H5C)
        iPad$ = iPad$ + CHR$(ASC(MID$(keyStr$, i, 1)) XOR &H36)
    NEXT

    ' SHA1$ must return 20-byte binary digest
    HMAC_SHA1$ = SHA1$(oPad$ + SHA1$(iPad$ + inputMsg$))
END FUNCTION

FUNCTION BytesToLong~& (s AS STRING)
    BytesToLong~& = (_SHL(ASC(MID$(s, 1, 1)), 24) OR _
                    _SHL(ASC(MID$(s, 2, 1)), 16) OR _
                    _SHL(ASC(MID$(s, 3, 1)), 8) OR _
                    ASC(MID$(s, 4, 1)))
END FUNCTION

I think we can do this - and don't tell me this has already been done, will hurt my feeling ahaha. I spent a long time on this - and it is not working
3 out of 2 people have trouble with fractions

Reply
#2
This is actually one of the best thing out there to be a project.
I have worked on the SHA256 algorithm yesterday: Github: SHA2
and I made some functions for managing rightRotate, LongToHex, BytesToLong, LongToBits, and a couple more.
Here they are:

__ to bits:
Code: (Select All)
Function ByteToBits$ (__BYTE As _Unsigned _Byte)
    Dim __I As _Unsigned _Byte
    Dim __O$8
    For __I = 0 To 7
        If _ReadBit(__BYTE, __I) Then Asc(__O$8, 1 + __I) = 49 Else Asc(__O$8, 1 + __I) = 48
    Next __I
    ByteToBits$ = __O$8
End Function
Function IntegerToBits$ (__A As _Unsigned Integer)
    Dim __I As _Unsigned _Byte
    Dim __O$16
    For __I = 0 To 15
        If _ReadBit(__A, 15 - __I) Then Asc(__O$16, 1 + __I) = 49 Else Asc(__O$16, 1 + __I) = 48
    Next __I
    IntegerToBits$ = __O$16
End Function
Function LongToBits$ (__A As _Unsigned Long)
    Dim __I As _Unsigned _Byte
    Dim __O$32
    For __I = 0 To 31
        If _ReadBit(__A, 31 - __I) Then Asc(__O$32, 1 + __I) = 49 Else Asc(__O$32, 1 + __I) = 48
    Next __I
    LongToBits$ = __O$32
End Function
Function Integer64ToBits$ (__A As _Unsigned _Integer64)
    Dim __I As _Unsigned _Byte
    Dim __O$64
    For __I = 0 To 63
        If _ReadBit(__A, 63 - __I) Then Asc(__O$64, 1 + __I) = 49 Else Asc(__O$64, 1 + __I) = 48
    Next __I
    Integer64ToBits$ = __O$64
End Function

__ to hex:
Code: (Select All)
Function ByteToHex$ (__A~%%)
    __H$ = Hex$(__A~%%)
    ByteToHex$ = String$(2 - Len(__H$), 48) + __H$
End Function
Function IntegerToHex$ (__A~%)
    __H$ = Hex$(__A~%)
    IntegerToHex$ = String$(4 - Len(__H$), 48) + __H$
End Function
Function LongToHex$ (__A~&)
    __H$ = Hex$(__A~&)
    LongToHex$ = String$(8 - Len(__H$), 48) + __H$
End Function
Function Integer64ToHex$ (__A~&&)
    __H$ = Hex$(__A~&&)
    Integer64ToHex$ = String$(16 - Len(__H$), 48) + __H$
End Function

rightRotate:
Code: (Select All)
Function rightRotateLong~& (A~&, B~%%)
    rightRotateLong~& = _SHR(A~&, B~%%) Or _SHL(A~&, 32 - B~%%)
End Function
Function rightRotateInteger64~& (A~&&, B~%%)
    rightRotateInteger64~& = _SHR(A~&&, B~%%) Or _SHL(A~&&, 32 - B~%%)
End Function

reverseCVL: (an alternative to your BytesToLong~&)
Code: (Select All)
Function ReverseCVL~& (__A$4)
    ReverseCVL~& = Asc(__A$4, 4) Or _SHL(Asc(__A$4, 3), 8) Or _SHL(Asc(__A$4, 2), 16) Or _SHL(Asc(__A$4, 1), 24)
End Function
The problem with using Bits is the Big-Endian and Little-Endian, I've had to debug them myself for minutes...

reverseCVI64: (for _integer64)
Code: (Select All)
Function ReverseCVI64~&& (__A$8)
    ReverseCVI64~&& = Asc(__A$8, 8) Or _SHL(Asc(__A$8, 7), 8) Or _SHL(Asc(__A$8, 6), 16) Or _SHL(Asc(__A$8, 5), 24) Or _SHL(Asc(__A$8, 4), 32) Or _SHL(Asc(__A$8, 3), 40) Or _SHL(Asc(__A$8, 2), 48) Or _SHL(Asc(__A$8, 1), 56)
End Function

I might also suggest you a couple of things, to optimize your algorithms:
1. Use ASC(S$, I) instead of ASC(MID$(I$, I, 1)), it is faster
2. Declare variables static, which you think will not affect the output of your algorithm... if not assigned to zero.
3. While calculating SHA1$, you can do this to make it fast: (provided you are using H(0 to 4) instead of H0, H1, H2, H3, H4)
Code: (Select All)

SHA1$ = String$(20, 0)

For I = 0 To 4
    Mid$(SHA1$, I * 4 + 1, 4) = MKL$(H(I))
Next I

You can refer to my sha256 algorithm, for optimizations.

I don't think anyone made this thing before, you are the first one.
Reply
#3
Hey thanks man, seriously appreciate the time you took here. Your breakdown’s sharp and definitely shows the kind of detail this stuff needs. I’m gonna dig through the functions you posted — especially the bit/hex converters and that reverse CVL/CVI64 trick — those look like they might simplify some of the chaos I was running into with endianness and raw buffer parsing in my version.
Just curious though: would this be compatible with a self-contained TOTP function if I sub in SHA1 instead of SHA256? From what I’ve seen, the TOTP standard still relies on HMAC-SHA1 by default (I know SHA256 is an option but it's not default). Did you get your SHA256 version working for time-based codes yet, or are you thinking it could be ported?
Also side note, I totally agree — doing this from scratch in QB64 is half-pain, half-fun. But I come here when I’m stuck deep because y’all make it less painful ?
3 out of 2 people have trouble with fractions

Reply
#4
if you're curious what the beta looks like, you can grab it here.

Yes, I’m a total L.O.R.D. obsessive — I’ve built versions in Pascal, QB64, and PHP — and now I wanted a PWM that actually fits me. This one’s close to flawless, though a few things still need ironing out.

2FA is working (TOTP is handled externally via a Python EXE in the py folder)
Currently compiled for Windows 10 — not tested on Win7 or Linux yet
Encryption is... okay. It's offline and for personal use, so it’s good enough for now.

Next up, need help with an AES clone (or close to it), so if anyone has code ideas for that, I’m all ears. Right now, the so-so security is tied to the user.

Sorry if this post is out of place — just excited to show what I’ve been building and where it’s headed and any complex security help - if removed I get it LOL!

https://www.mediafire.com/file/0fgxho0qz...b.zip/file
3 out of 2 people have trouble with fractions

Reply
#5
(06-02-2025, 08:23 PM)Ra7eN Wrote: Hey thanks man, seriously appreciate the time you took here. Your breakdown’s sharp and definitely shows the kind of detail this stuff needs. I’m gonna dig through the functions you posted — especially the bit/hex converters and that reverse CVL/CVI64 trick — those look like they might simplify some of the chaos I was running into with endianness and raw buffer parsing in my version.
Just curious though: would this be compatible with a self-contained TOTP function if I sub in SHA1 instead of SHA256? From what I’ve seen, the TOTP standard still relies on HMAC-SHA1 by default (I know SHA256 is an option but it's not default). Did you get your SHA256 version working for time-based codes yet, or are you thinking it could be ported?
Also side note, I totally agree — doing this from scratch in QB64 is half-pain, half-fun. But I come here when I’m stuck deep because y’all make it less painful ?
I also ran in this reverseCVL issue a year ago when I was working on the previous version of SHA256...
This SHA256 is working correctly, and it can be used for TOTP.

(06-02-2025, 08:45 PM)Ra7eN Wrote: if you're curious what the beta looks like, you can grab it here.

Yes, I’m a total L.O.R.D. obsessive — I’ve built versions in Pascal, QB64, and PHP — and now I wanted a PWM that actually fits me. This one’s close to flawless, though a few things still need ironing out.

2FA is working (TOTP is handled externally via a Python EXE in the py folder)
Currently compiled for Windows 10 — not tested on Win7 or Linux yet
Encryption is... okay. It's offline and for personal use, so it’s good enough for now.

Next up, need help with an AES clone (or close to it), so if anyone has code ideas for that, I’m all ears. Right now, the so-so security is tied to the user.

Sorry if this post is out of place — just excited to show what I’ve been building and where it’s headed and any complex security help - if removed I get it LOL!

https://www.mediafire.com/file/0fgxho0qz...b.zip/file
I have an AES clone attached here, which I use in a password manager...
It is not perfectly AES, but similar to it.


Attached Files
.zip   aes_clone.zip (Size: 1.81 KB / Downloads: 3)
Reply
#6
I'll have to check to see if i can use sha256, my understanding with google is that it prefers sha1 for compatibility across other 2FA apps.. I'll get back to you on that one.
3 out of 2 people have trouble with fractions

Reply
#7
(Yesterday, 05:40 AM)aadityap0901 Wrote: I have an AES clone attached here, which I use in a password manager...
It is not perfectly AES, but similar to it.
Here is my attempt.. it is pretty solid, gets rid of the dead wieght at the end. I noticed yours had some unprintables. maybe we can work together.. see if we can get a aes clone
Pretty basic
Code: (Select All)
secretMessage = SleeperCrypt$ ( "hello world", key, "e"["d"])


What it DOES NOT do:
  • GF(2^8) math (for mixcolumns) Way to slow for qb64
  • Bit shifts > 1 bit  I did find a way to emulate SHL/SHR after I did this, but for this simple script it does not do it
  • Byte masking   

Code: (Select All)

DIM SHARED SBox(0 TO 255) AS INTEGER

SUB InitSBox
    DIM sData AS STRING
    DIM i AS INTEGER

'had to redo all this because qb64 does not do:
' sData = sData + CHR$(&H63, &H7C, &H77, &H7B, &HF2, &H6B, &H6F, &HC5, &H30, &H1, &H67, &H2B, &HFE, &HD7, &HAB, &H76)


    sData = ""
    sData = sData + CHR$(&H63) + CHR$(&H7C) + CHR$(&H77) + CHR$(&H7B) + CHR$(&HF2) + CHR$(&H6B) + CHR$(&H6F) + CHR$(&HC5)
    sData = sData + CHR$(&H30) + CHR$(&H01) + CHR$(&H67) + CHR$(&H2B) + CHR$(&HFE) + CHR$(&HD7) + CHR$(&HAB) + CHR$(&H76)
    sData = sData + CHR$(&HCA) + CHR$(&H82) + CHR$(&HC9) + CHR$(&H7D) + CHR$(&HFA) + CHR$(&H59) + CHR$(&H47) + CHR$(&HF0)
    sData = sData + CHR$(&HAD) + CHR$(&HD4) + CHR$(&HA2) + CHR$(&HAF) + CHR$(&H9C) + CHR$(&HA4) + CHR$(&H72) + CHR$(&HC0)
    sData = sData + CHR$(&HB7) + CHR$(&HFD) + CHR$(&H93) + CHR$(&H26) + CHR$(&H36) + CHR$(&H3F) + CHR$(&HF7) + CHR$(&HCC)
    sData = sData + CHR$(&H34) + CHR$(&HA5) + CHR$(&HE5) + CHR$(&HF1) + CHR$(&H71) + CHR$(&HD8) + CHR$(&H31) + CHR$(&H15)
    sData = sData + CHR$(&H04) + CHR$(&HC7) + CHR$(&H23) + CHR$(&HC3) + CHR$(&H18) + CHR$(&H96) + CHR$(&H05) + CHR$(&H9A)
    sData = sData + CHR$(&H07) + CHR$(&H12) + CHR$(&H80) + CHR$(&HE2) + CHR$(&HEB) + CHR$(&H27) + CHR$(&HB2) + CHR$(&H75)
    sData = sData + CHR$(&H09) + CHR$(&H83) + CHR$(&H2C) + CHR$(&H1A) + CHR$(&H1B) + CHR$(&H6E) + CHR$(&H5A) + CHR$(&HA0)
    sData = sData + CHR$(&H52) + CHR$(&H3B) + CHR$(&HD6) + CHR$(&HB3) + CHR$(&H29) + CHR$(&HE3) + CHR$(&H2F) + CHR$(&H84)
    sData = sData + CHR$(&H53) + CHR$(&HD1) + CHR$(&H00) + CHR$(&HED) + CHR$(&H20) + CHR$(&HFC) + CHR$(&HB1) + CHR$(&H5B)
    sData = sData + CHR$(&H6A) + CHR$(&HCB) + CHR$(&HBE) + CHR$(&H39) + CHR$(&H4A) + CHR$(&H4C) + CHR$(&H58) + CHR$(&HCF)
    sData = sData + CHR$(&HD0) + CHR$(&HEF) + CHR$(&HAA) + CHR$(&HFB) + CHR$(&H43) + CHR$(&H4D) + CHR$(&H33) + CHR$(&H85)
    sData = sData + CHR$(&H45) + CHR$(&HF9) + CHR$(&H02) + CHR$(&H7F) + CHR$(&H50) + CHR$(&H3C) + CHR$(&H9F) + CHR$(&HA8)
    sData = sData + CHR$(&H51) + CHR$(&HA3) + CHR$(&H40) + CHR$(&H8F) + CHR$(&H92) + CHR$(&H9D) + CHR$(&H38) + CHR$(&HF5)
    sData = sData + CHR$(&HBC) + CHR$(&HB6) + CHR$(&HDA) + CHR$(&H21) + CHR$(&H10) + CHR$(&HFF) + CHR$(&HF3) + CHR$(&HD2)
    sData = sData + CHR$(&HCD) + CHR$(&H0C) + CHR$(&H13) + CHR$(&HEC) + CHR$(&H5F) + CHR$(&H97) + CHR$(&H44) + CHR$(&H17)
    sData = sData + CHR$(&HC4) + CHR$(&HA7) + CHR$(&H7E) + CHR$(&H3D) + CHR$(&H64) + CHR$(&H5D) + CHR$(&H19) + CHR$(&H73)
    sData = sData + CHR$(&H60) + CHR$(&H81) + CHR$(&H4F) + CHR$(&HDC) + CHR$(&H22) + CHR$(&H2A) + CHR$(&H90) + CHR$(&H88)
    sData = sData + CHR$(&H46) + CHR$(&HEE) + CHR$(&HB8) + CHR$(&H14) + CHR$(&HDE) + CHR$(&H5E) + CHR$(&H0B) + CHR$(&HDB)
    sData = sData + CHR$(&HE0) + CHR$(&H32) + CHR$(&H3A) + CHR$(&H0A) + CHR$(&H49) + CHR$(&H06) + CHR$(&H24) + CHR$(&H5C)
    sData = sData + CHR$(&HC2) + CHR$(&HD3) + CHR$(&HAC) + CHR$(&H62) + CHR$(&H91) + CHR$(&H95) + CHR$(&HE4) + CHR$(&H79)
    sData = sData + CHR$(&HE7) + CHR$(&HC8) + CHR$(&H37) + CHR$(&H6D) + CHR$(&H8D) + CHR$(&HD5) + CHR$(&H4E) + CHR$(&HA9)
    sData = sData + CHR$(&H6C) + CHR$(&H56) + CHR$(&HF4) + CHR$(&HEA) + CHR$(&H65) + CHR$(&H7A) + CHR$(&HAE) + CHR$(&H08)
    sData = sData + CHR$(&HBA) + CHR$(&H78) + CHR$(&H25) + CHR$(&H2E) + CHR$(&H1C) + CHR$(&HA6) + CHR$(&HB4) + CHR$(&HC6)
    sData = sData + CHR$(&HE8) + CHR$(&HDD) + CHR$(&H74) + CHR$(&H1F) + CHR$(&H4B) + CHR$(&HBD) + CHR$(&H8B) + CHR$(&H8A)
    sData = sData + CHR$(&H70) + CHR$(&H3E) + CHR$(&HB5) + CHR$(&H66) + CHR$(&H48) + CHR$(&H03) + CHR$(&HF6) + CHR$(&H0E)
    sData = sData + CHR$(&H61) + CHR$(&H35) + CHR$(&H57) + CHR$(&HB9) + CHR$(&H86) + CHR$(&HC1) + CHR$(&H1D) + CHR$(&H9E)
    sData = sData + CHR$(&HE1) + CHR$(&HF8) + CHR$(&H98) + CHR$(&H11) + CHR$(&H69) + CHR$(&HD9) + CHR$(&H8E) + CHR$(&H94)
    sData = sData + CHR$(&H9B) + CHR$(&H1E) + CHR$(&H87) + CHR$(&HE9) + CHR$(&HCE) + CHR$(&H55) + CHR$(&H28) + CHR$(&HDF)
    sData = sData + CHR$(&H8C) + CHR$(&HA1) + CHR$(&H89) + CHR$(&H0D) + CHR$(&HBF) + CHR$(&HE6) + CHR$(&H42) + CHR$(&H68)
    sData = sData + CHR$(&H41) + CHR$(&H99) + CHR$(&H2D) + CHR$(&H0F) + CHR$(&HB0) + CHR$(&H54) + CHR$(&HBB) + CHR$(&H16)

    FOR i = 0 TO 255
        SBox(i) = ASC(sData, i + 1)
    NEXT
END SUB


FUNCTION SleeperCrypt$ (plainText AS STRING, keyString AS STRING, method AS STRING)
    DIM inputText AS STRING
    inputText = plainText

    DIM result AS STRING
    DIM i AS INTEGER, j AS INTEGER, round AS INTEGER
    DIM value AS INTEGER, keyByte AS INTEGER, keyPos AS INTEGER

    DIM blockSize AS INTEGER: blockSize = 16

    ' Pad key to at least blockSize
    IF LEN(keyString) < blockSize THEN
        DO WHILE LEN(keyString) < blockSize
            keyString = keyString + keyString
        LOOP
    END IF
    keyString = LEFT$(keyString, blockSize)

    method = LCASE$(method)
    result = plainText

    FOR round = 1 TO 5 ' Do 5 rounds for added strength
        inputText = result
        result = ""

        FOR i = 1 TO LEN(inputText)
            keyPos = ((i - 1) MOD blockSize) + 1
            keyByte = ASC(keyString, keyPos)
            value = ASC(inputText, i)

            IF method = "e" THEN
                value = value XOR keyByte
                value = (value * 2) AND 255 ' Simulate ROL1
                value = SBox(value)
            ELSE
                ' Reverse S-Box
                FOR j = 0 TO 255
                    IF SBox(j) = value THEN value = j: EXIT FOR
                NEXT
                value = (value \ 2) OR ((value AND 1) * 128) ' Simulate ROR1
                value = value XOR keyByte
            END IF

            result = result + CHR$(value)
        NEXT
    NEXT

    SleeperCrypt$ = result
END FUNCTION
3 out of 2 people have trouble with fractions

Reply
#8
I have only briefly scanned this thread but I gather that in order to implement the  Time-based One-Time Password you need some form of encryption, there's a public domain C library https://www.libtom.net/LibTomCrypt/
Quote:Ciphers Supported:
  • AES (aka Rijndael)
  • Anubis (with optional tweak as proposed by the developers)
  • Blowfish
  • CAST5
  • Camellia
  • DES, two-key 3DES, 3DES
  • KASUMI
  • Khazad
  • Multi2
  • Noekeon
  • RC2
  • RC5
  • RC6
  • SAFER (K64, SK64, K128, SK128)
  • SAFER+
  • SEED
  • Skipjack
  • Twofish
  • XTEA
maybe you can use that library or borrow some code/ideas
Reply




Users browsing this thread: 1 Guest(s)