[Windows] Passwords and Prompts

Share your advanced PureBasic knowledge/code with the community.
User avatar
skywalk
Addict
Addict
Posts: 4220
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

[Windows] Passwords and Prompts

Post by skywalk »

Had to make some password stuff and used this GUI to test the routines.
The timed InputRequester was tricky due to my typos and having to debug WITHOUT netmaestro's Messaging tool :evil:
If only I saw that earlier... :cry:

Code: Select all

; COMPILER OPTIONS:
;   [x] Use Compiler:   PureBasic 5.00 (x86)
;   [ ] Use Icon:       
;   [ ] Enable inline ASM support
;   [±] Create unicode executable
;   [ ] Create threadsafe executable
;   [ ] Enable OnError lines support
;   [ ] Enable XP skin support
;   [ ] Request Administrator mode for Windows Vista
;   [ ] Request User mode for Windows Vista (no virtualization)
;   Library Subsystem:
;   Executable Format:  Windows  ;|Console|Shared DLL
;   CPU:                All      ;|Dynamic|w/MMX|w/3DNOW|w/SSE|w/SSE2
;   File Format:        UTF-8
EnableExplicit
#SP$    = Chr(32)
#SPC    = 32  ; " "   ;#SP    = 32      ; " "  #SP fails???
Procedure.s SF_Encrypt_AESB64(s$, Key$, nKeyBits.i=256, *AESinit.Integer=0)
  ; REV:  121124, skywalk
  ;       modified from Blue(Guy LeBleu), 111127
  ;       Ascii or Unicode
  ; WARNING:  AESEncoding can add Nulls to result, so cannot view with PeekS()
  ;           Must convert AESresult to Base64$ for simple viewing.
  ;           Also, if a result is stored in Ascii it cannot be decrypted with Unicode.
  ;           Keep encryption data in same format!
  ; *AESinitvector accepts 16 bytes.
  Protected.i n = StringByteLength(s$) + SizeOf(Character)  ; include null terminator
  If nKeyBits >= 256
    nKeyBits = 256
  ElseIf nKeyBits >= 192
    nKeyBits = 192
  Else
    nKeyBits = 128
  EndIf
  If n < nKeyBits / 8
    n = nKeyBits / 8        ; minimum size required by AES encoding
  EndIf
  Protected.i *AES = AllocateMemory(n)    ; buffer for binary encrypted result
  If *AES
    Key$ = LSet(Key$, nKeyBits/8, #SP$) ; Force Key$ to correct size.
    If *AESinit
      AESEncoder(@s$, *AES, n, @Key$, nKeyBits, *AESinit, #PB_Cipher_CBC)
    Else
      AESEncoder(@s$, *AES, n, @Key$, nKeyBits, 0, #PB_Cipher_ECB)
    EndIf
    ; convert AES$ to Base64 Ascii string
    Protected.i n64 = n * 1.5           ; coding space for Base64 = 150%
    If n64 < 64
      n64 = 64              ; minimum required
    EndIf
    s$ = Space(n64)         ; Reusing s$ variable = B64$
    Base64Encoder(*AES, n, @s$, n64)
    FreeMemory(*AES)
  Else
    s$ = #NULL$
  EndIf
  ProcedureReturn s$
EndProcedure

Procedure.s SF_Decrypt_AESB64(AESB64$, Key$, nKeyBits.i=256, *AESinit.Integer=0)
  ; REV:  121124, skywalk
  ;       modified from Blue(Guy LeBleu), 111127
  ;       Ascii or Unicode
  ; To rebuild AES encrypted block from a B64 string,
  ; you must know exact size of original AES encrypted buffer.
  ; WARNING:  AESEncoding can add Nulls to result, so cannot view with PeekS()
  ;           Must convert AESresult to Base64$ for simple viewing.
  ;           Also, if a result is stored in Ascii it cannot be decrypted with Unicode.
  ;           Keep encryption data in same format!
  ; *AESinitvector accepts 16 bytes.
  Protected.i nAESB64 = StringByteLength(AESB64$)
  Protected.i *AES = AllocateMemory(nAESB64)
  If *AES
    If nKeyBits >= 256
      nKeyBits = 256
    ElseIf nKeyBits >= 192
      nKeyBits = 192
    Else
      nKeyBits = 128
    EndIf
    Base64Decoder(@AESB64$, nAESB64, *AES, nAESB64)
    ; Decrypt AES buffer to recover original expression
    ; Calculate nBytes in AES cipher from nBytes in Base64 string
    ;   since each block of 4 base64 bytes represents 3 encrypted AES bytes,
    ;   except for last one, which may end with padding characters.
    ;   count number of padding bytes and subtract from final count.
    ;   nAESbytes = lenB64String / 4 * 3 - n_fill_bytes
    Protected.i *B64 = @AESB64$
    Protected.i nAES = StringByteLength(AESB64$)
    *B64 + nAES                         ; move pointer to last byte of string
    nAES / 4 * 3                        ; 3 AES bytes for each 4 Base64 bytes
    If PeekB(*B64 - 2) = #SPC
      nAES - 2
    ElseIf PeekB(*B64 - 1) = #SPC
      nAES - 1
    EndIf
    AESB64$ = Space(nAES)               ; Reusing AESB64$ variable = s$
    Key$ = LSet(Key$, nKeyBits/8, #SP$) ; Force Key$ to correct size.
    If *AESinit
      AESDecoder(*AES, @AESB64$, nAES, @Key$, nKeyBits, *AESinit, #PB_Cipher_CBC)
    Else
      AESDecoder(*AES, @AESB64$, nAES, @Key$, nKeyBits, 0, #PB_Cipher_ECB)
    EndIf
    FreeMemory(*AES)
  Else
    AESB64$ = #NULL$
  EndIf
  ProcedureReturn AESB64$
EndProcedure

#gui_TimerPSWDCHAR = 5555   ; Arbitrary number
#gui_TimerPSWDTMO  = 10000  ; Timeout(ms), Change to ~15000 after debugging
Procedure gui_InputRequesterPSWD_CB(hW.i, Msg.i, idEvent.i, Time.i)
  ; REV:  121126, skywalk
  Protected.s wT$ = Space(#MAX_PATH)
  GetWindowText_(hW, @wT$, #MAX_PATH)
  Protected.i hW_IRQ = FindWindow_("InputRequester", wT$)
  If idEvent = #gui_TimerPSWDCHAR
    Protected.i hW_Edit = FindWindowEx_(hW_IRQ, 0, "Edit", #NULL$)
    ;SendMessage_(hW_Edit, #WM_SETTEXT, 0, "You did not enter a password before timing out!")
    SendMessage_(hW_Edit, #EM_SETPASSWORDCHAR, '*', 0)
    SendMessage_(hW_Edit, #EM_SETSEL, 0, -1)
    KillTimer_(hW, idEvent)
    ; Set new Timer w/Password prompt limit = #gui_TimerPSWDTMO msec
    SetTimer_(hW, #gui_TimerPSWDTMO, #gui_TimerPSWDTMO, @gui_InputRequesterPSWD_CB())
  ElseIf idEvent = #gui_TimerPSWDTMO
    Protected.i hW_BTN = FindWindowEx_(hW_IRQ, 0, "Button", "OK")    ; Must explicitly search for "OK"!
    ;                    FindWindowEx_(hW_IRQ, 0, "Button", #NULL$)  ; Using Null$ or "" Fails! :(
    ; Choose your method to proceed once timeout occurs.
    ; Sendkeys [ESCAPE]     = CANCEL
    ;keybd_event_(#VK_ESCAPE,0,0,0)
    ;keybd_event_(#VK_ESCAPE,0,#KEYEVENTF_KEYUP,0)
    ; Sendkeys [ENTER]      = Full or partial capture of Password entered so far...
    ;keybd_event_(#VK_RETURN,0,0,0)
    ;keybd_event_(#VK_RETURN,0,#KEYEVENTF_KEYUP,0)
    ; SendClick [BM_CLICK]  = Full or partial capture of Password entered so far...
    SendMessage_(hW_BTN, #BM_CLICK, 0, 0)
    KillTimer_(hW, idEvent)
  EndIf
EndProcedure

Procedure.s gui_InputRequesterPSWD(wID, Prompt$="Enter Password", Def$=#NULL$)
  ; REV:  121126, skywalk
  SetTimer_(WindowID(wID), #gui_TimerPSWDCHAR, 10, @gui_InputRequesterPSWD_CB())
  ProcedureReturn InputRequester(GetWindowTitle(wID), Prompt$, Def$)
EndProcedure

#AESkey     = 2
#AESinit    = 4
#pwdYOU     = 6
#pwdEncrypt = 8
#pwdDecrypt = 10
#pwdREAL    = 12
If OpenWindow(0,0,0,400,400,"Password Prompt...",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  Define.s pwd$, key$ = "MYSUPERSECRETKEY", AESinit$ = "MYSUPERSECRETINITVECTOR"
  Define.s pwdEncrypt$
  Define.s pwdDecrypt$
  Define.s pwdREAL$ = "PB5"
  Define.i ht = 20, wd = 380, y = 10
  ButtonGadget(0, wd/1.5+10, 10+(ht+y)*4, 100, 25, "Password Prompt")
  TextGadget  (1,      75,10+(ht+y)*0,wd,ht," -- AES KEY (length depends on nKeyBits) -- ")
  StringGadget(#AESkey, 5,5+(ht+y)*1,wd,ht,key$)
  TextGadget  (3,      75,10+(ht+y)*2,wd,ht," -- AES InitVector (only 16 bytes used) -- ")
  StringGadget(#AESinit, 5,5+(ht+y)*3,wd,ht,AESinit$)
  TextGadget  (5,       75,10+(ht+y)*4,wd,ht," -- Password You Entered -- ")
  StringGadget(#pwdYOU, 5,5+(ht+y)*5,wd/1.5,ht,"")
  TextGadget  (7,      75,10+(ht+y)*6,wd,ht," -- AES256+BASE64 Encrypted Password -- ")
  StringGadget(#pwdEncrypt, 5,5+(ht+y)*7,wd,ht,"")
  TextGadget  (9,      75,10+(ht+y)*8,wd,ht," -- AES256+BASE64 Encrypted Password -> Unencrypted -- ")
  StringGadget(#pwdDecrypt,5,5+(ht+y)*9,wd/1.5,ht,"")
  TextGadget  (11,     75,10+(ht+y)*11,wd,ht," -- Password To Match -- ")
  StringGadget(#pwdREAL,5,5+(ht+y)*12,wd/1.5,ht,pwdREAL$)
  SetGadgetColor(#AESkey, #PB_Gadget_BackColor, #Yellow)
  SetGadgetColor(#AESinit, #PB_Gadget_BackColor, #Yellow)
  SetGadgetColor(#pwdYOU, #PB_Gadget_BackColor, #Yellow)
  Define.i evWW
  Repeat
    evWW = WaitWindowEvent()
    Select evWW
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0
        pwd$ = gui_InputRequesterPSWD(0, "Enter Password: " + Str(#gui_TimerPSWDTMO/1000) + " second time limit!", "You did not enter a password before timing out!")
        key$ = GetGadgetText(#AESkey)
        AESinit$ = GetGadgetText(#AESinit)
        SetGadgetText(#pwdYOU, pwd$)
        pwdEncrypt$ = SF_Encrypt_AESB64(pwd$, key$, 256, @AESinit$)
        SetGadgetText(#pwdEncrypt, pwdEncrypt$)
        pwdDecrypt$ = SF_Decrypt_AESB64(pwdEncrypt$, key$, 256, @AESinit$)
        SetGadgetText(#pwdDecrypt, pwdDecrypt$)
        pwdREAL$ = GetGadgetText(#pwdREAL)
        If pwdREAL$ = pwdDecrypt$
          SetGadgetColor(#pwdDecrypt, #PB_Gadget_BackColor, #Green)
          SetGadgetColor(#pwdREAL, #PB_Gadget_BackColor, #Green)
        Else
          SetGadgetColor(#pwdDecrypt, #PB_Gadget_BackColor, RGB(241, 77, 77))
          SetGadgetColor(#pwdREAL, #PB_Gadget_BackColor, RGB(241, 77, 77))
        EndIf
      EndIf
    EndSelect
  ForEver
EndIf
EDIT: added an error check to the encrypt/decrypt functions.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Re: [Windows] Passwords and Prompts

Post by GeoTrail »

Nice one, thanks for sharing :)
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: [Windows] Passwords and Prompts

Post by Kwai chang caine »

Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply