@@ -1,94 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm).
|
||||
# Copyright (c) 2022 Pol Henarejos.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
VERSION_MAJOR="6" #Version of Pico Keys SDK
|
||||
VERSION_MINOR="0"
|
||||
|
||||
echo "----------------------------"
|
||||
echo "VID/PID patcher for Pico HSM"
|
||||
echo "----------------------------"
|
||||
echo ""
|
||||
|
||||
if [ "$#" -le 0 ]; then
|
||||
echo "Usage: $0 VID:PID [input_uf2_file] [output_uf2_file]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IFS=':' read -r -a ARR <<< "$1"
|
||||
|
||||
if [ ${#ARR[@]} -ne 2 ]; then
|
||||
echo "ERROR: Specify vendor and product ids as VID:PID (e.g., $0 CAFE:1234)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VID=${ARR[0]}
|
||||
PID=${ARR[1]}
|
||||
|
||||
if [ ${#VID} -ne 4 ]; then
|
||||
echo "ERROR: VID length must be 4 hexadecimal characters"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${#PID} -ne 4 ]; then
|
||||
echo "ERROR: PID length must be 4 hexadecimal characters"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $VID =~ ^[0-9A-Fa-f]{1,}$ ]] ; then
|
||||
echo "ERROR: VID must contain hexadecimal characters"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $PID =~ ^[0-9A-Fa-f]{1,}$ ]] ; then
|
||||
echo "ERROR: PID must contain hexadecimal characters"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
UF2_FILE_IF="hsm2040.uf2"
|
||||
UF2_FILE_OF="$UF2_FILE_IF"
|
||||
|
||||
if [ "$#" -ge 2 ]; then
|
||||
UF2_FILE_IF="$2"
|
||||
UF2_FILE_OF="$UF2_FILE_IF"
|
||||
fi
|
||||
|
||||
if [ "$#" -ge 3 ]; then
|
||||
UF2_FILE_OF="$3"
|
||||
fi
|
||||
|
||||
|
||||
echo -n "Patching ${UF2_FILE_IF}... "
|
||||
|
||||
if [[ ! -f "$UF2_FILE_IF" ]]; then
|
||||
echo "ERROR: UF2 file ${UF2_FILE_IF} does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$UF2_FILE_IF" != "$UF2_FILE_OF" ]; then
|
||||
cp -R $UF2_FILE_IF $UF2_FILE_OF
|
||||
fi
|
||||
|
||||
LITTLE_VID="\x${VID:2:2}\x${VID:0:2}"
|
||||
LITTLE_PID="\x${PID:2:2}\x${PID:0:2}"
|
||||
|
||||
perl -pi -e "s/\xff\xfe\xfd\xfc\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/" $UF2_FILE_OF
|
||||
|
||||
echo "Done!"
|
||||
echo ""
|
||||
echo "Patched file was saved in ${UF2_FILE_OF}"
|
||||
@@ -57,6 +57,8 @@ from argparse import RawTextHelpFormatter
|
||||
|
||||
pin = None
|
||||
|
||||
BOOTKEY = [225, 209, 107, 167, 100, 171, 215, 18, 212, 239, 110, 62, 221, 116, 78, 213, 99, 140, 38, 11, 119, 28, 249, 129, 81, 17, 11, 175, 172, 155, 200, 113]
|
||||
|
||||
def hexy(a):
|
||||
return [hex(i) for i in a]
|
||||
|
||||
@@ -67,6 +69,7 @@ def parse_args():
|
||||
parser.add_argument('--pin', help='PIN number')
|
||||
parser_init.add_argument('--so-pin', help='SO-PIN number')
|
||||
parser_init.add_argument('--silent', help='Confirms initialization silently.', action='store_true')
|
||||
parser_init.add_argument('--no-dev-cert', help='Do not request a device certificate (it will use a self-signed certificate). Do not use if attestation is needed.', action='store_true', default=False)
|
||||
|
||||
parser_attestate = subparser.add_parser('attestate', help='Generates an attestation report for a private key and verifies the private key was generated in the devices or outside.')
|
||||
parser_attestate.add_argument('-k', '--key', help='The private key index', metavar='KEY_ID')
|
||||
@@ -94,11 +97,15 @@ def parse_args():
|
||||
parser_phy = subparser.add_parser('phy', help='Set PHY options.')
|
||||
subparser_phy = parser_phy.add_subparsers(title='commands', dest='subcommand', required=True)
|
||||
parser_phy_vp = subparser_phy.add_parser('vidpid', help='Sets VID/PID. Use VID:PID format (e.g. 1234:5678)')
|
||||
parser_phy_ledn = subparser_phy.add_parser('led', help='Sets LED GPIO number.')
|
||||
parser_phy_ledn = subparser_phy.add_parser('led_gpio', help='Sets LED GPIO number.')
|
||||
parser_phy_optwcid = subparser_phy.add_parser('wcid', help='Enable/Disable Web CCID interface.')
|
||||
parser_phy_vp.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?')
|
||||
parser_phy_ledn.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?')
|
||||
parser_phy_optwcid.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable Web CCID interface.', nargs='?')
|
||||
parser_phy_ledbtness = subparser_phy.add_parser('led_brightness', help='Sets LED max. brightness.')
|
||||
parser_phy_ledbtness.add_argument('value', help='Value of the max. brightness.', metavar='VAL', nargs='?')
|
||||
parser_phy_optdimm = subparser_phy.add_parser('led_dimmable', help='Enable/Disable LED dimming.')
|
||||
parser_phy_optdimm.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable LED dimming.', nargs='?')
|
||||
|
||||
parser_secure = subparser.add_parser('secure', help='Manages security of Pico HSM.')
|
||||
subparser_secure = parser_secure.add_subparsers(title='commands', dest='subcommand', required=True)
|
||||
@@ -131,12 +138,19 @@ def parse_args():
|
||||
parser_keygen = subparser.add_parser('keygen', help='Generates private keypair or secret key.')
|
||||
subparser_keygen = parser_keygen.add_subparsers(title='commands', dest='subcommand', required=True)
|
||||
parser_keygen_aes = subparser_keygen.add_parser('aes', help='Generates an AES key.')
|
||||
parser_keygen_aes.add_argument('--size', help='Specifies the size of AES key [128, 192 or 256]',choices=[128, 192, 256], default=128, type=int)
|
||||
parser_keygen_aes.add_argument('--size', help='Specifies the size of AES key [128, 192 or 256]', choices=[128, 192, 256], default=128, type=int)
|
||||
parser_keygen_x25519 = subparser_keygen.add_parser('x25519', help='Generates a private X25519 keypair.')
|
||||
parser_keygen_x448 = subparser_keygen.add_parser('x448', help='Generates a private X448 keypair.')
|
||||
parser_keygen_x25519 = subparser_keygen.add_parser('ed25519', help='Generates a private Ed25519 keypair.')
|
||||
parser_keygen_x448 = subparser_keygen.add_parser('ed448', help='Generates a private Ed448 keypair.')
|
||||
|
||||
parser_otp = subparser.add_parser('otp', help='Read/write OTP values.')
|
||||
parser_otp.add_argument('subcommand', choices=['read', 'write', 'secure_boot'], help='Read, write or enable Secure Boot', nargs='?')
|
||||
parser_otp.add_argument('--row', help='OTP row (in HEX)', required='write' in sys.argv or 'read' in sys.argv)
|
||||
parser_otp.add_argument('-d', '--data', help='Data to write (in HEX) [e.g. 0011223344556677889900AABBCCDDEEFF]', required='write' in sys.argv)
|
||||
parser_otp.add_argument('--lock', help='Lock & protect (no other firmwares can be loaded)', action='store_true')
|
||||
parser_otp.add_argument('--index', help='Bootkey index [0-3]', type=int, default=0, choices=[0, 1, 2, 3])
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
@@ -206,24 +220,25 @@ def initialize(picohsm, args):
|
||||
so_pin = '57621880'
|
||||
|
||||
picohsm.initialize(pin=pin, sopin=so_pin)
|
||||
response = picohsm.get_contents(DOPrefixes.EE_CERTIFICATE_PREFIX, 0x00)
|
||||
if (not args.no_dev_cert):
|
||||
response = picohsm.get_contents(DOPrefixes.EE_CERTIFICATE_PREFIX, 0x00)
|
||||
|
||||
cert = bytearray(response)
|
||||
Y = CVC().decode(cert).pubkey().find(0x86).data()
|
||||
print(f'Public Point: {hexlify(Y).decode()}')
|
||||
cert = bytearray(response)
|
||||
Y = CVC().decode(cert).pubkey().find(0x86).data()
|
||||
print(f'Public Point: {hexlify(Y).decode()}')
|
||||
|
||||
pbk = base64.urlsafe_b64encode(Y)
|
||||
data = urllib.parse.urlencode({'pubkey': pbk}).encode()
|
||||
j = get_pki_data('cvc', data=data)
|
||||
print('Device name: '+j['devname'])
|
||||
dataef = base64.urlsafe_b64decode(
|
||||
j['cvcert']) + base64.urlsafe_b64decode(j['dvcert']) + base64.urlsafe_b64decode(j['cacert'])
|
||||
pbk = base64.urlsafe_b64encode(Y)
|
||||
data = urllib.parse.urlencode({'pubkey': pbk}).encode()
|
||||
j = get_pki_data('cvc', data=data)
|
||||
print('Device name: '+j['devname'])
|
||||
dataef = base64.urlsafe_b64decode(
|
||||
j['cvcert']) + base64.urlsafe_b64decode(j['dvcert']) + base64.urlsafe_b64decode(j['cacert'])
|
||||
|
||||
picohsm.select_file(0x2f02)
|
||||
response = picohsm.put_contents(0x0000, data=dataef)
|
||||
picohsm.select_file(0x2f02)
|
||||
response = picohsm.put_contents(0x0000, data=dataef)
|
||||
|
||||
print('Certificate uploaded successfully!')
|
||||
print('')
|
||||
print('Certificate uploaded successfully!')
|
||||
print('')
|
||||
print('Note that the device is initialized with a default PIN and '
|
||||
'configuration.')
|
||||
print('Now you can initialize the device as usual with your chosen PIN '
|
||||
@@ -464,10 +479,15 @@ def phy(picohsm, args):
|
||||
sp = val.split(':')
|
||||
if (len(sp) != 2):
|
||||
print('ERROR: VID/PID have wrong format. Use VID:PID format (e.g. 1234:5678)')
|
||||
return
|
||||
val = int(sp[0],16).to_bytes(2, 'big') + int(sp[1],16).to_bytes(2, 'big')
|
||||
elif (args.subcommand == 'led'):
|
||||
elif (args.subcommand in ['led_gpio', 'led_brightness']):
|
||||
if (args.subcommand == 'led_brightness'):
|
||||
if (int(val) > 15 or int(val) < 0):
|
||||
print('ERROR: LED brightness must be between 0 and 15.')
|
||||
return
|
||||
val = [int(val)]
|
||||
elif (args.subcommand == 'wcid'):
|
||||
elif (args.subcommand in ['wcid', 'led_dimmable']):
|
||||
val = val == 'enable'
|
||||
ret = picohsm.phy(args.subcommand, val)
|
||||
if (ret):
|
||||
@@ -475,8 +495,21 @@ def phy(picohsm, args):
|
||||
else:
|
||||
print('Command executed successfully. Please, restart your Pico Key.')
|
||||
|
||||
def otp(picohsm, args):
|
||||
if (args.subcommand == 'read'):
|
||||
row = int(args.row, 16)
|
||||
ret = picohsm.otp(row=row)
|
||||
print(f'OTP row {args.row}: {hexlify(ret).decode()}')
|
||||
elif (args.subcommand == 'write'):
|
||||
row = int(args.row, 16)
|
||||
data = unhexlify(args.data)
|
||||
picohsm.otp(row=row, data=data)
|
||||
print(f'OTP row {args.row} written successfully.')
|
||||
elif (args.subcommand == 'secure_boot'):
|
||||
picohsm.secure_boot(BOOTKEY, bootkey_index=args.index, lock=args.lock)
|
||||
|
||||
def main(args):
|
||||
sys.stderr.buffer.write(b'Pico HSM Tool v1.16\n')
|
||||
sys.stderr.buffer.write(b'Pico HSM Tool v2.0\n')
|
||||
sys.stderr.buffer.write(b'Author: Pol Henarejos\n')
|
||||
sys.stderr.buffer.write(b'Report bugs to https://github.com/polhenarejos/pico-hsm/issues\n')
|
||||
sys.stderr.buffer.write(b'\n\n')
|
||||
@@ -503,7 +536,8 @@ def main(args):
|
||||
keygen(picohsm, args)
|
||||
elif (args.command == 'phy'):
|
||||
phy(picohsm, args)
|
||||
|
||||
elif (args.command == 'otp'):
|
||||
otp(picohsm, args)
|
||||
|
||||
def run():
|
||||
args = parse_args()
|
||||
|
||||
Reference in New Issue
Block a user