Added first step to Enterprise Attestation.

Once enabled, it allows to generate a CSR in the device, which is sent to our PKI. If valid, it returns a signed certificate by an intermediate CA that will be used for attestation.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2022-11-30 23:41:05 +01:00
parent a355f87f82
commit bae8450a8d
3 changed files with 81 additions and 1 deletions

View File

@@ -26,6 +26,7 @@
#include "mbedtls/ecdh.h" #include "mbedtls/ecdh.h"
#include "mbedtls/chachapoly.h" #include "mbedtls/chachapoly.h"
#include "mbedtls/hkdf.h" #include "mbedtls/hkdf.h"
#include "mbedtls/x509_csr.h"
extern uint8_t keydev_dec[32]; extern uint8_t keydev_dec[32];
extern bool has_keydev_dec; extern bool has_keydev_dec;
@@ -226,6 +227,45 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
has_keydev_dec = true; has_keydev_dec = true;
goto err; goto err;
} }
else if (cmd == CTAP_VENDOR_EA) {
if (vendorCmd == 0x01) {
uint8_t buffer[1024];
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev));
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
ret = mbedtls_ecp_mul(&ekey.grp, &ekey.Q, &ekey.d, &ekey.grp.G, random_gen, NULL);
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
pico_unique_board_id_t rpiid;
pico_get_unique_board_id(&rpiid);
mbedtls_x509write_csr ctx;
mbedtls_x509write_csr_init(&ctx);
snprintf((char *)buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %llu", ((uint64_t)rpiid.id[0] << 56) | ((uint64_t)rpiid.id[1] << 48) | ((uint64_t)rpiid.id[2] << 40) | ((uint64_t)rpiid.id[3] << 32) | (rpiid.id[4] << 24) | (rpiid.id[5] << 16) | (rpiid.id[6] << 8) | rpiid.id[7]);
mbedtls_x509write_csr_set_subject_name(&ctx, (char *)buffer);
mbedtls_pk_context key;
mbedtls_pk_init(&key);
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
key.pk_ctx = &ekey;
mbedtls_x509write_csr_set_key(&ctx, &key);
mbedtls_x509write_csr_set_md_alg(&ctx, MBEDTLS_MD_SHA256);
mbedtls_x509write_csr_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xE5\x1C\x01\x01\x04", 0xB, 0, aaguid, sizeof(aaguid));
ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_gen, NULL);
mbedtls_ecdsa_free(&ekey);
if (ret <= 0) {
mbedtls_x509write_csr_free(&ctx);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
}
}
else else
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION); CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder)); CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));

View File

@@ -124,6 +124,7 @@ typedef struct {
#define CTAP_VENDOR_BACKUP 0x01 #define CTAP_VENDOR_BACKUP 0x01
#define CTAP_VENDOR_MSE 0x02 #define CTAP_VENDOR_MSE 0x02
#define CTAP_VENDOR_UNLOCK 0x03 #define CTAP_VENDOR_UNLOCK 0x03
#define CTAP_VENDOR_EA 0x04
#define CTAP_PERMISSION_MC 0x01 // MakeCredential #define CTAP_PERMISSION_MC 0x01 // MakeCredential
#define CTAP_PERMISSION_GA 0x02 // GetAssertion #define CTAP_PERMISSION_GA 0x02 // GetAssertion

View File

@@ -27,6 +27,8 @@ from words import words
from threading import Event from threading import Event
from typing import Mapping, Any, Optional, Callable from typing import Mapping, Any, Optional, Callable
import struct import struct
import urllib.request
import json
from enum import IntEnum, unique from enum import IntEnum, unique
try: try:
@@ -48,6 +50,7 @@ try:
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography import x509
except: except:
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`') print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
sys.exit(-1) sys.exit(-1)
@@ -63,6 +66,21 @@ else:
print('ERROR: platform not supported') print('ERROR: platform not supported')
sys.exit(-1) sys.exit(-1)
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.picokeys.com/pico/pico-fido/{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
class VendorConfig(Config): class VendorConfig(Config):
class PARAM(IntEnum): class PARAM(IntEnum):
@@ -179,6 +197,7 @@ class Vendor:
VENDOR_BACKUP = 0x01 VENDOR_BACKUP = 0x01
VENDOR_MSE = 0x02 VENDOR_MSE = 0x02
VENDOR_UNLOCK = 0x03 VENDOR_UNLOCK = 0x03
VENDOR_CSR = 0x04
@unique @unique
class PARAM(IntEnum): class PARAM(IntEnum):
@@ -189,6 +208,7 @@ class Vendor:
ENABLE = 0x01 ENABLE = 0x01
DISABLE = 0x02 DISABLE = 0x02
KEY_AGREEMENT = 0x01 KEY_AGREEMENT = 0x01
CSR = 0x01
class RESP(IntEnum): class RESP(IntEnum):
PARAM = 0x01 PARAM = 0x01
@@ -342,6 +362,12 @@ class Vendor:
def disable_device_aut(self): def disable_device_aut(self):
self.vcfg.disable_device_aut() self.vcfg.disable_device_aut()
def csr(self):
return self._call(
Vendor.CMD.VENDOR_CSR,
Vendor.SUBCMD.CSR,
)
def parse_args(): 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")
@@ -352,6 +378,10 @@ def parse_args():
parser_backup.add_argument('subcommand', choices=['save', 'load'], help='Saves or loads a backup.') parser_backup.add_argument('subcommand', choices=['save', 'load'], help='Saves or loads a backup.')
parser_backup.add_argument('filename', help='File to save or load the backup.') parser_backup.add_argument('filename', help='File to save or load the backup.')
parser_attestation = subparser.add_parser('attestation', help='Manages Enterprise Attestation')
parser_attestation.add_argument('subcommand', choices=['csr', 'upload'])
parser_attestation.add_argument('--filename', help='Uploads the certificate filename to the device as enterprise attestation certificate. If not provided, it will generate an enterprise attestation certificate automatically.')
args = parser.parse_args() args = parser.parse_args()
return args return args
@@ -369,9 +399,16 @@ def backup(vdr, args):
elif (args.subcommand == 'load'): elif (args.subcommand == 'load'):
vdr.backup_load(args.filename) vdr.backup_load(args.filename)
def attestation(vdr, args):
if (args.subcommand == 'csr'):
csr = x509.load_der_x509_csr(vdr.csr()[1])
data = urllib.parse.urlencode({'csr': csr.public_bytes(Encoding.PEM)}).encode()
j = get_pki_data('csr', data=data)
cert = x509.load_pem_x509_certificate(j['x509'].encode())
print(cert)
def main(args): def main(args):
print('Pico Fido Tool v1.2') print('Pico Fido Tool v1.4')
print('Author: Pol Henarejos') print('Author: Pol Henarejos')
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues') print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
print('') print('')
@@ -385,6 +422,8 @@ def main(args):
secure(vdr, args) secure(vdr, args)
elif (args.command == 'backup'): elif (args.command == 'backup'):
backup(vdr, args) backup(vdr, args)
elif (args.command == 'attestation'):
attestation(vdr, args)
def run(): def run():
args = parse_args() args = parse_args()