mirror of
https://github.com/polhenarejos/pico-hsm.git
synced 2026-02-07 19:18:23 +00:00
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:
parent
30301c68f1
commit
a7682d2639
4 changed files with 120 additions and 17 deletions
|
|
@ -19,9 +19,12 @@
|
|||
#include "mbedtls/aes.h"
|
||||
#include "mbedtls/cmac.h"
|
||||
#include "mbedtls/hkdf.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "sc_hsm.h"
|
||||
#include "kek.h"
|
||||
#include "asn1.h"
|
||||
#include "oid.h"
|
||||
|
||||
int cmd_cipher_sym() {
|
||||
int key_id = P1(apdu);
|
||||
|
|
@ -33,9 +36,6 @@ int cmd_cipher_sym() {
|
|||
return SW_FILE_NOT_FOUND();
|
||||
if (key_has_purpose(ef, algo) == false)
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
if ((apdu.nc % 16) != 0) {
|
||||
return SW_WRONG_LENGTH();
|
||||
}
|
||||
if (wait_button_pressed() == true) // timeout
|
||||
return SW_SECURE_MESSAGE_EXEC_ERROR();
|
||||
int key_size = file_get_size(ef);
|
||||
|
|
@ -45,6 +45,9 @@ int cmd_cipher_sym() {
|
|||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (algo == ALGO_AES_CBC_ENCRYPT || algo == ALGO_AES_CBC_DECRYPT) {
|
||||
if ((apdu.nc % 16) != 0) {
|
||||
return SW_WRONG_LENGTH();
|
||||
}
|
||||
mbedtls_aes_context aes;
|
||||
mbedtls_aes_init(&aes);
|
||||
uint8_t tmp_iv[IV_SIZE];
|
||||
|
|
@ -105,6 +108,43 @@ int cmd_cipher_sym() {
|
|||
return SW_EXEC_ERROR();
|
||||
res_APDU_size = apdu.nc;
|
||||
}
|
||||
else if (algo == ALGO_EXT_CIPHER_ENCRYPT || algo == ALGO_EXT_CIPHER_DECRYPT) {
|
||||
size_t oid_len = 0, aad_len = 0, iv_len = 0, enc_len = 0;
|
||||
uint8_t *oid = NULL, *aad = NULL, *iv = NULL, *enc = NULL;
|
||||
if (!asn1_find_tag(apdu.data, apdu.nc, 0x6, &oid_len, &oid) || oid_len == 0 || oid == NULL) {
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
asn1_find_tag(apdu.data, apdu.nc, 0x81, &enc_len, &enc);
|
||||
asn1_find_tag(apdu.data, apdu.nc, 0x82, &iv_len, &iv);
|
||||
asn1_find_tag(apdu.data, apdu.nc, 0x83, &aad_len, &aad);
|
||||
uint8_t tmp_iv[16];
|
||||
memset(tmp_iv, 0, sizeof(tmp_iv));
|
||||
if (memcmp(oid, OID_CHACHA20_POLY1305, oid_len) == 0)
|
||||
{
|
||||
if (algo == ALGO_EXT_CIPHER_DECRYPT && enc_len < 16) {
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
int r = 0;
|
||||
mbedtls_chachapoly_context ctx;
|
||||
mbedtls_chachapoly_init(&ctx);
|
||||
if (algo == ALGO_EXT_CIPHER_ENCRYPT) {
|
||||
r = mbedtls_chachapoly_encrypt_and_tag(&ctx, enc_len, iv ? iv : tmp_iv, aad, aad_len, enc, res_APDU, res_APDU + enc_len);
|
||||
}
|
||||
else if (algo == ALGO_EXT_CIPHER_DECRYPT) {
|
||||
r = mbedtls_chachapoly_auth_decrypt(&ctx, enc_len - 16, iv ? iv : tmp_iv, aad, aad_len, enc + enc_len - 16, enc, res_APDU);
|
||||
}
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
mbedtls_chachapoly_free(&ctx);
|
||||
if (r != 0)
|
||||
return SW_EXEC_ERROR();
|
||||
if (algo == ALGO_EXT_CIPHER_ENCRYPT)
|
||||
res_APDU_size = enc_len + 16;
|
||||
else if (algo == ALGO_EXT_CIPHER_DECRYPT)
|
||||
res_APDU_size = enc_len - 16;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
return SW_WRONG_P1P2();
|
||||
|
|
|
|||
|
|
@ -103,4 +103,7 @@
|
|||
#define OID_CC_FF_PKA OID_CC_FORMAT "\x03"
|
||||
#define OID_CC_FF_KDA OID_CC_FORMAT "\x04"
|
||||
|
||||
#define OID_CHACHA20_POLY1305 "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x12"
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ extern const uint8_t sc_hsm_aid[];
|
|||
#define ALGO_AES_CBC_ENCRYPT 0x10
|
||||
#define ALGO_AES_CBC_DECRYPT 0x11
|
||||
#define ALGO_AES_CMAC 0x18
|
||||
#define ALGO_EXT_CIPHER_ENCRYPT 0x51 /* Extended ciphering Encrypt */
|
||||
#define ALGO_EXT_CIPHER_DECRYPT 0x52 /* Extended ciphering Decrypt */
|
||||
#define ALGO_AES_DERIVE 0x99
|
||||
|
||||
#define HSM_OPT_RRC 0x0001
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue