-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
#!/usr/bin/env expect | ||
|
||
# Walk a NodeMCU device through some basic functionality tests. | ||
# | ||
# Requires `socat` and `openssl` on the host side; tested only on Linux. | ||
# | ||
# Tries to guess the host's IP address using `ip route get`, but this can be | ||
# overridden with the -ip command line option. | ||
# | ||
# A typical invocation looks like: | ||
# ./tls-test.expect -serial /dev/ttyUSB3 -wifi "$(cat wificmd)" | ||
# | ||
# where the file `wificmd` contains something like | ||
# wifi.setmode(wifi.STATION); wifi.sta.config({...}); wifi.sta.connect() | ||
# where the ... is filled in with the local network's configuration. All on | ||
# one line, tho', so that the script just gets one prompt back. | ||
# | ||
# For debugging the test itself, it may be useful to invoke expect with -d, | ||
# which will give a great deal of diagnostic information about the expect state | ||
# machine's internals: | ||
# expect -d ./tls-test.expect ... | ||
|
||
|
||
package require struct::stack | ||
|
||
::struct::stack ulogstack | ||
proc pushulog { new } { | ||
ulogstack push [log_user -info] | ||
log_user ${new} | ||
} | ||
proc populog { } { log_user [ulogstack pop] } | ||
|
||
proc send_exp_prompt { sid cmd } { | ||
send -i ${sid} -- "${cmd}\n" | ||
expect { | ||
-i ${sid} -ex "\n> " { } | ||
} | ||
} | ||
|
||
proc send_exp_prompt_c { sid cmd } { | ||
send -i ${sid} -- "${cmd}\n" | ||
expect { | ||
-i ${sid} -ex "\n>> " { } | ||
} | ||
} | ||
|
||
proc genectls { curve pfx } { | ||
exec "openssl" "ecparam" "-genkey" "-name" ${curve} "-out" "${pfx}.key" | ||
exec "openssl" "req" "-new" "-sha256" "-subj" "/CN=${curve}" "-key" "${pfx}.key" "-out" "${pfx}.csr" | ||
exec "openssl" "req" "-x509" "-sha256" "-days" "365" "-key" "${pfx}.key" "-in" "${pfx}.csr" "-out" "${pfx}.crt" | ||
} | ||
|
||
proc preptls { victim } { | ||
send_exp_prompt_c ${victim} "function tlsbasic(id,port,host)" | ||
send_exp_prompt_c ${victim} " local c = tls.createConnection()" | ||
send_exp_prompt_c ${victim} " c:on(\"receive\", function(sck, d) print(\"RECV\",id,d) end)" | ||
send_exp_prompt_c ${victim} " c:on(\"connection\", function(sck) print(\"CONN\",id); sck:send(\"GET / HTTP/1.0\\r\\n\\r\\n\") end)" | ||
send_exp_prompt_c ${victim} " c:on(\"disconnection\", function(sck) print(\"DISC\",id) end)" | ||
send_exp_prompt_c ${victim} " c:connect(port,host)" | ||
send_exp_prompt_c ${victim} " return c" | ||
send_exp_prompt ${victim} "end" | ||
} | ||
|
||
# Basic connectivity test, including disconnection of localsid. | ||
proc basicconntest { id localsid victimsid victimconn } { | ||
set timeout 15 | ||
expect { | ||
-i ${localsid} -re ".+" { | ||
# If socat says anything, it's almost surely an error | ||
exit 1 | ||
} | ||
-i ${victimsid} "CONN\t${id}" { } | ||
} | ||
set timeout 2 | ||
pushulog 0 | ||
expect { | ||
-i ${localsid} "GET / HTTP/1.0\r\n\r\n" { | ||
send -i ${localsid} "abracadabra" | ||
} | ||
} | ||
populog | ||
expect { | ||
-i ${victimsid} "RECV\t${id}\tabracadabra" { | ||
send_exp_prompt ${victimsid} "${victimconn}:send(\"test 1 2 3 4\")" | ||
} | ||
} | ||
pushulog 0 | ||
expect { | ||
-i ${localsid} "test 1 2 3 4" { | ||
close -i ${localsid} | ||
} | ||
} | ||
populog | ||
set timeout 15 | ||
expect { | ||
-i ${victimsid} "DISC\t${id}" { } | ||
} | ||
} | ||
|
||
# Generate some TLS certificates for our use, if they don't exist | ||
set fntls256v1 "test-256v1" | ||
if { ! [file exists "${fntls256v1}.key" ] } { genectls "prime256v1" ${fntls256v1} } | ||
set fntls384r1 "test-384r1" | ||
if { ! [file exists "${fntls384r1}.key" ] } { genectls "secp384r1" ${fntls384r1} } | ||
|
||
package require cmdline | ||
set cmd_parameters { | ||
{ serial.arg "/dev/ttyUSB0" "Set the serial interface name" } | ||
{ wifi.arg "" "Command to run to bring up the network" } | ||
{ ip.arg "" "My IP address (will guess if not given)" } | ||
# { debug "Turn on debugging" } | ||
} | ||
set cmd_usage "- A NodeMCU TLS test program" | ||
if {[catch {array set cmdopts [cmdline::getoptions ::argv $cmd_parameters $cmd_usage]}]} { | ||
send_user [cmdline::usage $cmd_parameters $cmd_usage] | ||
exit 0 | ||
} | ||
|
||
# if { ${cmdopts(debug)} } { exp_internal 1 } | ||
|
||
send_user "===> Note: Serial port is ${cmdopts(serial)}; debug is ${cmdopts(debug)} <===\n" | ||
|
||
spawn "socat" "STDIO" "${cmdopts(serial)},rawer,crnl" | ||
set victim ${spawn_id} | ||
close -onexec 1 -i ${victim} | ||
|
||
# Use DTR/RTS signaling to reboot the device | ||
## I'm not sure why we have to keep resetting the mode, but so it goes. | ||
set victimfd [open "${cmdopts(serial)}"] | ||
fconfigure ${victimfd} -mode 115200,n,8,1 -ttycontrol {DTR 0 RTS 1} | ||
sleep 0.1 | ||
fconfigure ${victimfd} -mode 115200,n,8,1 -ttycontrol {DTR 0 RTS 0} | ||
close ${victimfd} | ||
|
||
# Wait for the system to boot | ||
expect { | ||
-i ${victim} "Formatting file system" { | ||
set timeout 60 | ||
exp_continue | ||
} | ||
-i ${victim} "powered by Lua" { } | ||
timeout { | ||
send_user "\n===> Did not see boot sequence; bailing out <===\n" | ||
exit 0 | ||
} | ||
} | ||
# Catch nwf's system bootup, in case we're testing an existing system, | ||
# rather than a blank firmware. | ||
expect { | ||
-i ${victim} "Reset delay!" { send "got:stop()\n" ; expect "> " } | ||
-i ${victim} "> " { } | ||
} | ||
send_user "\n===> Machine has booted <===\n" | ||
|
||
# Program a routine for TLS connections | ||
preptls ${victim} | ||
|
||
# Connect the board to the network | ||
|
||
if {[expr 0 < [string length ${cmdopts(wifi)}]]} { | ||
send_exp_prompt ${victim} ${cmdopts(wifi)} | ||
} | ||
|
||
for {set i 0} {${i} < 10} {incr i} { | ||
send -i ${victim} "=wifi.sta.getip()\n" | ||
expect { | ||
-i ${victim} -re "\n(\[^\n\t]+)\t\[^\t]+\t\[^\t]+\n> " { | ||
set victimip ${expect_out(1,string)} | ||
send_user "\n===> Victim IP address ${victimip} <===\n" | ||
break | ||
} | ||
-i ${victim} -ex "nil\r\n> " { | ||
# must not be connected | ||
sleep 1 | ||
} | ||
} | ||
} | ||
if {[expr 10 == $i]} { | ||
send_user "\n===> Unable to connect to network; bailing out! <===\n" | ||
exit 1 | ||
} | ||
|
||
if {[expr 0 < [string length ${cmdopts(ip)}]]} { | ||
set myip ${cmdopts(ip)} | ||
} else { | ||
# Guess our IP address by using the victim's | ||
spawn "ip" "route" "get" ${victimip} | ||
expect { | ||
-re "src (\[^ ]*) " { | ||
set myip ${expect_out(1,string)} | ||
} | ||
} | ||
close | ||
} | ||
|
||
send_exp_prompt ${victim} "tls.setDebug(2)" | ||
send_exp_prompt ${victim} "tls.cert.verify(false)" | ||
|
||
send_user "\n===> TEST SSL 256v1, no verify <===\n" | ||
|
||
spawn -noecho "socat" "STDIO,cfmakeraw" "OPENSSL-LISTEN:12345,verify=0,certificate=${fntls256v1}.crt,key=${fntls256v1}.key,reuseaddr" | ||
send_exp_prompt ${victim} "c = tlsbasic(0,12345,\"${myip}\")" | ||
basicconntest 0 ${spawn_id} ${victim} "c" | ||
|
||
send_user "\n===> TEST SSL 384r1, no verify <===\n" | ||
|
||
spawn -noecho "socat" "STDIO,cfmakeraw" "OPENSSL-LISTEN:12345,verify=0,certificate=${fntls384r1}.crt,key=${fntls384r1}.key,reuseaddr" | ||
send_exp_prompt ${victim} "c = tlsbasic(1,12345,\"${myip}\")" | ||
basicconntest 1 ${spawn_id} ${victim} "c" | ||
|
||
send_user "\n===> TEST SSL 384r1, verify <===\n" | ||
|
||
set cert [open "${fntls384r1}.crt"] | ||
send_exp_prompt_c ${victim} "tls.cert.verify(\[\[" | ||
while { [gets $cert line] >= 0 } { | ||
send_exp_prompt_c ${victim} $line | ||
} | ||
send_exp_prompt ${victim} "]])" | ||
close ${cert} | ||
send_exp_prompt ${victim} "tls.cert.verify(true)" | ||
|
||
spawn -noecho "socat" "STDIO,cfmakeraw" "OPENSSL-LISTEN:12345,verify=0,certificate=${fntls384r1}.crt,key=${fntls384r1}.key,reuseaddr" | ||
send_exp_prompt ${victim} "c = tlsbasic(2,12345,\"${myip}\")" | ||
basicconntest 2 ${spawn_id} ${victim} "c" | ||
|
||
send_user "\n===> TESTS OK <===\n" |