Skip to content

Commit

Permalink
Added key attestation example. Updated ATECC608A example config.
Browse files Browse the repository at this point in the history
  • Loading branch information
BenUdall-Microchip committed Mar 5, 2019
1 parent a6f03a6 commit 03caf6e
Show file tree
Hide file tree
Showing 14 changed files with 610 additions and 88 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ build/
*.pyc
.dist/
dist/

# Virtualenv
venv*
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ the expected outcome from it should be.

## Pull Requests

This reposistory does not have a [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_License_Agreement).
This repository does not have a [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_License_Agreement).
Because there is no CLA currently, pull requests will be reviewed as per the
issues above and taken as bug reports or suggestions. Rather than opening
a pull request it would be better to open an issue. When a CLA has been added
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ software license.

-------------------------------------------------------------------------------

(c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
(c) 2015-2019 Microchip Technology Inc. and its subsidiaries.

Subject to your compliance with these terms, you may use Microchip software
and any derivatives exclusively with Microchip products. It is your
Expand Down
25 changes: 17 additions & 8 deletions python/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ Install the python requirements via the command:
```

## Running the examples
The examples are intended to be simple and straightforward to illustrate the basic concepts.
To get help on any example you can consult the associated document (e.g. [info.py](info.py) has an accompanying
[info.md](info.md) document) or from the command line:
The examples are intended to be simple and straightforward to illustrate the
basic concepts. To get help on any example you can consult the associated
document (e.g. [info.py](info.py) has an accompanying [info.md](info.md)
document) or from the command line:
```
info.py -h
```
Expand Down Expand Up @@ -46,15 +47,23 @@ optional arguments:

## list of Examples

- [config.py](config.py): Configure and provision a blank device for these
examples. See [config.md](config.md)
- [info.py](info.py): Read device info. See [info.md](info.md)
- [key_attestation.py](key_attestation.md): Demonstrate a key attestation flow
for proving possesion of an asymmetric key.
See [key_attestation.md](key_attestation.md)
- [ecdh.py](ecdh.py): Perform ECDH calculation. See [ecdh.md](ecdh.md)
- [sign_verify.py](sign_verify.py): Perform ECDSA signature and verification. See [sign_verify.md](sign_verify.md)
- [read_write.py](read_write.py): Perform encrypted writes and successive reads from a slot. See [read_write.md](read_write.md)
- [sign_verify.py](sign_verify.py): Perform ECDSA signature and verification.
See [sign_verify.md](sign_verify.md)
- [read_write.py](read_write.py): Perform encrypted writes and successive reads
from a slot. See [read_write.md](read_write.md)

## What does the Python CryptoAuthLib package do?
CryptoAuthLib module gives access to most functions available as part of standard cryptoauthlib
(which is written in 'C'). These python functions for the most part are very similar to 'C'
functions. The module in short acts as a wrapper over the 'C' cryptoauth library functions.
CryptoAuthLib module gives access to most functions available as part of
standard cryptoauthlib (which is written in 'C'). These python functions for
the most part are very similar to 'C' functions. The module in short acts as a
wrapper over the 'C' cryptoauth library functions.

Microchip cryptoauthlib product page:
[Link]( http://www.microchip.com/SWLibraryWeb/product.aspx?product=CryptoAuthLib)
Expand Down
15 changes: 10 additions & 5 deletions python/examples/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,24 @@ def pretty_print_hex(a, l=16, indent=''):
"""
Format a list/bytes/bytearray object into a formatted ascii hex string
"""
s = ''
lines = []
a = bytearray(a)
for x in range(0, len(a), l):
s += indent + ''.join(['%02X ' % y for y in a[x:x+l]]) + '\n'
return s
lines.append(indent + ' '.join(['{:02X}'.format(y) for y in a[x:x+l]]))
return '\n'.join(lines)


def convert_ec_pub_to_pem(raw_pub_key):
"""
Convert to the key to PEM format. Expects bytes
"""
public_key_pem = bytearray.fromhex('3059301306072A8648CE3D020106082A8648CE3D03010703420004') + raw_pub_key
public_key_pem = '-----BEGIN PUBLIC KEY-----\n' + base64.b64encode(public_key_pem).decode('ascii') + '\n-----END PUBLIC KEY-----'
public_key_der = bytearray.fromhex('3059301306072A8648CE3D020106082A8648CE3D03010703420004') + raw_pub_key
public_key_b64 = base64.b64encode(public_key_der).decode('ascii')
public_key_pem = (
'-----BEGIN PUBLIC KEY-----\n'
+ '\n'.join(public_key_b64[i:i + 64] for i in range(0, len(public_key_b64), 64)) + '\n'
+ '-----END PUBLIC KEY-----'
)
return public_key_pem


Expand Down
9 changes: 5 additions & 4 deletions python/examples/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Example Configurations
===============================================================================
Before a CryptoAuthentication device may be used it's configuration must be set
for the intended use case(s). Configurations can be quite complex and need to
be carefully created and analyized for security gaps that could be introduced
be carefully created and analyzed for security gaps that could be introduced
by an unintended configuration interaction. When developing a new configuration
please consult both the datasheet and your FAE.

Expand All @@ -16,9 +16,10 @@ ATECC508A Configuraiton
be rewritten without prior knowledge of a secret.

ATECC608A Configuration:
* All the above from the ATECC508A
* Slot 4 is additionally used as the io protection key for reading ECDH
premaster secret and KDF material from tempkey.
* Slot 6 allows unencrypted writes which means the io protection key can
be rewritten without prior knowledge of a secret.
* Slot 6 is additionally used as the io protection key for reading ECDH
premaster secret and KDF material.

ATSHA204A Configuration:
* Slots may be freely written without prior knowledge of the slot contents
Expand Down
117 changes: 73 additions & 44 deletions python/examples/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
# THIS SOFTWARE.

from cryptoauthlib import *
from cryptoauthlib.device import *
from common import *
import time
import ctypes
import base64

_atsha204_config = bytearray.fromhex(
'C8 00 55 00 8F 80 80 A1 82 E0 C4 F4 84 00 A0 85'
Expand All @@ -44,13 +47,13 @@

# Example configuration for ATECC608A minus the first 16 bytes which are fixed by the factory
_atecc608_config = bytearray.fromhex(
'B0 00 55 01 8F 20 C4 44 87 20 87 20 8F 0F C4 36'
'9F 0F 82 20 0F 0F C4 44 0F 0F 0F 0F 0F 0F 0F 0F'
'0F 0F 0F 0F FF FF FF FF 00 00 00 00 FF FF FF FF'
'00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
'00 00 00 00 00 00 55 55 FF FF 06 40 00 00 00 00'
'33 00 1C 00 13 00 13 00 7C 00 1C 00 3C 00 33 00'
'3C 00 3C 00 3C 00 30 00 3C 00 3C 00 3C 00 30 00')
'6A 00 00 01 85 00 82 00 85 20 85 20 85 20 C6 46'
'8F 0F 9F 8F 0F 0F 8F 0F 0F 0F 0F 0F 0F 0F 0F 0F'
'0D 1F 0F 0F FF FF FF FF 00 00 00 00 FF FF FF FF'
'00 00 00 00 00 00 03 F7 00 69 76 00 00 00 00 00'
'00 00 00 00 00 00 55 55 FF FF 0E 60 00 00 00 00'
'53 00 53 00 73 00 73 00 73 00 38 00 7C 00 1C 00'
'3C 00 1A 00 3C 00 30 00 3C 00 30 00 12 00 30 00')

_configs = {'ATSHA204A': _atsha204_config,
'ATECC508A': _atecc508_config,
Expand Down Expand Up @@ -120,31 +123,29 @@ def configure_device(iface='hid', device='ecc', i2c_addr=None, keygen=True, **kw
response = bytearray(4)
assert ATCA_SUCCESS == atcab_read_bytes_zone(0, 0, 16, response, 4)
print(' Current Address: {:02X}'.format(response[0]))
if 'ecc' == device and not config_zone_lock:
if i2c_addr is None:
i2c_addr = 0xB0
if 0xC0 != i2c_addr:
print('\n The AT88CK590 Kit does not support changing the I2C addresses of devices.')
print(' If you are not using an AT88CK590 kit you may continue without errors')
print(' otherwise exit and specify a compatible (0xC0) address.')
if 'Y' != input(' Continue (Y/n): '):
exit(0)
print(' New Address: {:02X}'.format(i2c_addr))

# Program the configuration zone
print('\nProgram Configuration')
if not config_zone_lock:
config = _configs.get(dev_name)
if config is not None:
print(' Programming {} Configuration'.format(dev_name))
else:
print(' Unknown Device')
raise ValueError('Unknown Device Type: {:02X}'.format(dev_type))
if config is None:
raise ValueError('Unknown Device Type: {}'.format(dev_type))

# Update with the target I2C Address
if i2c_addr is not None:
config[0] = i2c_addr

print('\n New Address: {:02X}'.format(config[0]))
ck590_i2c_addr = 0xC0 if dev_name != 'ATSHA204A' else 0xC8
if config[0] != ck590_i2c_addr:
print(' The AT88CK590 Kit does not support changing the I2C addresses of devices.')
print(' If you are not using an AT88CK590 kit you may continue without errors')
print(' otherwise exit and specify a compatible (0x{:02X}) address.'.format(ck590_i2c_addr))
if 'Y' != input(' Continue (Y/n): '):
exit(0)

print(' Programming {} Configuration'.format(dev_name))

# Write configuration
assert ATCA_SUCCESS == atcab_write_bytes_zone(0, 0, 16, config, len(config))
print(' Success')
Expand All @@ -167,38 +168,66 @@ def configure_device(iface='hid', device='ecc', i2c_addr=None, keygen=True, **kw
# Check data zone lock
print('\nActivating Configuration')
if not data_zone_lock:
# Generate initial ECC key pairs, if applicable
key_gen(dev_name)

# Lock the data zone
assert ATCA_SUCCESS == atcab_lock_data_zone()
print(' Activated')
else:
print(' Already Active')

# Generate new keys
if keygen or not data_zone_lock:
if 'ATSHA204A' == dev_name:
# print('\nProgramming Keys')
print('\nProgram SHA204 Keys manually')
else:
print('\nGenerating New Keys')
pubkey = bytearray(64)
assert ATCA_SUCCESS == atcab_genkey(0, pubkey)
print(' Key 0 Success:')
print(pretty_print_hex(pubkey, indent=' '))

assert ATCA_SUCCESS == atcab_genkey(2, pubkey)
print(' Key 2 Success:')
print(pretty_print_hex(pubkey, indent=' '))

assert ATCA_SUCCESS == atcab_genkey(3, pubkey)
print(' Key 3 Success:')
print(pretty_print_hex(pubkey, indent=' '))

assert ATCA_SUCCESS == atcab_genkey(7, pubkey)
print(' Key 7 Success:')
print(pretty_print_hex(pubkey, indent=' '))
if keygen and data_zone_lock:
print('\nGenerating New Keys')
key_gen(dev_name)

atcab_release()


def key_gen(dev_name):
"""Reviews the configuration of a device and generates new random ECC key pairs for slots that allow it."""
ATCA_SUCCESS = 0x00

if 'ECC' not in dev_name:
return # SHA device, no keys to generate

# Read the device configuration
config_data = bytearray(128)
assert ATCA_SUCCESS == atcab_read_config_zone(config_data)
if dev_name == 'ATECC508A':
config = Atecc508aConfig.from_buffer(config_data)
elif dev_name == 'ATECC608A':
config = Atecc608aConfig.from_buffer(config_data)
else:
raise ValueError('Unsupported device {}'.format(dev_name))

# Review all slot configurations and generate keys where possible
for slot in range(16):
if not config.KeyConfig[slot].Private:
continue # Not a private key
if config.LockValue != 0x55:
# Data zone is already locked, additional conditions apply
skip_msg = ' Skipping key pair generation in slot {}: '.format(slot)
if not config.SlotConfig[slot].WriteConfig & 0x02:
print(skip_msg + 'GenKey is disabled')
continue
if not config.SlotLocked & (1 << slot):
print(skip_msg + 'Slot has ben locked')
continue
if config.KeyConfig[slot].ReqAuth:
print(skip_msg + 'Slot requires authorization')
continue
if config.KeyConfig[slot].PersistentDisable:
print(skip_msg + 'Slot requires persistent latch')
continue

print(' Generating key pair in slot {}'.format(slot))
public_key = bytearray(64)
assert ATCA_SUCCESS == atcab_genkey(slot, public_key)
print(convert_ec_pub_to_pem(public_key))


if __name__ == '__main__':
parser = setup_example_runner(__file__)
parser.add_argument('--i2c', help='I2C Address (in hex)')
Expand Down
19 changes: 13 additions & 6 deletions python/examples/ecdh.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from common import *
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from cryptography.utils import int_from_bytes

import time

Expand Down Expand Up @@ -55,10 +57,11 @@ def ECDH(slot, iface='hid', **kwargs):
# Get the device type from the info command
info = bytearray(4)
assert atcab_info(info) == ATCA_SUCCESS
dev_type = get_device_type_id(get_device_name(info))
dev_name = get_device_name(info)
dev_type = get_device_type_id(dev_name)

# Check device type
if dev_type in [0, 0x20]:
if dev_type in ['ATSHA204A', 'ATECC108A']:
raise ValueError('Device does not support ECDH operations')
elif dev_type != cfg.devtype:
cfg.dev_type = dev_type
Expand All @@ -70,11 +73,11 @@ def ECDH(slot, iface='hid', **kwargs):
host_key = ec.generate_private_key(ec.SECP256R1(), default_backend())

# Convert host's public key into ATECCx08 format
host_pub = host_key.public_key().public_numbers().encode_point()[1:]
host_pub = host_key.public_key().public_bytes(encoding=Encoding.X962, format=PublicFormat.UncompressedPoint)[1:]

# Display the host's public key
print("\nHost Public Key:")
print(pretty_print_hex(host_pub, indent=' '))
print(convert_ec_pub_to_pem(host_pub))

# Buffers for device public key and shared secret
device_pub = bytearray(64)
Expand All @@ -92,10 +95,14 @@ def ECDH(slot, iface='hid', **kwargs):

# Display the device's public key
print("\nDevice public key:")
print(pretty_print_hex(device_pub, indent=' '))
print(convert_ec_pub_to_pem(device_pub))

# Convert device public key to a cryptography public key object
device_pub = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256R1(), b'\04' + device_pub).public_key(default_backend())
device_pub = ec.EllipticCurvePublicNumbers(
curve=ec.SECP256R1(),
x=int_from_bytes(device_pub[0:32], byteorder='big'),
y=int_from_bytes(device_pub[32:64], byteorder='big'),
).public_key(default_backend())

# Perform the host side ECDH computation
host_shared = host_key.exchange(ec.ECDH(), device_pub)
Expand Down
9 changes: 2 additions & 7 deletions python/examples/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,12 @@ def info(iface='hid', device='ecc', **kwargs):
data_zone_locked = bool(is_locked.value)
print(' Data Zone is %s' % ('locked' if data_zone_locked else 'unlocked'))

#Load the public key
# Load the public key
if data_zone_locked:
print('\nLoading Public key\n')
public_key = bytearray(64)
assert atcab_get_pubkey(0, public_key) == ATCA_SUCCESS

public_key = bytearray.fromhex('3059301306072A8648CE3D020106082A8648CE3D03010703420004') + bytes(public_key)
public_key = base64.b64encode(public_key).decode('ascii')
public_key = ''.join(public_key[i:i+64] + '\n' for i in range(0,len(public_key),64))
public_key = '-----BEGIN PUBLIC KEY-----\n' + public_key + '-----END PUBLIC KEY-----'
print(public_key)
print(convert_ec_pub_to_pem(public_key))

# Free the library
atcab_release()
Expand Down
Loading

0 comments on commit 03caf6e

Please sign in to comment.