Page 1 of 1

[Windows] Passwords and Prompts

Posted: Tue Nov 27, 2012 3:40 am
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.

Re: [Windows] Passwords and Prompts

Posted: Sat Dec 01, 2012 2:35 pm
by GeoTrail
Nice one, thanks for sharing :)

Re: [Windows] Passwords and Prompts

Posted: Sat Dec 01, 2012 11:23 pm
by Kwai chang caine
Thanks for sharing 8)