1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
# -*- coding: utf-8 -*-
#
# Copyright 2019 SoloKeys Developers
#
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
# http://opensource.org/licenses/MIT>, at your option. This file may not be
# copied, modified, or distributed except according to those terms.
import json
import click
import usb.core
import solo
import solo.operations
from solo.cli.key import key
from solo.cli.monitor import monitor
from solo.cli.program import program
from ._checks import init_checks
init_checks()
@click.group()
def solo_cli():
pass
solo_cli.add_command(key)
solo_cli.add_command(monitor)
solo_cli.add_command(program)
@click.command()
def version():
"""Version of solo1 library and tool."""
print(solo.__version__)
solo_cli.add_command(version)
@click.command()
@click.option("--input-seed-file")
@click.argument("output_pem_file")
def genkey(input_seed_file, output_pem_file):
"""Generates key pair that can be used for Solo signed firmware updates.
\b
* Generates NIST P256 keypair.
* Public key must be copied into correct source location in solo bootloader
* The private key can be used for signing updates.
* You may optionally supply a file to seed the RNG for key generating.
"""
vk = solo.operations.genkey(output_pem_file, input_seed_file=input_seed_file)
print("Public key in various formats:")
print()
print([c for c in vk.to_string()])
print()
print("".join(["%02x" % c for c in vk.to_string()]))
print()
print('"\\x' + "\\x".join(["%02x" % c for c in vk.to_string()]) + '"')
print()
solo_cli.add_command(genkey)
@click.command()
@click.argument("verifying-key")
@click.argument("app-hex")
@click.argument("output-json")
def sign(verifying_key, app_hex, output_json):
"""Signs a firmware hex file, outputs a .json file that can be used for signed update."""
msg = solo.operations.sign_firmware(verifying_key, app_hex)
print("Saving signed firmware to", output_json)
with open(output_json, "wb+") as fh:
fh.write(json.dumps(msg).encode())
solo_cli.add_command(sign)
@click.command()
@click.option("--attestation-key", help="attestation key in hex")
@click.option("--attestation-cert", help="attestation certificate file")
@click.option(
"--lock",
help="Indicate to lock device from unsigned changes permanently.",
default=False,
is_flag=True,
)
@click.argument("input_hex_files", nargs=-1)
@click.argument("output_hex_file")
@click.option(
"--end_page",
help="Set APPLICATION_END_PAGE. Should be in sync with firmware settings.",
default=20,
type=int,
)
def mergehex(
attestation_key, attestation_cert, lock, input_hex_files, output_hex_file, end_page
):
"""Merges hex files, and patches in the attestation key.
\b
If no attestation key is passed, uses default Solo Hacker one.
Note that later hex files replace data of earlier ones, if they overlap.
"""
solo.operations.mergehex(
input_hex_files,
output_hex_file,
attestation_key=attestation_key,
APPLICATION_END_PAGE=end_page,
attestation_cert=attestation_cert,
lock=lock,
)
solo_cli.add_command(mergehex)
@click.command()
@click.option(
"-a", "--all", is_flag=True, default=False, help="Show ST DFU devices too."
)
def ls(all):
"""List Solos (in firmware or bootloader mode) and potential Solos in dfu mode."""
solos = solo.client.find_all()
print(":: Solos")
for c in solos:
descriptor = c.dev.descriptor
if hasattr(descriptor, "product_name"):
product_name = descriptor.product_name
elif c.is_solo_bootloader():
product_name = "Solo Bootloader device"
else:
product_name = "FIDO2 device"
if hasattr(descriptor, "serial_number"):
serial_or_path = descriptor.serial_number
else:
serial_or_path = descriptor.path
print(f"{serial_or_path}: {product_name}")
if all:
print(":: Potential Solos in DFU mode")
try:
st_dfus = solo.dfu.find_all()
for d in st_dfus:
dev_raw = d.dev
dfu_serial = dev_raw.serial_number
print(f"{dfu_serial}")
except usb.core.NoBackendError:
print("No libusb available.")
print(
"This error is only relevant if you plan to use the ST DFU interface."
)
print("If you are on Windows, please install a driver:")
print("https://github.com/libusb/libusb/wiki/Windows#driver-installation")
solo_cli.add_command(ls)
|