Hide characters in a string prompt dialog for secure password entry

I am prompting the user for a password with a doprompt dialog, but it shows the password in clear text. This is not very secure, so I would like to hide the characters as is commonly done with passwords.  I thought of creating a panel with a setvariable control with valueColor the same as valueBackColor, but even that shows the entered characters until the user hits return. Does anyone have any ideas how to prompt the user for a password string while hiding the string from view?

In reply to by JimProuty

That is a good idea, but it creates more problems:

1) How can I change the font of the value of the setvariable control separately from the title?

2) How can I distribute a non-standard font to the users of my procedure easily?

Here's a code snippet that creates a notebook subwindow and processes user input with a hook function. It doesn't do what you need but could be adapted.

One other method is to capture every keystroke in the SetVariable procedure, store each of them sequentially in an internal string (wave), and only return * (asterisk) to the panel display for any character input. When the keystroke is a return, then process the function call further using the string (wave).

In reply to by jjweimer

jjweimer wrote:

One other method is to capture every keystroke in the SetVariable procedure,

Note that there's no eventCode for keyboard events in the WMSetVariableAction structure.

In reply to by tony

tony wrote:

 

jjweimer wrote:

 

One other method is to capture every keystroke in the SetVariable procedure,

 

 

Note that there's no eventCode for keyboard events in the WMSetVariableAction structure.

I believe that I have done this in the past by capturing the sva.sval parameter during an eventCode 3 (live update) and/or eventCode 8 (begin edit Igor Pro 7 or later) and then forcing the control to change back to something else at the event. It is not an easy process to manage but it is a doable process if I recall.

Feature Request: This could be handled by the equivalent of an fstyle=8: password mode for setvariable controls.

Thanks all for the good comments.

I followed Tony's notebook with hook idea.

Here is the code using a dot from the Wingding font.

BEWARE: this example code prints the resulting password (which is hidden as the notebook userdata) as plain text in the history.

function Passwordpanel()
    string suser=IgorInfo(7)
    DoWindow/K plogin
    NewPanel/K=1/N=plogin /W=(411,380,721,583) as "Login"
    SetVariable setusr, win=plogin,fSize=14,pos={5,62},size={300,19}, title="User: ", value=_STR:suser
    TitleBox titpw, win=plogin,fSize=14,pos={5,90},size={80,19}, frame=0, title="Password:\r (optional)"
   NewNotebook /F=1 /N=npw /HOST=plogin /W=(80,90,300,116) /OPTS=3
   notebook plogin#npw fSize=14, frameStyle=5, showRuler=0,font="Wingdings"
   SetWindow plogin#npw, activeChildFrame=0
    Button btnCANCEL,win=plogin,fSize=12,pos={174,135},size={105,50}, title="CANCEL", proc=PasswordButtonProc
    Button btnOK,win=plogin,fSize=12,pos={34,136},size={105,50}, title="LOGIN", proc=PasswordButtonProc
    SetWindow plogin, hook(MyHook)=PasswordWindowHook
    PauseForUser plogin
end

Function PasswordWindowHook(s)
    STRUCT WMWinHookStruct &s
   
    Variable hookResult = 0 // 0 if we do not handle event, 1 if we handle it.
    strswitch(s.winName)
        case "plogin#npw"//password notebook
            string password=GetUserData(s.winName, "", "password")
            switch(s.eventCode)
                case 11:    // Keyboard event  
                    switch(s.specialKeyCode)
                        case 300:   //delete
                            SetWindow $s.winName, userdata(password)=RemoveEnding(password)
                            break
                        case 0: //ascii character
                            SetWindow $s.winName, userdata(password)+=s.keyText //store password as userdata
                            break
                        default:
                            return 1
                            break
                    endswitch
                    password=GetUserData(s.winName, "", "password")
                    string blank=Padstring("",strlen(password),108) //Wingdings gives dots
                    GetSelection notebook, $s.winName, 1
                    notebook $s.winName selection={startOfFile,endofFile}, text=blank   //overwrite notebook
                    //print password, blank
                    hookResult = 1  // We handled keystroke
                    break
            endswitch
            break
    endswitch  
    return hookResult       // If non-zero, we handled event and Igor will ignore it.
