Integrate all commands to a single script

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2022-08-24 13:53:11 +02:00
parent e399b1c0b1
commit 9ef088971b
2 changed files with 163 additions and 196 deletions

View File

@@ -1,143 +0,0 @@
#!/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()

View File

@@ -24,11 +24,16 @@ from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import CardRequestTimeoutException from smartcard.Exceptions import CardRequestTimeoutException
from cvc.certificates import CVC from cvc.certificates import CVC
from cvc.asn1 import ASN1 from cvc.asn1 import ASN1
from cvc.oid import oid2scheme
from cvc.utils import scheme_rsa
from cryptography.hazmat.primitives.asymmetric import ec
import json import json
import urllib.request import urllib.request
import base64 import base64
from binascii import hexlify from binascii import hexlify
import sys
import argparse
import os
class APDUResponse(Exception): class APDUResponse(Exception):
def __init__(self, sw1, sw2): def __init__(self, sw1, sw2):
@@ -51,21 +56,161 @@ def send_apdu(card, command, p1, p2, data):
raise APDUResponse(sw1, sw2) raise APDUResponse(sw1, sw2)
return response return response
def parse_args():
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(title="commands", dest="command")
_ = subparser.add_parser('initialize', help='Performs the first initialization of the Pico HSM.')
def main(): 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.')
print('Pico HSM burning certificates tool v1.0') parser_attestate.add_argument('-k', '--key', help='The private key index', metavar='KEY_ID', required=True)
print('Author: Pol Henarejos')
print('Report bugs to https://github.com/polhenarejos/pico-hsm/') parser_pki = subparser.add_parser('pki', help='Performs PKI operations.')
print('') subparser_pki = parser_pki.add_subparsers(title='commands', dest='subcommand')
print('') parser_pki_init = subparser_pki.add_parser('initialize', help='Initializes the Public Key Infrastructure (PKI)')
parser_pki_init.add_argument('--certs-dir', help='Store the PKI certificates into this directory.', default='certs')
parser_pki_init.add_argument('--default', help='Setups the default public PKI from public Pico HSM PKI.', action='store_true')
parser_pki_init.add_argument('--force', help='Forces the download of certificates.', action='store_true')
args = parser.parse_args()
return args
def get_pki_data(url, data=None, method='GET'):
user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; '
'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7'
method = 'GET'
if (data is not None):
method = 'POST'
req = urllib.request.Request(f"https://www.henarejos.me/pico-hsm/{url}/",
method=method,
data=data,
headers={'User-Agent': user_agent, })
response = urllib.request.urlopen(req)
resp = response.read().decode('utf-8')
j = json.loads(resp)
return j
def get_pki_certs(certs_dir='certs', force=False):
certs = get_pki_data('certs')
if (os.path.exists(certs_dir) is False):
os.mkdir(certs_dir)
cvcap = os.path.join(certs_dir, certs['cvca']['CHR'])
dvcap = os.path.join(certs_dir, certs['dvca']['CHR'])
if (os.path.exists(cvcap) is False or force is True):
with open(cvcap, 'wb') as f:
f.write(certs['cvca']['cert'].encode())
if (os.path.exists(dvcap) is False or force is True):
with open(dvcap, 'wb') as f:
f.write(certs['dvca']['cert'].encode())
def pki(card, args):
if (args.subcommand == 'initialize'):
if (args.default is True):
get_pki_certs(certs_dir=args.certs_dir, force=args.force)
def initialize(card):
print('********************************') print('********************************')
print('* PLEASE READ IT CAREFULLY *') print('* PLEASE READ IT CAREFULLY *')
print('********************************') print('********************************')
print('') print('')
print('This tool will erase and reset your device. It will delete all ' print('This tool will erase and reset your device. It will delete all '
'private and secret keys.') 'private and secret keys.')
print('Are you sure?') print('Are you sure?')
_ = input('[Press enter to confirm]') _ = input('[Press enter to confirm]')
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()}')
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'])
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.')
def attestate(card, args):
kid = int(args.key)
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, 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 {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 {kid}')
else:
print(' Status: NOT VALID')
print(f' This certificate is NOT signed with private key {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 {kid} is generated by device {chr.decode()}')
else:
print(f'Key {kid} is NOT generated by device {chr.decode()}')
def main(args):
print('Pico HSM burning certificates tool v1.1')
print('Author: Pol Henarejos')
print('Report bugs to https://github.com/polhenarejos/pico-hsm/')
print('')
print('')
cardtype = AnyCardType() cardtype = AnyCardType()
try: try:
# request card insertion # request card insertion
@@ -75,55 +220,20 @@ def main():
# connect to the card and perform a few transmits # connect to the card and perform a few transmits
card.connection.connect() 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: except CardRequestTimeoutException:
print('time-out: no card inserted during last 10s') print('time-out: no card inserted during last 10s')
# Following commands may raise APDU exception on error
if (args.command == 'initialize'):
initialize(card)
elif (args.command == 'attestate'):
attestate(card, args)
elif (args.command == 'pki'):
pki(card, args)
def run(): def run():
main() args = parse_args()
main(args)
if __name__ == "__main__": if __name__ == "__main__":
run() run()