Renaming the tools and moving to tools/ folder.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
143
tools/pico-hsm-attestation.py
Normal file
143
tools/pico-hsm-attestation.py
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
from smartcard.CardType import AnyCardType
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import CardRequestTimeoutException
|
||||
from cvc.certificates import CVC, ASN1
|
||||
from cvc.oid import oid2scheme
|
||||
from cvc.utils import scheme_rsa
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from binascii import hexlify
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class APDUResponse(Exception):
|
||||
def __init__(self, sw1, sw2):
|
||||
self.sw1 = sw1
|
||||
self.sw2 = sw2
|
||||
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
|
||||
|
||||
|
||||
def send_apdu(card, command, p1, p2, data):
|
||||
lc = [0x00] + list(len(data).to_bytes(2, 'big'))
|
||||
le = [0x00, 0x00]
|
||||
if (isinstance(command, list) and len(command) > 1):
|
||||
apdu = command
|
||||
else:
|
||||
apdu = [0x00, command]
|
||||
|
||||
apdu = apdu + [p1, p2] + lc + data + le
|
||||
response, sw1, sw2 = card.connection.transmit(apdu)
|
||||
if (sw1 != 0x90):
|
||||
raise APDUResponse(sw1, sw2)
|
||||
return bytearray(response)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Prints the certificate of attestated key in a Pico HSM')
|
||||
parser.add_argument('--version', help='Displays the current version', action='store_true')
|
||||
parser.add_argument('kid',help='Certificate to print', metavar='KEY_ID')
|
||||
if ('--version' in sys.argv):
|
||||
print('Pico HSM Attestation tool')
|
||||
print('Author: Pol Henarejos')
|
||||
print(f'Version 1.0')
|
||||
print('')
|
||||
print('Report bugs to http://github.com/polhenarejos/pico-hsm/issues')
|
||||
print('')
|
||||
sys.exit(0)
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
def main(args):
|
||||
cardtype = AnyCardType()
|
||||
try:
|
||||
# request card insertion
|
||||
cardrequest = CardRequest(timeout=10, cardType=cardtype)
|
||||
card = cardrequest.waitforcard()
|
||||
|
||||
# connect to the card and perform a few transmits
|
||||
card.connection.connect()
|
||||
|
||||
except CardRequestTimeoutException:
|
||||
print('time-out: no card inserted during last 10s')
|
||||
|
||||
try:
|
||||
response = send_apdu(card, 0xB1, 0x2F, 0x02, [0x54, 0x02, 0x00, 0x00])
|
||||
except APDUResponse as a:
|
||||
print('ERROR: There is an error with the device certificate.')
|
||||
sys.exit(1)
|
||||
|
||||
devcert = ASN1().decode(response).find(0x7f21, pos=0).data(return_tag=True)
|
||||
dica = ASN1().decode(response).find(0x7f21, pos=1).data(return_tag=True)
|
||||
|
||||
try:
|
||||
cert = send_apdu(card, 0xB1, 0xCE, int(args.kid), [0x54, 0x02, 0x00, 0x00])
|
||||
except APDUResponse as a:
|
||||
if (a.sw1 == 0x6a and a.sw2 == 0x82):
|
||||
print('ERROR: Key not found')
|
||||
sys.exit(1)
|
||||
|
||||
print(f'Details of key {args.kid}:\n')
|
||||
print(f' CAR: {(CVC().decode(cert).car()).decode()}')
|
||||
print(' Public Key:')
|
||||
puboid = CVC().decode(cert).pubkey().oid()
|
||||
print(f' Scheme: {oid2scheme(puboid)}')
|
||||
chr = CVC().decode(cert).chr()
|
||||
car = CVC().decode(cert).car()
|
||||
if (scheme_rsa(puboid)):
|
||||
print(f' Modulus: {hexlify(CVC().decode(cert).pubkey().find(0x81).data()).decode()}')
|
||||
print(f' Exponent: {hexlify(CVC().decode(cert).pubkey().find(0x82).data()).decode()}')
|
||||
else:
|
||||
print(f' Public Point: {hexlify(CVC().decode(cert).pubkey().find(0x86).data()).decode()}')
|
||||
print(f' CHR: {chr.decode()}')
|
||||
print(' Key signature:')
|
||||
inret = CVC().decode(cert).verify()
|
||||
if (inret):
|
||||
print(' Status: VALID')
|
||||
print(f' This certificate is signed with private key {args.kid}')
|
||||
else:
|
||||
print(' Status: NOT VALID')
|
||||
print(f' This certificate is NOT signed with private key {args.kid}')
|
||||
print(' Cert signature:')
|
||||
print(f' Outer CAR: {CVC().decode(cert).outer_car().decode()}')
|
||||
outret = CVC().decode(cert).verify(outer=True, dica=devcert, curve=ec.SECP256R1())
|
||||
if (outret):
|
||||
print(' Status: VALID')
|
||||
print(' This certificate is signed with the device key')
|
||||
else:
|
||||
print(' Status: NOT VALID')
|
||||
print(' This certificate is NOT signed with the device key')
|
||||
|
||||
if (inret is True and outret is True):
|
||||
print(f'Key {args.kid} is generated by device {chr.decode()}')
|
||||
else:
|
||||
print(f'Key {args.kid} is NOT generated by device {chr.decode()}')
|
||||
|
||||
def run():
|
||||
args = parse_args()
|
||||
main(args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
94
tools/pico-hsm-patch-vidpid.sh
Executable file
94
tools/pico-hsm-patch-vidpid.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/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="2" #Version of Pico CCID Core
|
||||
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}"
|
||||
129
tools/pico-hsm-tool.py
Normal file
129
tools/pico-hsm-tool.py
Normal file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
"""
|
||||
|
||||
from smartcard.CardType import AnyCardType
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import CardRequestTimeoutException
|
||||
from cvc.certificates import CVC
|
||||
from cvc.asn1 import ASN1
|
||||
import json
|
||||
import urllib.request
|
||||
import base64
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
class APDUResponse(Exception):
|
||||
def __init__(self, sw1, sw2):
|
||||
self.sw1 = sw1
|
||||
self.sw2 = sw2
|
||||
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
|
||||
|
||||
|
||||
def send_apdu(card, command, p1, p2, data):
|
||||
lc = [0x00] + list(len(data).to_bytes(2, 'big'))
|
||||
le = [0x00, 0x00]
|
||||
if (isinstance(command, list) and len(command) > 1):
|
||||
apdu = command
|
||||
else:
|
||||
apdu = [0x00, command]
|
||||
|
||||
apdu = apdu + [p1, p2] + lc + data + le
|
||||
response, sw1, sw2 = card.connection.transmit(apdu)
|
||||
if (sw1 != 0x90):
|
||||
raise APDUResponse(sw1, sw2)
|
||||
return response
|
||||
|
||||
|
||||
def main():
|
||||
print('Pico HSM burning certificates tool v1.0')
|
||||
print('Author: Pol Henarejos')
|
||||
print('Report bugs to https://github.com/polhenarejos/pico-hsm/')
|
||||
print('')
|
||||
print('')
|
||||
print('********************************')
|
||||
print('* PLEASE READ IT CAREFULLY *')
|
||||
print('********************************')
|
||||
print('')
|
||||
print('This tool will erase and reset your device. It will delete all '
|
||||
'private and secret keys.')
|
||||
print('Are you sure?')
|
||||
_ = input('[Press enter to confirm]')
|
||||
cardtype = AnyCardType()
|
||||
try:
|
||||
# request card insertion
|
||||
cardrequest = CardRequest(timeout=10, cardType=cardtype)
|
||||
card = cardrequest.waitforcard()
|
||||
|
||||
# connect to the card and perform a few transmits
|
||||
card.connection.connect()
|
||||
|
||||
reset_data = [0x80, 0x02, 0x00, 0x01, 0x81, 0x06, 0x36, 0x34, 0x38,
|
||||
0x32, 0x31,
|
||||
0x39, 0x82, 0x08, 0x35, 0x37, 0x36, 0x32, 0x31, 0x38,
|
||||
0x38, 0x30, 0x91, 0x01, 0x03]
|
||||
response = send_apdu(card, [0x80, 0x50], 0x00, 0x00, reset_data)
|
||||
|
||||
response = send_apdu(card, 0xB1, 0xCE, 0x00, [0x54, 0x02, 0x00, 0x00])
|
||||
|
||||
cert = bytearray(response)
|
||||
Y = CVC().decode(cert).pubkey().find(0x86).data()
|
||||
print(f'Public Point: {hexlify(Y).decode()}')
|
||||
|
||||
user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; '
|
||||
'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7'
|
||||
pbk = base64.urlsafe_b64encode(Y)
|
||||
data = urllib.parse.urlencode({'pubkey': pbk}).encode()
|
||||
req = urllib.request.Request("https://www.henarejos.me/pico-hsm/cvc/",
|
||||
method='POST',
|
||||
data=data,
|
||||
headers={'User-Agent': user_agent, })
|
||||
response = urllib.request.urlopen(req)
|
||||
resp = response.read().decode('utf-8')
|
||||
j = json.loads(resp)
|
||||
print('Device name: '+j['devname'])
|
||||
dataef = base64.urlsafe_b64decode(
|
||||
j['cvcert']) + base64.urlsafe_b64decode(j['dvcert'])
|
||||
|
||||
response = send_apdu(card, 0xa4, 0x00, 0x00, [0x2f, 0x02])
|
||||
pin = b'648219'
|
||||
response = send_apdu(card, 0x20, 0x00, 0x81, list(pin))
|
||||
|
||||
apdu_data = [0x54, 0x02, 0x00, 0x00] + \
|
||||
list(ASN1.make_tag(0x53, dataef))
|
||||
response = send_apdu(card, 0xd7, 0x00, 0x00, apdu_data)
|
||||
|
||||
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 you chosen PIN '
|
||||
'and configuration options.')
|
||||
|
||||
except CardRequestTimeoutException:
|
||||
print('time-out: no card inserted during last 10s')
|
||||
|
||||
|
||||
def run():
|
||||
main()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
Reference in New Issue
Block a user