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:
@@ -19,9 +19,12 @@
|
|||||||
#include "mbedtls/aes.h"
|
#include "mbedtls/aes.h"
|
||||||
#include "mbedtls/cmac.h"
|
#include "mbedtls/cmac.h"
|
||||||
#include "mbedtls/hkdf.h"
|
#include "mbedtls/hkdf.h"
|
||||||
|
#include "mbedtls/chachapoly.h"
|
||||||
#include "crypto_utils.h"
|
#include "crypto_utils.h"
|
||||||
#include "sc_hsm.h"
|
#include "sc_hsm.h"
|
||||||
#include "kek.h"
|
#include "kek.h"
|
||||||
|
#include "asn1.h"
|
||||||
|
#include "oid.h"
|
||||||
|
|
||||||
int cmd_cipher_sym() {
|
int cmd_cipher_sym() {
|
||||||
int key_id = P1(apdu);
|
int key_id = P1(apdu);
|
||||||
@@ -33,9 +36,6 @@ int cmd_cipher_sym() {
|
|||||||
return SW_FILE_NOT_FOUND();
|
return SW_FILE_NOT_FOUND();
|
||||||
if (key_has_purpose(ef, algo) == false)
|
if (key_has_purpose(ef, algo) == false)
|
||||||
return SW_CONDITIONS_NOT_SATISFIED();
|
return SW_CONDITIONS_NOT_SATISFIED();
|
||||||
if ((apdu.nc % 16) != 0) {
|
|
||||||
return SW_WRONG_LENGTH();
|
|
||||||
}
|
|
||||||
if (wait_button_pressed() == true) // timeout
|
if (wait_button_pressed() == true) // timeout
|
||||||
return SW_SECURE_MESSAGE_EXEC_ERROR();
|
return SW_SECURE_MESSAGE_EXEC_ERROR();
|
||||||
int key_size = file_get_size(ef);
|
int key_size = file_get_size(ef);
|
||||||
@@ -45,6 +45,9 @@ int cmd_cipher_sym() {
|
|||||||
return SW_EXEC_ERROR();
|
return SW_EXEC_ERROR();
|
||||||
}
|
}
|
||||||
if (algo == ALGO_AES_CBC_ENCRYPT || algo == ALGO_AES_CBC_DECRYPT) {
|
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_context aes;
|
||||||
mbedtls_aes_init(&aes);
|
mbedtls_aes_init(&aes);
|
||||||
uint8_t tmp_iv[IV_SIZE];
|
uint8_t tmp_iv[IV_SIZE];
|
||||||
@@ -105,6 +108,43 @@ int cmd_cipher_sym() {
|
|||||||
return SW_EXEC_ERROR();
|
return SW_EXEC_ERROR();
|
||||||
res_APDU_size = apdu.nc;
|
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 {
|
else {
|
||||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||||
return SW_WRONG_P1P2();
|
return SW_WRONG_P1P2();
|
||||||
|
|||||||
@@ -103,4 +103,7 @@
|
|||||||
#define OID_CC_FF_PKA OID_CC_FORMAT "\x03"
|
#define OID_CC_FF_PKA OID_CC_FORMAT "\x03"
|
||||||
#define OID_CC_FF_KDA OID_CC_FORMAT "\x04"
|
#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
|
#endif
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ extern const uint8_t sc_hsm_aid[];
|
|||||||
#define ALGO_AES_CBC_ENCRYPT 0x10
|
#define ALGO_AES_CBC_ENCRYPT 0x10
|
||||||
#define ALGO_AES_CBC_DECRYPT 0x11
|
#define ALGO_AES_CBC_DECRYPT 0x11
|
||||||
#define ALGO_AES_CMAC 0x18
|
#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 ALGO_AES_DERIVE 0x99
|
||||||
|
|
||||||
#define HSM_OPT_RRC 0x0001
|
#define HSM_OPT_RRC 0x0001
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ except ModuleNotFoundError:
|
|||||||
import json
|
import json
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import base64
|
import base64
|
||||||
from binascii import hexlify
|
from binascii import hexlify, unhexlify
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -66,6 +66,8 @@ class APDUResponse(Exception):
|
|||||||
self.sw2 = sw2
|
self.sw2 = sw2
|
||||||
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
|
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):
|
def send_apdu(card, command, p1, p2, data=None):
|
||||||
lc = []
|
lc = []
|
||||||
@@ -89,7 +91,7 @@ def parse_args():
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
subparser = parser.add_subparsers(title="commands", dest="command")
|
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 = 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_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.')
|
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('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_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_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()
|
args = parser.parse_args()
|
||||||
return args
|
return args
|
||||||
|
|
||||||
@@ -153,6 +166,12 @@ def pki(card, args):
|
|||||||
else:
|
else:
|
||||||
print('Error: no PKI is passed. Use --default to retrieve default PKI.')
|
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):
|
def initialize(card, args):
|
||||||
print('********************************')
|
print('********************************')
|
||||||
print('* PLEASE READ IT CAREFULLY *')
|
print('* PLEASE READ IT CAREFULLY *')
|
||||||
@@ -164,14 +183,9 @@ def initialize(card, args):
|
|||||||
_ = input('[Press enter to confirm]')
|
_ = input('[Press enter to confirm]')
|
||||||
|
|
||||||
send_apdu(card, 0xA4, 0x04, 0x00, [0xE8, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xC3, 0x1F, 0x02, 0x01])
|
send_apdu(card, 0xA4, 0x04, 0x00, [0xE8, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xC3, 0x1F, 0x02, 0x01])
|
||||||
if (args.pin):
|
if (not args.pin):
|
||||||
pin = args.pin.encode()
|
pin = b'648219'
|
||||||
try:
|
|
||||||
response = send_apdu(card, 0x20, 0x00, 0x81, list(pin))
|
|
||||||
except APDUResponse:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
pin = b'648219'
|
|
||||||
if (args.so_pin):
|
if (args.so_pin):
|
||||||
so_pin = args.so_pin.encode()
|
so_pin = args.so_pin.encode()
|
||||||
try:
|
try:
|
||||||
@@ -179,7 +193,7 @@ def initialize(card, args):
|
|||||||
except APDUResponse:
|
except APDUResponse:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
so_pin = b'57621880'
|
so_pin = b'57621880'
|
||||||
|
|
||||||
pin_data = [0x81, len(pin)] + list(pin)
|
pin_data = [0x81, len(pin)] + list(pin)
|
||||||
so_pin_data = [0x82, len(so_pin)] + list(so_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):
|
if (a.sw1 == 0x6a and a.sw2 == 0x82):
|
||||||
print('ERROR: Key not found')
|
print('ERROR: Key not found')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
from binascii import hexlify
|
|
||||||
print(hexlify(bytearray(cert)))
|
print(hexlify(bytearray(cert)))
|
||||||
print(f'Details of key {kid}:\n')
|
print(f'Details of key {kid}:\n')
|
||||||
print(f' CAR: {(CVC().decode(cert).car()).decode()}')
|
print(f' CAR: {(CVC().decode(cert).car()).decode()}')
|
||||||
@@ -359,8 +373,46 @@ def secure(card, args):
|
|||||||
elif (args.subcommand == 'disable'):
|
elif (args.subcommand == 'disable'):
|
||||||
slck.disable_device_aut()
|
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):
|
def main(args):
|
||||||
print('Pico HSM Tool v1.6')
|
print('Pico HSM Tool v1.8')
|
||||||
print('Author: Pol Henarejos')
|
print('Author: Pol Henarejos')
|
||||||
print('Report bugs to https://github.com/polhenarejos/pico-hsm/issues')
|
print('Report bugs to https://github.com/polhenarejos/pico-hsm/issues')
|
||||||
print('')
|
print('')
|
||||||
@@ -377,6 +429,9 @@ def main(args):
|
|||||||
except CardRequestTimeoutException:
|
except CardRequestTimeoutException:
|
||||||
print('time-out: no card inserted during last 10s')
|
print('time-out: no card inserted during last 10s')
|
||||||
|
|
||||||
|
if (args.pin):
|
||||||
|
login(card, args)
|
||||||
|
|
||||||
# Following commands may raise APDU exception on error
|
# Following commands may raise APDU exception on error
|
||||||
if (args.command == 'initialize'):
|
if (args.command == 'initialize'):
|
||||||
initialize(card, args)
|
initialize(card, args)
|
||||||
@@ -390,6 +445,9 @@ def main(args):
|
|||||||
opts(card, args)
|
opts(card, args)
|
||||||
elif (args.command == 'secure'):
|
elif (args.command == 'secure'):
|
||||||
secure(card, args)
|
secure(card, args)
|
||||||
|
elif (args.command == 'cipher'):
|
||||||
|
cipher(card, args)
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|||||||
Reference in New Issue
Block a user