Richard Suchenwirth 2005-07-31: Instead of letting users enter a known password, one can also ask them to apply a "secret" formula to a random set of variable values ("challenge") and enter the result. This way, even though challenge and response are openly transmitted, an onlooker can not easily deduce the underlying formula, as compared with a password.
Here is some code to experiment with this concept. User interaction can be done either via stdio, or a dialog widget. Both return the user input and the random values of the variables.
See Also edit
- A little login dialog
Description edit
proc pf_stdio {} {
puts -nonewline "Username: "
flush stdout
gets stdin user
foreach var {A B C} {set $var [lpick {1 2 3 4 5 6 7 8 9}]}
puts -nonewline "A: $A B: $B C: $C > "
flush stdout
gets stdin res
list $user $A $B $C $res
}
proc pf_dialog {} {
toplevel .w
label .w.1 -text Username:
entry .w.2 -textvar ::user
foreach var {A B C} {set $var [lpick {1 2 3 4 5 6 7 8 9}]}
label .w.3 -text "A: $A B: $B C: $C > "
entry .w.4 -textvar ::res
bind .w.4 <Return> {.w.ok invoke}
button .w.ok -text " OK " -command {set ok 1}
button .w.c -text Cancel -command {set ok 0}
grid .w.1 .w.2 - -sticky ew
grid .w.3 .w.4 - -sticky ew
grid x .w.ok .w.c -sticky ew
raise .w
vwait ::ok
destroy .w
if {!$::ok} return
list $::user $A $B $C $::res
}
The validation function takes the result list plus the name of an accessor function that returns the formula associated to a user, in a format like (A+B)*(C-1). For simple calculation without paper trail, the formula should contain only the operators +, -, *, in addition to the variable names, and possibly integer constants, and parens. For
expr evaluation, $ signs are inserted in the formula before every variable name, with
regsub.
proc pf_validate {reslist function} {
if {![llength $reslist]} {return 0}
foreach {user A B C res} $reslist break
set formula [$function $user]
if {$formula eq ""} {return 0}
regsub -all {([ABC])} $formula {$\1} formula
expr ($formula) eq {$res}
}
#-- Generally useful routine:
proc lpick list {lindex $list [expr {int(rand()*[llength $list])}]}
Now testing (either stdio or the dialog, depending on the presence of Tk, that is, whether you call this script with
tclsh or
wish). The accessor function is very simple, barely sufficient to test for a single user:
proc verysimple user {
if {$user eq "suchenwi"} {return "(A+B)*(C-1)"}
}
set Tk [expr {[package version Tk] ne ""}]
set interaction [expr {$Tk? "pf_dialog" : "pf_stdio"}]
set hit 0
foreach try {1 2 3} { #-- allow three attempts, as mental arithmetics may go wrong
set answer [$interaction]
if [pf_validate $answer verysimple] {incr hit; break}
}
if !$hit {error "Access denied!"; exit 1}
#... passform-protected code starts here...
if $Tk {
pack [label .0 -text "welcome, [lindex $answer 0]!"]
raise .
} else {
puts "welcome, [lindex $answer 0]!"
}