Elixir support for the NervesKey
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.



CircleCI Hex version

The NervesKey is a configured ATECC508A Crypto Authentication chip that's used for authenticating devices with NervesHub and other cloud services. At a high level, it is simple HSM that protects one private key by requiring all operations on that key to occur inside chip. The project provides access to the chip from Elixir and makes configuration decisions to make working with the device easier. It has the following features:

  1. Provision blank ATECC508A devices - this includes private key generation
  2. Storage for serial number and one-time calibration data (useful if primary storage is on a removable MicroSD card)
  3. Support for Microchip's compressed X.509 certificate format for interop with C libraries
  4. Support for signing device certificates so that devices can be included in a PKI
  5. Support for storing a small amount of run-time configuration in unused data slots

It cannot be stressed enough that if you are provisioning ATECC508A or ATECC608A chips with this library that you keep in mind that the chips are essentially one-time programmable. Mistakes are corrected by replacing the chip or the entire NervesKey.

See the hw folder for hardware design files.


If available in Hex, the package can be installed by adding nerves_key to your list of dependencies in mix.exs:

def deps do
    {:nerves_key, "~> 0.1.0"}

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nerves_key.

Preparing for provisioning

The ATECC508A in the NervesKey needs to be provisioned before it can be used. Before you can do that, you'll need a signing certificate and some information about your device.

Signing certificates

Part of the provisioning process creates an X.509 certificate for the NervesKey that can be used to authenticate TLS connections. This certificate is signed by a "signer certificate". This is called a certificate authority in other contexts. You will eventually need to upload the signer certificate to NervesHub or AWS IoT or wherever you would like to authenticate devices.

Due to memory limitations, the ATECC508A has a way to compress X.509 certificates on chip. See ATECC Compressed Certificate Definition. To comply with the limitations of compressible certificates, NervesKey provides a mix task:

$ mix nerves_key.signer create nerveskey_prod_signer1
Created signing cert, nerveskey_prod_signer1.cert and private key, nerveskey_prod_signer1.key.

Please store nerveskey_prod_signer1.key in a safe place.

nerveskey_prod_signer1.cert is ready to be uploaded to the servers that need
to authenticate devices signed by the private key.

Check with your IoT service on how the signer certificate is used. If it's only used for first time device registration, then the signer certificate may not need a long expiration time. You may also be interested in creating more than one signing certificate if you have more than one manufacturing facility.

Manufacturer serial numbers

Be aware that there are a lot of things called serial numbers. In an attempt to minimize confusion, we'll refer to the serial number that identifies the device to humans and other machines as the "manufacturer serial number". This string (it need not be a number) is commonly printed on a label on a device. It may be embedded in a barcode. Other serial numbers exist - the ATECC508A has a 9 byte one and X.509 certificates have ones. Those serial numbers have guarantees on uniqueness. It is up to the device manufacturer to make sure that the "manufacturer serial number" is unique. People generally want to do this for their own sanity.

The NervesKey saves the manufacturing serial number in the one-time programmable memory on the ATECC508A and also in the devices X.509 certificate. The device's X.509 certificate is signed, so cloud servers can trust the manufacturer serial number.

At this point, you're the manufacturer. Decide how you'd like your serial numbers to look. Whatever you pick, it must fit in a 16-bytes when represented in ASCII (UTF-8 might work).


Now that you have a signing certificate, the signer's private key, and a manufacturer serial number, you can provision a NervesKey or the ATECC508A acting as a NervesKey in your device. Usually there's some custom manufucturing support software that performs this step. We'll provision at the iex prompt.

Use sftp to push the signer certificate and private key to your device. We'll put them /tmp so that they disappear on reboot:

$ sftp nerves.local
Connected to nerves.local.
sftp> cd /tmp
sftp> put nerveskey_prod_signer1.*
Uploading nerveskey_prod_signer1.cert to /tmp/nerveskey_prod_signer1.cert
nerveskey_prod_signer1.cert                                              100%  636    78.3KB/s   00:00
Uploading nerveskey_prod_signer1.key to /tmp/nerveskey_prod_signer1.key
nerveskey_prod_signer1.key                                               100%  228    78.3KB/s   00:00
sftp> exit

Next, go to the IEx prompt on the device and run the following:

# Customize these
manufacturer_sn = "NK-1234"
board_name = "NervesKey"

# These lines should be copy/paste
signer_cert = File.read!("/tmp/#{cert_name}.cert") |> X509.Certificate.from_pem!;true
signer_key = File.read!("/tmp/#{cert_name}.key") |> X509.PrivateKey.from_pem!();true

{:ok, i2c} = ATECC508A.Transport.I2C.init([])
provision_info = %NervesKey.ProvisioningInfo{manufacturer_sn: manufacturer_sn, board_name: board_name}

# Double-check what you typed above before running this
NervesKey.provision(i2c, provision_info, signer_cert, signer_key)

If the last line returns :ok after about 2 seconds, then celebrate. You successfully programmed a NervesKey. You can't program it again. If you try, you'll get an error.


If you run into problems, please help us improve this project by filing an issue.