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 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More