Merge "gfch100: more peer cleanup"
diff --git a/wifi/configs.py b/wifi/configs.py
index 676182a..3377a08 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -4,6 +4,8 @@
 
 import subprocess
 
+import Crypto.Protocol.KDF
+
 import experiment
 import utils
 
@@ -133,12 +135,6 @@
 wpa_ptk_rekey=0
 """
 
-_WPA_SUPPLICANT_UNSECURED_TPL = """network={{
-\tssid="{ssid}"
-\tkey_mgmt=NONE
-}}
-"""
-
 
 def generate_hostapd_config(
     phy_info, interface, band, channel, width, protocols, psk, opt):
@@ -327,22 +323,55 @@
   return '\n'.join(hostapd_conf_parts)
 
 
+def make_network_block(network_block_lines):
+  return 'network={\n%s\n}\n' % '\n'.join(network_block_lines)
+
+
+def open_network_lines(ssid):
+  return ['\tssid="%s"' % utils.sanitize_ssid(ssid),
+          '\tkey_mgmt=NONE']
+
+
+def wpa_network_lines(ssid, passphrase):
+  """Like `wpa_passphrase "$ssid" "$passphrase"`, but more convenient output.
+
+  This generates raw config lines, so we can update the config when the defaults
+  don't make sense for us without doing parsing.
+
+  N.b. wpa_passphrase double quotes provided SSID and passphrase arguments, and
+  does not escape quotes or backslashes.
+
+  Args:
+    ssid: a wifi network SSID
+    passphrase: a wifi network PSK
+  Returns:
+    lines of a network block that will let wpa_supplicant join this network
+  """
+  clean_ssid = utils.sanitize_ssid(ssid)
+  network_lines = ['\tssid="%s"' % clean_ssid]
+  clean_passphrase = utils.validate_and_sanitize_psk(passphrase)
+  if len(clean_passphrase) == 64:
+    network_lines += ['\tpsk=%s' % clean_passphrase]
+  else:
+    raw_psk = Crypto.Protocol.KDF.PBKDF2(clean_passphrase, clean_ssid, 32, 4096)
+    hex_psk = ''.join(ch.encode('hex') for ch in raw_psk)
+    network_lines += ['\t#psk="%s"' % clean_passphrase, '\tpsk=%s' % hex_psk]
+
+  return network_lines
+
+
 def generate_wpa_supplicant_config(ssid, passphrase, opt):
   """Generate a wpa_supplicant config from the provided arguments."""
-
-  if passphrase is not None:
-    network_block = subprocess.check_output(
-        ('wpa_passphrase',
-         utils.sanitize_ssid(ssid),
-         utils.validate_and_sanitize_psk(passphrase)))
+  if passphrase is None:
+    network_block_lines = open_network_lines(ssid)
   else:
-    network_block = _WPA_SUPPLICANT_UNSECURED_TPL.format(ssid=ssid)
+    network_block_lines = wpa_network_lines(ssid, passphrase)
 
+  network_block_lines.append('\tscan_ssid=1')
   if opt.bssid:
-    network_block_lines = network_block.splitlines(True)
-    network_block_lines[-1:-1] = ['\tbssid=%s\n' %
-                                  utils.validate_and_sanitize_bssid(opt.bssid)]
-    network_block = ''.join(network_block_lines)
+    network_block_lines.append('\tbssid=%s' %
+                               utils.validate_and_sanitize_bssid(opt.bssid))
+  network_block = make_network_block(network_block_lines)
 
   lines = [
       'ctrl_interface=/var/run/wpa_supplicant',
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index 684c45f..ab5d6c7 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python -S
+#!/usr/bin/python
 
 """Tests for configs.py."""
 
@@ -17,6 +17,7 @@
 \tssid="some ssid"
 \t#psk="some passphrase"
 \tpsk=41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f
+\tscan_ssid=1
 }
 """
 
@@ -27,6 +28,7 @@
 \tssid="some ssid"
 \t#psk="some passphrase"
 \tpsk=41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f
+\tscan_ssid=1
 \tbssid=12:34:56:78:90:ab
 }
 """
@@ -39,6 +41,7 @@
 network={
 \tssid="some ssid"
 \tkey_mgmt=NONE
+\tscan_ssid=1
 \tbssid=12:34:56:78:90:ab
 }
 """
@@ -395,6 +398,47 @@
     self.supports_provisioning = False
 
 
+def wpa_passphrase(ssid, passphrase):
+  return configs.make_network_block(
+      configs.wpa_network_lines(ssid, passphrase))
+
+
+def wpa_passphrase_subprocess(ssid, passphrase):
+  return subprocess.check_output(
+      ('wpa_passphrase',
+       utils.sanitize_ssid(ssid),
+       utils.validate_and_sanitize_psk(passphrase)))
+
+
+@wvtest.wvtest
+def wpa_passphrase_test():
+  """Make sure the configs we generate are the same as wpa_passphrase."""
+  for testdata in (
+      ('some ssid', 'some passphrase'),
+      (r'some\ssid', 'some passphrase'),
+      ('some ssid', r'some\passphrase'),
+      ('some"ssid', 'some passphrase'),
+      ('some ssid', 'some"passphrase')):
+    got, want = wpa_passphrase(*testdata), wpa_passphrase_subprocess(*testdata)
+    wvtest.WVPASSEQ(got, want)
+
+
+@wvtest.wvtest
+def wpa_raw_psk_test():
+  """Make sure we do the right thing when we get a raw PSK too."""
+  ssid = 'some ssid'
+  psk = '41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f'
+
+  got = wpa_passphrase(ssid, psk)
+  # hard code since `wpa_passphrase` dies if given a 64-character hex psk
+  want = """network={
+\tssid="some ssid"
+\tpsk=41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f
+}
+"""
+  wvtest.WVPASSEQ(got, want)
+
+
 # pylint: disable=protected-access
 @wvtest.wvtest
 def generate_hostapd_config_test():
diff --git a/wifi/wifi_test.py b/wifi/wifi_test.py
index 10c01ad..7b5f3f4 100755
--- a/wifi/wifi_test.py
+++ b/wifi/wifi_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python -S
+#!/usr/bin/python
 
 """Tests for wifi.py."""