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, 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, 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