demo/secure_element/resources/generation_scripts/create_device_cert.py

1 #
2 # EVALUATION AND USE OF THIS SOFTWARE IS SUBJECT TO THE TERMS AND
3 # CONDITIONS OF THE CONTROLLING LICENSE AGREEMENT FOUND AT LICENSE.md
4 # IN THIS SDK. IF YOU DO NOT AGREE TO THE LICENSE TERMS AND CONDITIONS,
5 # PLEASE RETURN ALL SOURCE FILES TO SILICON LABORATORIES.
6 # (c) Copyright 2018, Silicon Laboratories Inc. All rights reserved.
7 #
8 
9 from __future__ import print_function
10 import datetime
11 import traceback
12 import optparse
13 
14 from secure_element_common import *
15 from secure_element_device import secure_element_device
16 from cert2certdef import gen_cert_def_c_device
17 from pyasn1.compat.integer import from_bytes
18 
19 
20 ''' ******************************************************************************************* '''
21 def create_device_cert():
22  print('\nOpening connection to Device')
23  with secure_element_device() as device:
24  _create_device_cert(device)
25 
26 
27 ''' ******************************************************************************************* '''
28 def _create_device_cert(device):
29  print('\nInitializing Device')
30  device_info = device.init()
31  print(' MAC Address: %s' % device_info.mac)
32  print(' ATECC608A SN: %s' % device_info.se.serial_number)
33  print(' ATECC608A Public Key:')
34 
35 
36  public_key = binascii.hexlify(device_info.se.public_key)
37  int_size = len(public_key) / 2
38  print(' X: %s' % public_key[:int_size])
39  print(' Y: %s' % public_key[int_size:])
40 
41 
42  print('\nLoading signer CA key')
43  if not os.path.isfile(CREDENTIAL_PATH(SIGNER_CA_KEY_FILENAME)):
44  raise Exception('Failed to find signer CA key file, ' + SIGNER_CA_KEY_FILENAME + '. Have you run the script: ca_create_signer.py first?')
45 
46  with open(CREDENTIAL_PATH(SIGNER_CA_KEY_FILENAME), 'rb') as f:
47  print(' Loading from ' + f.name)
48  signer_ca_priv_key = serialization.load_pem_private_key(
49  data=f.read(),
50  password=None,
51  backend=crypto_be)
52 
53  print('\nLoading signer CA certificate')
54  if not os.path.isfile(CREDENTIAL_PATH(SIGNER_CA_CERT_FILENAME)):
55  raise Exception('Failed to find signer CA certificate file, ' + SIGNER_CA_CERT_FILENAME + '. Have you run the script: ca_create_signer.py first?')
56 
57  with open(CREDENTIAL_PATH(SIGNER_CA_CERT_FILENAME), 'rb') as f:
58  print(' Loading from ' + f.name)
59  signer_ca_cert = x509.load_pem_x509_certificate(f.read(), crypto_be)
60 
61 
62  print('\nRequesting device CSR')
63  resp = device.gen_csr()
64  device_csr = x509.load_der_x509_csr(str(resp.csr), crypto_be)
65  if not device_csr.is_signature_valid:
66  raise Exception('Device CSR has invalid signature.')
67 
68  with open(CREDENTIAL_PATH(DEVICE_CSR_FILENAME), 'wb') as f:
69  print(' Saving to ' + f.name)
70  f.write(device_csr.public_bytes(encoding=serialization.Encoding.PEM))
71 
72  print('\nGenerating device certificate from CSR')
73  # Build certificate
74  builder = x509.CertificateBuilder()
75  builder = builder.issuer_name(signer_ca_cert.subject)
76  builder = builder.not_valid_before(datetime.datetime.now(tz=pytz.utc).replace(minute=0,second=0)) # Device cert must have minutes and seconds set to 0
77  builder = builder.not_valid_after(datetime.datetime(3000, 12, 31, 23, 59, 59)) # Should be year 9999, but this doesn't work on windows
78  builder = builder.subject_name(device_csr.subject)
79  builder = builder.public_key(device_csr.public_key())
80  # Device certificate is generated from certificate dates and public key
81  builder = builder.serial_number(device_cert_sn(16, builder))
82  # Add in extensions specified by CSR
83  for extension in device_csr.extensions:
84  builder = builder.add_extension(extension.value, extension.critical)
85  # Subject Key ID is used as the thing name and MQTT client ID and is required for this demo
86  builder = builder.add_extension(
87  x509.SubjectKeyIdentifier.from_public_key(builder._public_key),
88  critical=False)
89  issuer_ski = signer_ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
90  builder = builder.add_extension(
91  x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(issuer_ski),
92  critical=False)
93 
94  # Sign certificate
95  device_cert = builder.sign(
96  private_key=signer_ca_priv_key,
97  algorithm=hashes.SHA256(),
98  backend=crypto_be)
99 
100  # Save certificate for reference
101  with open(CREDENTIAL_PATH(DEVICE_CERT_FILENAME), 'wb') as f:
102  print(' Saving to ' + f.name)
103  f.write(device_cert.public_bytes(encoding=serialization.Encoding.PEM))
104 
105  print('Generating ' + DEVICE_CERT_C_FILENAME)
106  with open(SOURCE_PATH(DEVICE_CERT_C_FILENAME), 'wb') as f:
107  cert_def_2_device = gen_cert_def_c_device(CREDENTIAL_PATH(DEVICE_CERT_FILENAME))
108  f.write(cert_def_2_device)
109 
110  print('\nDone')
111 
112 
113 
114 
115 ''' *******************************************************************************************
116  The cert_def_2_device.c has:
117 
118  .sn_source = SNSRC_PUB_KEY_HASH,
119 
120  In atcacert_def.h, SNSRC_PUB_KEY_HASH = Cert serial number is the SHA256(Subject public key + Encoded dates)
121 '''
122 def device_cert_sn(size, builder):
123  pubkey = pub_key_to_bytes(builder._public_key)
124 
125  # Get the encoded dates
126  expire_years = 0
127  enc_dates = bytearray(b'\x00'*3)
128  enc_dates[0] = (enc_dates[0] & 0x07) | ((((builder._not_valid_before.year - 2000) & 0x1F) << 3) & 0xFF)
129  enc_dates[0] = (enc_dates[0] & 0xF8) | ((((builder._not_valid_before.month) & 0x0F) >> 1) & 0xFF)
130  enc_dates[1] = (enc_dates[1] & 0x7F) | ((((builder._not_valid_before.month) & 0x0F) << 7) & 0xFF)
131  enc_dates[1] = (enc_dates[1] & 0x83) | (((builder._not_valid_before.day & 0x1F) << 2) & 0xFF)
132  enc_dates[1] = (enc_dates[1] & 0xFC) | (((builder._not_valid_before.hour & 0x1F) >> 3) & 0xFF)
133  enc_dates[2] = (enc_dates[2] & 0x1F) | (((builder._not_valid_before.hour & 0x1F) << 5) & 0xFF)
134  enc_dates[2] = (enc_dates[2] & 0xE0) | ((expire_years & 0x1F) & 0xFF)
135  enc_dates = bytes(enc_dates)
136 
137  # SAH256 hash of the public key and encoded dates
138  digest = hashes.Hash(hashes.SHA256(), backend=crypto_be)
139  digest.update(str(pubkey))
140  digest.update(enc_dates)
141  raw_sn = bytearray(digest.finalize()[:size])
142  raw_sn[0] = raw_sn[0] & 0x7F # Force MSB bit to 0 to ensure positive integer
143  raw_sn[0] = raw_sn[0] | 0x40 # Force next bit to 1 to ensure the integer won't be trimmed in ASN.1 DER encoding
144 
145  return from_bytes(raw_sn, signed=False)
146 
147 
148 
149 
150 
151 
152 ''' ******************************************************************************************* '''
153 if __name__ == '__main__':
154  parser = optparse.OptionParser(description='Uses secure element to generate device certificate and provisions device. NOTE: The device must be connected and programmed with the app running to use this script')
155  options, _ = parser.parse_args()
156 
157  try:
158  create_device_cert()
159  except Exception as e:
160  traceback.print_exc()
161  print(e)
162 
163