#!/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 . */ """ 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()