End

Function PasswordButtonProc(ba) : ButtonControl
    STRUCT WMButtonAction &ba
    switch( ba.eventCode )
        case 2: // mouse up
            strswitch (ba.ctrlname)
                case "btnOK":
                    //evaluate values from login panel
                    ControlInfo /W=plogin setusr
                    string user=S_Value
                    string pswd=GetUserData("plogin#npw", "", "password")
                    print user, pswd    //!!!!!PRINTING PASSWORD IN PLAIN TEXT!!!!!
                    DoWindow/K plogin
                    break
                case "btnCancel":
                    print "cancelled"
                    DoWindow/K plogin
                    break
            endswitch
            break
        case -1: // control being killed
            break
    endswitch
    return 0
End

 

I used a similar approach but used a panel instead of a notebook. I simply hid the SetVariable input by disabling it and putting it off-screen, then temporarily used a global string to pass the password rather than the userdata approach.

Function Passwordpanel()
    //Get password from secure window. Uses the hook function StringEntryHook
    DebuggerOptions enable=0 //disable the debugger, so that downstream errors don't provide a chance to find the plaintext password
    PauseUpdate; Silent 1       // building window...
    NewPanel/K=1/N=PWEntry/W=(465,419,660,500)
    SetDrawLayer UserBack
    DrawText 35,35,"Type password.\rPress Enter when done."
    SetVariable setvar0,pos={7600.00,6400.00},size={50.00,14.00},disable=1,value= _STR:"" //entry box is hidden (disabled) and off-screen
    SetVariable setvar1,pos={25,50},size={150.00,14.00},disable=0,font="Wingdings",value= _STR:"" //box to show number of characters
    SetWindow kwTopWin,hook(SEHook)=StringEntryHook
    PauseForUser PWEntry //wait for user to type in password and press Enter
    SVAR SE=SE //get the password from the global string "SE" that the window hook created
    string pwd=SE //store the password in a local string.
    KillStrings SE //kills the global string "SE". Password lives on in the local string "password"
   
    Print pwd //!!!!!PRINTING PASSWORD IN PLAIN TEXT!!!!!
End

Function StringEntryHook(s)
    STRUCT WMWinHookStruct &s
    Variable hookResult = 0
    ControlInfo setvar0
    string runningString=S_Value
    ControlInfo setvar1
    string dotString=S_Value
    strswitch (s.eventname)
        case "keyboard":
            switch (s.specialKeyCode)
                case 200: //Return
                    String/G SE=runningString
                    setwindow $(s.winname), hook(modified)=$""
                    KillWindow $(s.winname)
                    break
                case 201: //Enter
                    String/G SE=runningString
                    setwindow $(s.winname), hook(modified)=$""
                    KillWindow $(s.winname)
                    break
                case 300: //delete
                    runningString=RemoveEnding(runningString)
                    dotString=RemoveEnding(dotString)
                    String/G SE=runningString
                    setVariable setvar1 value=_STR:dotString
                    break
                case 0: //regular character
                    runningString=runningString+s.keytext
                    setvariable setvar0 value=_STR:runningString
                    dotString=dotString+"l"
                    setVariable setvar1 value=_STR:dotString
                    break
            endswitch
            break
        case "kill":
            String/G SE=runningString
            setwindow $(s.winname), hook(modified)=$""
            break
    endswitch
    return hookResult       // 0 if nothing done, else 1
End

 

In reply to by ajleenheer

This is a very nice bit of code!

I suggest changing this line of Passwordpanel():

    SetVariable setvar1,pos={25,50},size={150.00,14.00},disable=0,font="Wingdings",value= _STR:"" //box to show number of characters

into:

    SetVariable setvar1,pos={25,50},size={150.00,14.00},disable=2,font="Wingdings",value= _STR:"" //box to show number of characters

So that if the user clicks in the control's edit field it doesn't show the typed characters as Wingdings glpyhs:

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More