From 08a56856a24d007b7386f1f0ea9a0fa824d28b16 Mon Sep 17 00:00:00 2001 From: dhairyashah1 Date: Thu, 6 Apr 2023 20:43:31 +0530 Subject: [PATCH] [mfg_tool] Replaces spake2p, mfg_gen, chip-tool binary executable usages in 'tools/mfg_tool/mfg_tool.py' with respective python implementations - spake2p.py, mfg_gen.py, generate_setup_payload.py Removes functions get_chip_qrcode, get_chip_manualcode, get_qrcode_args & get_manualcode_args from 'tools/mfg_tool/utlis.py' Adds python_stdnum dependency for using 'tools/mfg_tool/mfg_tool.py' in `tools/mfg_tool/requirements.txt' Signed-off-by: dhairyashah1 [mfg_tool] Adds python_stdnum dependency for using mfg_tool.py in 'requirements.txt' Signed-off-by: dhairyashah1 mfg_tool: Adds mfg_tool dependencies reinstallation note in 'RELEASE_NOTES.md' Signed-off-by: dhairyashah1 [mfg_tool] Adds details on reinstalling tools/mfg_tool dependencies Signed-off-by: dhairyashah1 --- RELEASE_NOTES.md | 9 +++ tools/mfg_tool/mfg_tool.py | 99 ++++++++++++++++----------------- tools/mfg_tool/requirements.txt | 1 + tools/mfg_tool/utils.py | 57 ------------------- 4 files changed, 58 insertions(+), 108 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 333a275f3..e97a2b4dc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,12 @@ +# 7-April-2023 + +- `tools/mfg_tool/mfg_tool.py` now uses `connectedhomeip/connectedhomeip/src/setup_payload/python/generate_setup_payload.py` instead of previously used compiled `chip-tool` binary executable which additionally depends on `python-stdnum` module. +- Reinstall dependencies to use `mfg_tool` by running the following commands - +``` +source $IDF_PATH/export.sh +python3 -m pip install -r tools/mfg_tool/requirements.txt +``` + # 15-March-2023 API Change diff --git a/tools/mfg_tool/mfg_tool.py b/tools/mfg_tool/mfg_tool.py index c05585a97..25cab8c9b 100755 --- a/tools/mfg_tool/mfg_tool.py +++ b/tools/mfg_tool/mfg_tool.py @@ -23,6 +23,7 @@ import sys import csv import uuid import shutil +import base64 import random import logging import binascii @@ -32,16 +33,27 @@ import pyqrcode from chip_nvs import * from utils import * from datetime import datetime +from types import SimpleNamespace if not os.getenv('IDF_PATH'): logging.error("IDF_PATH environment variable is not set") sys.exit(1) +if not os.getenv('ESP_MATTER_PATH'): + logging.error("ESP_MATTER_PATH environment variable is not set") + sys.exit(1) + +sys.path.insert(0, os.path.join(os.getenv('ESP_MATTER_PATH'), 'connectedhomeip', 'connectedhomeip', 'scripts', 'tools', 'spake2p')) +from spake2p import generate_verifier + +sys.path.insert(0, os.path.join(os.getenv('IDF_PATH'), 'tools', 'mass_mfg')) +from mfg_gen import generate + +sys.path.insert(0, os.path.join(os.getenv('ESP_MATTER_PATH'), 'connectedhomeip', 'connectedhomeip', 'src', 'setup_payload', 'python')) +from generate_setup_payload import SetupPayload, CommissioningFlow + TOOLS = { - 'spake2p': None, 'chip-cert': None, - 'chip-tool': None, - 'mfg_gen': None, } PAI = { @@ -67,10 +79,6 @@ UUIDs = list() def check_tools_exists(args): - TOOLS['spake2p'] = shutil.which('spake2p') - if TOOLS['spake2p'] is None: - logging.error('spake2p not found, please add spake2p path to PATH environment variable') - sys.exit(1) # if the certs and keys are not in the generated partitions or the specific dac cert and key are used, # the chip-cert is not needed. if args.paa or (args.pai and (args.dac_cert is None and args.dac_key is None)): @@ -79,40 +87,26 @@ def check_tools_exists(args): logging.error('chip-cert not found, please add chip-cert path to PATH environment variable') sys.exit(1) - TOOLS['chip-tool'] = shutil.which('chip-tool') - if TOOLS['chip-tool'] is None: - logging.error('chip-tool not found, please add chip-tool path to PATH environment variable') - sys.exit(1) - - TOOLS['mfg_gen'] = os.sep.join([os.getenv('IDF_PATH'), 'tools', 'mass_mfg', 'mfg_gen.py']) - if not os.path.exists(TOOLS['mfg_gen']): - logging.error('mfg_gen.py not found, please make sure IDF_PATH environment variable is set correctly') - sys.exit(1) - logging.debug('Using following tools:') - logging.debug('spake2p: {}'.format(TOOLS['spake2p'])) logging.debug('chip-cert: {}'.format(TOOLS['chip-cert'])) - logging.debug('chip-tool: {}'.format(TOOLS['chip-tool'])) - logging.debug('mfg_gen: {}'.format(TOOLS['mfg_gen'])) def generate_passcodes(args): iter_count_max = 10000 salt_len_max = 32 - - cmd = [ - TOOLS['spake2p'], 'gen-verifier', - '--count', str(args.count), - '--iteration-count', str(iter_count_max), - '--salt-len', str(salt_len_max), - '--out', OUT_FILE['pin_csv'], - ] - - # If passcode is provided, use it - if (args.passcode): - cmd.extend(['--pin-code', str(args.passcode)]) - - execute_cmd(cmd) + with open(OUT_FILE['pin_csv'], 'w', newline='') as f: + writer = csv.writer(f) + writer.writerow(["Index", "PIN Code", "Iteration Count", "Salt", "Verifier"]) + for i in range(0, args.count): + if args.passcode: + passcode = args.passcode + else: + passcode = random.randint(1, 99999998) + if passcode in INVALID_PASSCODES: + passcode -= 1 + salt = os.urandom(salt_len_max) + verifier = generate_verifier(passcode, salt, iter_count_max) + writer.writerow([i, passcode, iter_count_max, base64.b64encode(salt).decode('utf-8'), base64.b64encode(verifier).decode('utf-8')]) def generate_discriminators(args): @@ -402,33 +396,36 @@ def generate_summary(args): for row in pin_disc_dict: pincode = row['PIN Code'] discriminator = row['Discriminator'] - qrcode = get_chip_qrcode(TOOLS['chip-tool'], args.vendor_id, args.product_id, - args.commissioning_flow, discriminator, pincode, args.discovery_mode) - manualcode = get_chip_manualcode(TOOLS['chip-tool'], args.vendor_id, args.product_id, - args.commissioning_flow, discriminator, pincode) + payloads = SetupPayload(int(discriminator), int(pincode), 1 << args.discovery_mode, CommissioningFlow(args.commissioning_flow), + args.vendor_id, args.product_id) + qrcode = payloads.generate_qrcode() + manualcode = payloads.generate_manualcode() summary_csv_data += summary_lines[1 + int(row['Index'])] + ',' + pincode + ',' + qrcode + ',' + manualcode + '\n' with open(summary_csv, 'w') as scsvf: scsvf.write(summary_csv_data) def generate_partitions(suffix, size, encrypt): - cmd = [ - 'python3', TOOLS['mfg_gen'], 'generate', - OUT_FILE['config_csv'], OUT_FILE['mcsv'], - suffix, hex(size), '--outdir', OUT_DIR['top'] - ] - + partition_args = SimpleNamespace(fileid = None, + version = 2, + inputkey = None, + outdir = OUT_DIR['top'], + conf = OUT_FILE['config_csv'], + values = OUT_FILE['mcsv'], + size = hex(size), + prefix = suffix) if encrypt: - cmd.append('--keygen') - - execute_cmd(cmd) + partition_args.keygen = True + else: + partition_args.keygen = False + generate(partition_args) def generate_onboarding_data(args, index, discriminator, passcode): - chip_manualcode = get_chip_manualcode(TOOLS['chip-tool'], args.vendor_id, args.product_id, - args.commissioning_flow, discriminator, passcode) - chip_qrcode = get_chip_qrcode(TOOLS['chip-tool'], args.vendor_id, args.product_id, - args.commissioning_flow, discriminator, passcode, args.discovery_mode) + payloads = SetupPayload(discriminator, passcode, 1 << args.discovery_mode, CommissioningFlow(args.commissioning_flow), + args.vendor_id, args.product_id) + chip_qrcode = payloads.generate_qrcode() + chip_manualcode = payloads.generate_manualcode() logging.info('Generated QR code: ' + chip_qrcode) logging.info('Generated manual code: ' + chip_manualcode) diff --git a/tools/mfg_tool/requirements.txt b/tools/mfg_tool/requirements.txt index 52a0dbdfc..8daeea7c3 100644 --- a/tools/mfg_tool/requirements.txt +++ b/tools/mfg_tool/requirements.txt @@ -5,3 +5,4 @@ future==0.18.2 pycparser==2.21 pypng==0.0.21 PyQRCode==1.2.1 +python_stdnum==1.18 \ No newline at end of file diff --git a/tools/mfg_tool/utils.py b/tools/mfg_tool/utils.py index feac25e70..6e232818e 100644 --- a/tools/mfg_tool/utils.py +++ b/tools/mfg_tool/utils.py @@ -277,60 +277,3 @@ def execute_cmd(cmd): logging.error('[stderr]: {}'.format(status.stderr.decode('utf-8').strip())) logging.error('Command failed with error: {}'.format(e)) sys.exit(1) - - -def get_manualcode_args(vid, pid, flow, discriminator, passcode): - payload_args = list() - payload_args.append('--discriminator') - payload_args.append(str(discriminator)) - payload_args.append('--setup-pin-code') - payload_args.append(str(passcode)) - payload_args.append('--version') - payload_args.append('0') - payload_args.append('--vendor-id') - payload_args.append(str(vid)) - payload_args.append('--product-id') - payload_args.append(str(pid)) - payload_args.append('--commissioning-mode') - payload_args.append(str(flow)) - return payload_args - - -def get_qrcode_args(vid, pid, flow, discriminator, passcode, disc_mode): - payload_args = get_manualcode_args(vid, pid, flow, discriminator, passcode) - payload_args.append('--rendezvous') - payload_args.append(str(1 << disc_mode)) - return payload_args - - -def get_chip_qrcode(chip_tool, vid, pid, flow, discriminator, passcode, disc_mode): - payload_args = get_qrcode_args(vid, pid, flow, discriminator, passcode, disc_mode) - cmd_args = [chip_tool, 'payload', 'generate-qrcode'] - cmd_args.extend(payload_args) - data = subprocess.check_output(cmd_args) - - # Command output is as below: - # \x1b[0;32m[1655386003372] [23483:7823617] CHIP: [TOO] QR Code: MT:Y.K90-WB010E7648G00\x1b[0m - return data.decode('utf-8').split('QR Code: ')[1][:QRCODE_LEN] - - -def get_chip_manualcode(chip_tool, vid, pid, flow, discriminator, passcode): - payload_args = get_manualcode_args(vid, pid, flow, discriminator, passcode) - cmd_args = [chip_tool, 'payload', 'generate-manualcode'] - cmd_args.extend(payload_args) - data = subprocess.check_output(cmd_args) - - # Command output is as below: - # \x1b[0;32m[1655386909774] [24424:7837894] CHIP: [TOO] Manual Code: 749721123365521327689\x1b[0m\n - # OR - # \x1b[0;32m[1655386926028] [24458:7838229] CHIP: [TOO] Manual Code: 34972112338\x1b[0m\n - # Length of manual code depends on the commissioning flow: - # For standard commissioning flow it is 11 digits - # For User-intent and custom commissioning flow it is 21 digits - manual_code_len = LONG_MANUALCODE_LEN if flow else SHORT_MANUALCODE_LEN - ret = data.decode('utf-8').split('Manual Code: ')[1][:manual_code_len] - # For 11-digits manual code, use the format 'XXXX-XXX-XXXX' - # TODO: change the format of 21-digits maunal code - if manual_code_len == SHORT_MANUALCODE_LEN: - ret = ret[:4] + '-' + ret[4:7] + '-' + ret[7:] - return ret