1#!/usr/bin/python
2#
3# Example Android logcat to wpa_supplicant wrapper for QR Code scans
4# Copyright (c) 2017, Qualcomm Atheros, Inc.
5#
6# This software may be distributed under the terms of the BSD license.
7# See README for more details.
8
9import os
10import sys
11import argparse
12import logging
13import qrcode
14
15scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
16sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
17
18import wpaspy
19
20wpas_ctrl = '/var/run/wpa_supplicant'
21
22def wpas_connect():
23    ifaces = []
24    if os.path.isdir(wpas_ctrl):
25        try:
26            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
27        except OSError as error:
28            print("Could not find wpa_supplicant: ", error)
29            return None
30
31    if len(ifaces) < 1:
32        print("No wpa_supplicant control interface found")
33        return None
34
35    for ctrl in ifaces:
36        try:
37            wpas = wpaspy.Ctrl(ctrl)
38            return wpas
39        except Exception as e:
40            pass
41    return None
42
43def dpp_logcat():
44    for line in iter(sys.stdin.readline, ''):
45        if "ResultHandler: Launching intent: Intent" not in line:
46            continue
47        if "act=android.intent.action.VIEW" not in line:
48            continue
49        uri = None
50        for val in line.split(' '):
51            if val.startswith('dat='):
52                uri = val.split('=', 1)[1]
53                break
54        if not uri:
55            continue
56        if not uri.startswith('DPP:'):
57            continue
58        print("Found DPP bootstrap info URI:")
59        print(uri)
60        wpas = wpas_connect()
61        if not wpas:
62            print("Could not connect to wpa_supplicant")
63            print('')
64            continue
65        res = wpas.request("DPP_QR_CODE " + uri);
66        try:
67            id = int(res)
68        except ValueError:
69            print("QR Code URI rejected")
70            continue
71        print("QR Code URI accepted - ID=%d" % id)
72        print(wpas.request("DPP_BOOTSTRAP_INFO %d" % id))
73        del wpas
74
75def dpp_display(curve):
76        wpas = wpas_connect()
77        if not wpas:
78            print("Could not connect to wpa_supplicant")
79            return
80        res = wpas.request("STATUS")
81        addr = None
82        for line in res.splitlines():
83            if line.startswith("address="):
84                addr = line.split('=')[1]
85                break
86        cmd = "DPP_BOOTSTRAP_GEN type=qrcode"
87        cmd += " chan=81/1"
88        if addr:
89            cmd += " mac=" + addr.replace(':','')
90        if curve:
91            cmd += " curve=" + curve
92        res = wpas.request(cmd)
93        try:
94            id = int(res)
95        except ValueError:
96            print("Failed to generate bootstrap info URI")
97            return
98        print("Bootstrap information - ID=%d" % id)
99        print(wpas.request("DPP_BOOTSTRAP_INFO %d" % id))
100        uri = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id)
101        print(uri)
102        print("ID=%d" % id)
103        qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M,
104                           border=3)
105        qr.add_data(uri, optimize=5)
106        qr.print_ascii(tty=True)
107        print("ID=%d" % id)
108        del wpas
109
110def main():
111    parser = argparse.ArgumentParser(description='Android logcat to wpa_supplicant integration for DPP QR Code operations')
112    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
113                        action='store_const', dest='loglevel',
114                        help='verbose debug output')
115    parser.add_argument('--curve', '-c',
116                        help='set a specific curve (P-256, P-384, P-521, BP-256R1, BP-384R1, BP-512R1) for key generation')
117    parser.add_argument('command', choices=['logcat',
118                                            'display'],
119                        nargs='?')
120    args = parser.parse_args()
121
122    logging.basicConfig(level=args.loglevel)
123
124    if args.command == "logcat":
125        dpp_logcat()
126    elif args.command == "display":
127        dpp_display(args.curve)
128
129if __name__ == '__main__':
130    main()
131