Adding Extended Cipher feature.

With this new subcommand, Pico HSM will support newer cipher algorithms.
ChaCha20-Poly1305 is the first. It will be based on a custom P2 subcommand to support an arbitrary structure with multiple parameters (AAD, IV, etc.)

pico-hsm-tool.py shall be used.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2022-11-07 21:37:11 +01:00
parent 30301c68f1
commit a7682d2639
4 changed files with 120 additions and 17 deletions

View File

@@ -51,7 +51,7 @@ except ModuleNotFoundError:
import json
import urllib.request
import base64
from binascii import hexlify
from binascii import hexlify, unhexlify
import sys
import argparse
import os
@@ -66,6 +66,8 @@ class APDUResponse(Exception):
self.sw2 = sw2
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
def hexy(a):
return [hex(i) for i in a]
def send_apdu(card, command, p1, p2, data=None):
lc = []
@@ -89,7 +91,7 @@ def parse_args():
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(title="commands", dest="command")
parser_init = subparser.add_parser('initialize', help='Performs the first initialization of the Pico HSM.')
parser_init.add_argument('--pin', help='PIN number')
parser.add_argument('--pin', help='PIN number')
parser_init.add_argument('--so-pin', help='SO-PIN number')
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.')
@@ -111,9 +113,20 @@ def parse_args():
parser_opts.add_argument('opt', choices=['button', 'counter'], help='Button: press-to-confirm button.\nCounter: every generated key has an internal counter.')
parser_opts.add_argument('onoff', choices=['on', 'off'], help='Toggles state ON or OFF', metavar='ON/OFF', nargs='?')
parser_secure = subparser.add_parser('secure', help='Manages security of Pico Fido.')
parser_secure = subparser.add_parser('secure', help='Manages security of Pico HSM.')
parser_secure.add_argument('subcommand', choices=['enable', 'disable', 'unlock'], help='Enables, disables or unlocks the security.')
parser_cipher = subparser.add_parser('cipher', help='Implements extended symmetric ciphering with new algorithms and options.\n\tIf no file input/output is specified, stdin/stoud will be used.')
parser_cipher.add_argument('subcommand', choices=['encrypt','decrypt','e','d','keygen'], help='Encrypts, decrypts or generates a new key.')
parser_cipher.add_argument('--alg', choices=['CHACHAPOLY'], help='Selects the algorithm.', required='keygen' not in sys.argv)
parser_cipher.add_argument('--iv', help='Sets the IV/nonce (hex string).')
parser_cipher.add_argument('--file-in', help='File to encrypt or decrypt.')
parser_cipher.add_argument('--file-out', help='File to write the result.')
parser_cipher.add_argument('--aad', help='Specifies the authentication data (it can be a string or hex string. Combine with --hex if necesary).')
parser_cipher.add_argument('--hex', help='Parses the AAD parameter as a hex string (for binary data).', action='store_true')
parser_cipher.add_argument('-k', '--key', help='The private key index', metavar='KEY_ID', required=True)
parser_cipher.add_argument('-s', '--key-size', default=32, help='Size of the key in bytes.')
args = parser.parse_args()
return args
@@ -153,6 +166,12 @@ def pki(card, args):
else:
print('Error: no PKI is passed. Use --default to retrieve default PKI.')
def login(card, args):
try:
response = send_apdu(card, 0x20, 0x00, 0x81, list(args.pin.encode()))
except APDUResponse:
pass
def initialize(card, args):
print('********************************')
print('* PLEASE READ IT CAREFULLY *')
@@ -164,14 +183,9 @@ def initialize(card, args):
_ = input('[Press enter to confirm]')
send_apdu(card, 0xA4, 0x04, 0x00, [0xE8, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xC3, 0x1F, 0x02, 0x01])
if (args.pin):
pin = args.pin.encode()
try:
response = send_apdu(card, 0x20, 0x00, 0x81, list(pin))
except APDUResponse:
pass
else:
pin = b'648219'
if (not args.pin):
pin = b'648219'
if (args.so_pin):
so_pin = args.so_pin.encode()
try:
@@ -179,7 +193,7 @@ def initialize(card, args):
except APDUResponse:
pass
else:
so_pin = b'57621880'
so_pin = b'57621880'
pin_data = [0x81, len(pin)] + list(pin)
so_pin_data = [0x82, len(so_pin)] + list(so_pin)
@@ -229,7 +243,7 @@ def attestate(card, args):
if (a.sw1 == 0x6a and a.sw2 == 0x82):
print('ERROR: Key not found')
sys.exit(1)
from binascii import hexlify
print(hexlify(bytearray(cert)))
print(f'Details of key {kid}:\n')
print(f' CAR: {(CVC().decode(cert).car()).decode()}')
@@ -359,8 +373,46 @@ def secure(card, args):
elif (args.subcommand == 'disable'):
slck.disable_device_aut()
def cipher(card, args):
if (args.subcommand == 'keygen'):
ksize = 0xB2
if (args.key_size == 24):
ksize = 0xB1
elif (args.key_size == 16):
ksize = 0xB0
ret = send_apdu(card, 0x48, int(args.key), ksize)
else:
if (args.alg == 'CHACHAPOLY'):
oid = b'\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x12'
if (args.subcommand[0] == 'e'):
alg = 0x51
elif (args.subcommand[0] == 'd'):
alg = 0x52
if (args.file_in):
fin = open(args.file_in, 'rb')
else:
fin = sys.stdin.buffer
enc = fin.read()
fin.close()
data = [0x06, len(oid)] + list(oid) + [0x81, len(enc)] + list(enc)
if (args.iv):
data += [0x82, len(args.iv)/2] + list(unhexlify(args.iv))
if (args.aad):
if (args.hex):
data += [0x83, len(args.aad)/2] + list(unhexlify(args.aad))
else:
data += [0x83, len(args.aad)] + list(args.aad)
ret = send_apdu(card, [0x80, 0x78], int(args.key), alg, data)
sys.stdout.buffer.write(bytes(ret))
def main(args):
print('Pico HSM Tool v1.6')
print('Pico HSM Tool v1.8')
print('Author: Pol Henarejos')
print('Report bugs to https://github.com/polhenarejos/pico-hsm/issues')
print('')
@@ -377,6 +429,9 @@ def main(args):
except CardRequestTimeoutException:
print('time-out: no card inserted during last 10s')
if (args.pin):
login(card, args)
# Following commands may raise APDU exception on error
if (args.command == 'initialize'):
initialize(card, args)
@@ -390,6 +445,9 @@ def main(args):
opts(card, args)
elif (args.command == 'secure'):
secure(card, args)
elif (args.command == 'cipher'):
cipher(card, args)
def run():
args = parse_args()