It is currently Wed Jul 24, 2019 9:55 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: TOTP / 2FA [All-OSs]
PostPosted: Tue Jul 09, 2019 3:27 pm 
Offline
Enthusiast
Enthusiast

Joined: Mon Jan 20, 2014 6:32 pm
Posts: 237
Please improve on it if you can and post your changes.

totp.pbi
Code:
XIncludeFile "hmac2.pb" ;https://www.purebasic.fr/english/viewtopic.php?f=12&t=66417
XIncludeFile "inc.base32.pbi"

ImportC "":time(*tloc = #Null):EndImport ;unix timestamp crossplattform

Procedure.l Revese(*ptr,length=8) ;pack Quad
  *mem=AllocateMemory(length)
  CopyMemory(*ptr+3,*mem+4,1)
  CopyMemory(*ptr+2,*mem+5,1)
  CopyMemory(*ptr+1,*mem+6,1)
  CopyMemory(*ptr+0,*mem+7,1) 
  ProcedureReturn *mem
EndProcedure

Procedure.s GenerateOTP(secrect$,digits=6,interval=30)
  Protected OTP=0, modNumber=0, offset=0, FullOtp=0
  Protected Dim HashByteArray.a (0) 
  Protected secretbase32Hex$=base32Encode(secrect$,"hex") ;create base32 Hex String
  Protected SecretLen=Len(secretbase32Hex$)/2             ;size byte array
  Protected Dim Secret.a (0)
  ReDim Secret(SecretLen)
  Hex2Dec(Secret(), secretbase32Hex$) ;create byte array from string
                                      ;ShowMemoryViewer(@Secret(),SecretLen)
  Protected timestamp=(time()/interval)
  Protected *timestamp=Revese(@timestamp)
  Protected signature$ = hmac_sha1binMod(@Secret(),SecretLen, *timestamp,8)
  ;ShowMemoryViewer(*timestamp,8)
  FreeMemory(*timestamp)
  ;Debug "signature$: " + signature$
  Protected len=Len(signature$)/2
  ReDim  HashByteArray(len)
  Hex2Dec(HashByteArray(), signature$)
 
  offset= HashByteArray(ArraySize(HashByteArray())-1) & $F
  FullOtp=(HashByteArray(offset) & $7f) * Pow(2, 24)
  FullOtp = FullOtp + (HashByteArray(offset + 1) & $ff) * Pow(2, 16)
  FullOtp = FullOtp + (HashByteArray(offset + 2) & $ff) * Pow(2, 8)
  FullOtp = FullOtp + (HashByteArray(offset + 3)  & $ff)
  modNumber = Pow(10, digits)
  OTP= FullOtp % modNumber
  ProcedureReturn RSet(Str(OTP), digits,"0")
EndProcedure

CompilerIf #PB_Compiler_IsMainFile
  Debug GenerateOTP("12345678901234567890")
CompilerEndIf


hmac2.pb
Code:
;EnableExplicit
;    HMAC function implementation
;   2016         (c) Luna Sole

UseMD5Fingerprint() : UseSHA1Fingerprint() : UseSHA2Fingerprint() ; currently only verified with these algorithms


; convert hex string into raw bytes
; Out()      unsigned char array to receive result
; Hex$      string with hex data
; RETURN:   decimal value is placed to Out() array
Procedure Hex2Dec (Array Out.a (1), Hex$)
  Protected i2, max = Len(Hex$)
  ReDim Out((max + 1) / 2)
  For i2 = 1 To max Step 2
    Out(i2 / 2) = Val("$" + Mid(Hex$, i2, 2))
  Next i2
EndProcedure

; generates HMAC signature for specified message and key
; NOTE:         This function forces strings conversion to ASCII, both for key and message
;             I'm not sure how right to do that, but let it be for now (I don't want to do a painful debug of unicode version also ^^)
; PB_Cipher      what hashing to use (MD5, SHA1, SHA256 and some others)
; Message$      data to hash
; Key$         a very secret key
; RETURN:      string, representing HMAC hash
Procedure$ StringHMAC (PB_Cipher, Message$, Key$)
 
  #HMAC_BLOCKSIZE = 64 ; blocksize is 64 (bytes) when using one of the following hash functions: SHA-1, MD5, RIPEMD-128/160.
 
  ; First of all, convert key from string to binary
  ; If key is longer than block size, replace it with hash(key)
  Protected Dim key_bdata.a (#HMAC_BLOCKSIZE)
  If (StringByteLength(Key$, #PB_Ascii) > #HMAC_BLOCKSIZE)
    PokeS(@key_bdata(0), StringFingerprint(Key$, PB_Cipher), -1, #PB_Ascii | #PB_String_NoZero)
  Else
    PokeS(@key_bdata(0), Key$, -1, #PB_Ascii | #PB_String_NoZero)
  EndIf
 
  ; Now introduce o_key_pad/i_key_pad and XOR them with some magic numbers
  Protected Dim i_key_pad.a (0)
  Protected Dim o_key_pad.a (0)
  Protected Tmp
  CopyArray(key_bdata(), i_key_pad())
  CopyArray(key_bdata(), o_key_pad())
  For Tmp = 0 To #HMAC_BLOCKSIZE
    i_key_pad(Tmp) ! $36
    o_key_pad(Tmp) ! $5c
  Next Tmp
 
  ; At last, start hashing
  Protected Hash_i$, Hash_o$         ; there are two steps, those variables storing result for step 1 and 2
  Protected hHash                    ; handle to initiated hash routine
  Protected Dim TempRaw.a (0)        ; a temporary buffer for data transfer
 
  ; First, hash using i_key_pad() and data
  ReDim TempRaw(StringByteLength(Message$, #PB_Ascii))
  PokeS(@TempRaw(0), Message$, -1, #PB_Ascii | #PB_String_NoZero)
  hHash = StartFingerprint (#PB_Any, PB_Cipher)
  AddFingerprintBuffer (hHash, @i_key_pad(0), #HMAC_BLOCKSIZE)
  If ArraySize(TempRaw())
    AddFingerprintBuffer (hHash, @TempRaw(0), ArraySize(TempRaw()))
  EndIf
  Hash_i$ = FinishFingerprint(hHash)
  ; Finally, hash once more using o_key_pad() + result of previous hashing
  Hex2Dec(TempRaw(), Hash_i$)
  hHash = StartFingerprint (#PB_Any, PB_Cipher)
  AddFingerprintBuffer (hHash, @o_key_pad(0), #HMAC_BLOCKSIZE)
  AddFingerprintBuffer (hHash, @TempRaw(0), ArraySize(TempRaw()))
  Hash_o$ = FinishFingerprint(hHash)
 
  ProcedureReturn Hash_o$
EndProcedure

;Mod to handle binary data
Procedure.s hmac_sha1binMod(*SecretByteArray, SecretLen,*msg,msglen, blocksize.i=64, opad.a=$5C, ipad.a=$36)
  Protected.i KeyLength, x
  Protected Result$, i_key$
  Protected *key, *o_key_pad, *i_key_pad, *i, *o
 
  KeyLength = SecretLen
  ;Debug "KeyLength: " + KeyLength
  If KeyLength > blocksize
    *key = AllocateMemory(KeyLength)
  ElseIf KeyLength < blocksize
    *key = AllocateMemory(blocksize)
  EndIf
 
  If *key
    CopyMemory(*SecretByteArray,*key,KeyLength)
    ;ShowMemoryViewer(*key,blocksize)
    *o_key_pad = AllocateMemory(blocksize, #PB_Memory_NoClear)
    If *o_key_pad
      For x = 0 To blocksize - 1
        PokeA(*o_key_pad + x, PeekA(*key + x) ! opad)
      Next x
     
      *i_key_pad = AllocateMemory(blocksize, #PB_Memory_NoClear)
      If *i_key_pad
        For x = 0 To blocksize - 1
          PokeA(*i_key_pad + x, PeekA(*key + x) ! ipad)
        Next x
       
        *i = AllocateMemory(blocksize + msglen)
        If *i
          CopyMemory(*i_key_pad, *i, blocksize)
          ;this work with bytes
          CopyMemory(*msg,*i + blocksize, msglen)
          ;ShowMemoryViewer(*msg,msglen)
          i_key$ = Fingerprint(*i, MemorySize(*i),#PB_Cipher_SHA1)
          FreeMemory(*i)
         
          *o = AllocateMemory(blocksize + 20)
          If *o
            CopyMemory(*o_key_pad, *o, blocksize)
            For x = 0 To 19
              PokeA(*o + blocksize + x, Val("$" + Mid(i_key$, x * 2 + 1, 2)))
            Next x
           
            Result$ = Fingerprint(*o, MemorySize(*o),#PB_Cipher_SHA1)
            FreeMemory(*o)
          EndIf
         
        EndIf
        FreeMemory(*i_key_pad)
      EndIf
      FreeMemory(*o_key_pad)
    EndIf
    FreeMemory(*key)
  EndIf
  ProcedureReturn Result$
EndProcedure

Procedure.s hmac_sha1(key$, msg$, blocksize.i=64, opad.a=$5C, ipad.a=$36)
 
  Protected.i KeyLength, x
  Protected Result$, i_key$
  Protected *key, *o_key_pad, *i_key_pad, *i, *o
 
 
  KeyLength = StringByteLength(key$, #PB_UTF8)
  If KeyLength > blocksize
    *key = AllocateMemory(KeyLength)
    If *key
      PokeS(*key, key$, -1, #PB_UTF8|#PB_String_NoZero)
      key$ = Fingerprint(*key, MemorySize(*key),#PB_Cipher_SHA1)
      FreeMemory(*key)
      KeyLength = StringByteLength(key$, #PB_UTF8)
      *key = AllocateMemory(KeyLength)
    EndIf
  ElseIf KeyLength < blocksize
    *key = AllocateMemory(blocksize)
  EndIf
 
  If *key
    PokeS(*key, key$, -1, #PB_UTF8|#PB_String_NoZero)
   
    *o_key_pad = AllocateMemory(blocksize, #PB_Memory_NoClear)
    If *o_key_pad
      For x = 0 To blocksize - 1
        PokeA(*o_key_pad + x, PeekA(*key + x) ! opad)
      Next x
     
      *i_key_pad = AllocateMemory(blocksize, #PB_Memory_NoClear)
      If *i_key_pad
        For x = 0 To blocksize - 1
          PokeA(*i_key_pad + x, PeekA(*key + x) ! ipad)
        Next x
       
        *i = AllocateMemory(blocksize + StringByteLength(msg$, #PB_UTF8))
        If *i
          CopyMemory(*i_key_pad, *i, blocksize)
          PokeS(*i + blocksize, msg$, -1, #PB_UTF8|#PB_String_NoZero)
          i_key$ = Fingerprint(*i, MemorySize(*i),#PB_Cipher_SHA1)
          FreeMemory(*i)
         
          *o = AllocateMemory(blocksize + 20)
          If *o
            CopyMemory(*o_key_pad, *o, blocksize)
            For x = 0 To 19
              PokeA(*o + blocksize + x, Val("$" + Mid(i_key$, x * 2 + 1, 2)))
            Next x
           
            Result$ = Fingerprint(*o, MemorySize(*o),#PB_Cipher_SHA1)
            FreeMemory(*o)
          EndIf
         
        EndIf
        FreeMemory(*i_key_pad)
      EndIf
      FreeMemory(*o_key_pad)
    EndIf
    FreeMemory(*key)
  EndIf
 
  ProcedureReturn Result$
 
EndProcedure




Procedure.s hmac_md5(key$, msg$, blocksize.i=64, opad.a=$5C, ipad.a=$36)
 
  Protected.i KeyLength, x
  Protected Result$, i_key$
  Protected *key, *o_key_pad, *i_key_pad, *i, *o
 
 
  KeyLength = StringByteLength(key$, #PB_UTF8)
  If KeyLength > blocksize
    *key = AllocateMemory(KeyLength)
    If *key
      PokeS(*key, key$, -1, #PB_UTF8|#PB_String_NoZero)
      key$ = Fingerprint(*key, MemorySize(*key),#PB_Cipher_MD5)
      FreeMemory(*key)
      KeyLength = StringByteLength(key$, #PB_UTF8)
      *key = AllocateMemory(KeyLength)
    EndIf
  ElseIf KeyLength < blocksize
    *key = AllocateMemory(blocksize)
  EndIf
 
  If *key
    PokeS(*key, key$, -1, #PB_UTF8|#PB_String_NoZero)
   
    *o_key_pad = AllocateMemory(blocksize, #PB_Memory_NoClear)
    If *o_key_pad
      For x = 0 To blocksize - 1
        PokeA(*o_key_pad + x, PeekA(*key + x) ! opad)
      Next x
     
      *i_key_pad = AllocateMemory(blocksize, #PB_Memory_NoClear)
      If *i_key_pad
        For x = 0 To blocksize - 1
          PokeA(*i_key_pad + x, PeekA(*key + x) ! ipad)
        Next x
       
        *i = AllocateMemory(blocksize + StringByteLength(msg$, #PB_UTF8))
        If *i
          CopyMemory(*i_key_pad, *i, blocksize)
          PokeS(*i + blocksize, msg$, -1, #PB_UTF8|#PB_String_NoZero)
          i_key$ = Fingerprint(*i, MemorySize(*i),#PB_Cipher_MD5)
          FreeMemory(*i)
         
          *o = AllocateMemory(blocksize + 16)
          If *o
            CopyMemory(*o_key_pad, *o, blocksize)
            For x = 0 To 15
              PokeA(*o + blocksize + x, Val("$" + Mid(i_key$, x * 2 + 1, 2)))
            Next x
           
            Result$ = Fingerprint(*o, MemorySize(*o),#PB_Cipher_MD5)
            FreeMemory(*o)
          EndIf
         
        EndIf
        FreeMemory(*i_key_pad)
      EndIf
      FreeMemory(*o_key_pad)
    EndIf
    FreeMemory(*key)
  EndIf
 
  ProcedureReturn Result$
 
EndProcedure


inc.base32.pbi
Code:
Procedure.s base32_decode(in1.s)
  keyStr$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
  For i = 1 To Len(in1)
   vvll=FindString(keyStr$,Mid(in1,i,1))-1
    If vvll >= 0 And vvll < 32
      buffer << 5;
      buffer | vvll;
      bitsLeft + 5;
      If bitsLeft >= 8
        dStr$ + Chr((buffer >> (bitsLeft - 8)) & 255); + $FF;
        bitsLeft - 8                                   
      EndIf           
    EndIf     
  Next   
  ProcedureReturn dStr$
EndProcedure

Procedure.s base32Encode(base32$,outputFormat$="bin") ;encode base32 string; outputFormat$ bin/hex
 
  Protected key$="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", output$=""
  Protected i=0, buffer=0, bitsLeft=0
 
  While i < Len(base32$)
    val=FindString(key$,Mid(base32$,i+1,1))-1
    If  val>=0 And val<32
      buffer= (buffer << 5 ) | val
      bitsLeft=bitsLeft+5
      If  bitsLeft>=8
        If outputFormat$="hex"
          output$=output$ + RSet(Hex((buffer >> (bitsLeft - 8)) & $FF),2,"0")
        Else
          output$=output$ + Chr((buffer >> (bitsLeft - 8)) & $FF)
        EndIf
        bitsLeft=bitsLeft-8
      EndIf
    EndIf
    i=i+1
  Wend
 
  If bitsLeft>0
    buffer=buffer<<5
    If outputFormat$="hex"
      output$=output$ + RSet(Hex((buffer >> (bitsLeft - 3)) & $FF),2,"0")
    Else
      output$=output$ + Chr((buffer >> (bitsLeft - 3)) & $FF)
    EndIf
  EndIf
 
  ProcedureReturn output$
EndProcedure


I think this should work on all OSs if not let me know only tested on Linux.

_________________
WARNING: I dont know what I am doing! I just put stuff here and there and sometimes like magic it works. So please improve on my code and post your changes so I can learn more. TIA


Top
 Profile  
Reply with quote  
 Post subject: Re: TOTP / 2FA [All-OSs]
PostPosted: Wed Jul 10, 2019 8:21 am 
Offline
Addict
Addict
User avatar

Joined: Sun Nov 05, 2006 11:42 pm
Posts: 4472
Location: Lyon - France
Hello vwidmer
Works in W7 X86 / v5.70 X86
Return a number of 6 characters, obviously never the same at each run
Thanks for sharing 8)

_________________
ImageThe happiness is a road...
Not a destination


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye