Powered by AppSignal & Oban Pro

Provision Volatile Key Configuration

provision-volatile-key-config.livemd

Provision Volatile Key Configuration

Mix.install([:nerves_key, :atecc508a])

Setup

{:ok, i2c} = ATECC508A.Transport.I2C.init([])

Set up your keys

Warning!

Remember that provisioning an ATECC608 chip is permanent. If it goes wrong or you use the wrong key it cannot be fixed, changed or restored.

Particularly for the volatile configuration, losing the activation key will mean you cannot use the chip for much of anything.

manufacturer_sn_field = Kino.Input.text("Your serial number", default: "LAB00001")
board_name_field = Kino.Input.text("Board name", default: "PowerBoard5000")
cert_name_field = Kino.Input.text("Certificate name", default: "nerveskey-test-signer1")

activation_field = Kino.Input.text("Activation key", default: "correcthorsenail")
encryption_field = Kino.Input.text("Encryption key", default: "nailhorsecorrect")
accepted_field = Kino.Input.checkbox("I understand the consequences")
fields = [manufacturer_sn_field, board_name_field, cert_name_field, activation_field, encryption_field, accepted_field]
Kino.Layout.grid(fields, columns: 3) |> Kino.render()

# These keys are considered test keys and we share the private key intentionally
cert_field = Kino.Input.textarea("Signer certificate", default: """
-----BEGIN CERTIFICATE-----
MIIBpzCCAU2gAwIBAgIQc3p50MJrn1hkMJ20b6c6cjAKBggqhkjOPQQDAjARMQ8w
DQYDVQQDDAZTaWduZXIwHhcNMjQwNDE5MTEwMDAwWhcNMjUwNDE5MTEwMDAwWjAR
MQ8wDQYDVQQDDAZTaWduZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARyJw8a
eyEXB4jxcsTCgyh8vCa5IV7IaOkGIWunkoTmFh16UkAdAJG3dpQRhiooBxl5+ADe
vcMFkPeIPScLGZx3o4GGMIGDMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/
BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU
32moIyTaiQOONSkBK3EUTXXn9kEwHwYDVR0jBBgwFoAU32moIyTaiQOONSkBK3EU
TXXn9kEwCgYIKoZIzj0EAwIDSAAwRQIgCQ/QP5EJrabRoPs9F6iJR3bEzl20gqGB
dCVsdeA9dKUCIQDftgLAbaQujGuq3riTcb0T8VsA1ayweAh9PsRZveVSiQ==
-----END CERTIFICATE-----
""")

key_field = Kino.Input.textarea("Signer key", default: """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEID9UV6zO4sVnA+yeCL7SF36dcU841YPmXZfRQ6htENkyoAoGCCqGSM49
AwEHoUQDQgAEcicPGnshFweI8XLEwoMofLwmuSFeyGjpBiFrp5KE5hYdelJAHQCR
t3aUEYYqKAcZefgA3r3DBZD3iD0nCxmcdw==
-----END EC PRIVATE KEY-----
""")

pk_fields = [cert_field, key_field]

Kino.Layout.grid(pk_fields, columns: 2)
activation_key = Kino.Input.read(activation_field)
encryption_key = Kino.Input.read(encryption_field)
accepted? = Kino.Input.read(accepted_field)

activation_key_size = byte_size(activation_key)
# Condition will produce an error for 0 length or >16 byte
activation_key =
  cond do
    activation_key_size == 16 -> activation_key
    activation_key_size < 16 and activation_key_size > 0 ->
      pad = 16 - activation_key_size
      IO.puts("Short key, padding #{pad} bytes")
      <<activation_key::binary, 0::size(pad * 8)>>
  end

encryption_key_size = byte_size(encryption_key)
# Condition will produce an error for 0 length or >16 byte
encryption_key =
  cond do
    encryption_key_size == 16 -> encryption_key
    encryption_key_size < 16 and encryption_key_size > 0 ->
      pad = 16 - encryption_key_size
      IO.puts("Short key, padding #{pad} bytes")
      <<encryption_key::binary, 0::size(pad * 8)>>
  end

# Validate some things
signer_cert =
  cert_field
  |> Kino.Input.read()
  |> X509.Certificate.from_pem!()

signer_key =
  key_field
  |> Kino.Input.read()
  |> X509.PrivateKey.from_pem!()

manufacturer_sn = Kino.Input.read(manufacturer_sn_field)
board_name = Kino.Input.read(board_name_field)
provision_info =
  %NervesKey.ProvisioningInfo{
    manufacturer_sn: manufacturer_sn,
    board_name: board_name
  }
IO.inspect(activation_key)
if accepted? do
  IO.inspect(NervesKey.provisioned?(i2c), label: "provisioned")
  if not NervesKey.provisioned?(i2c) do
    NervesKey.volatile_provision(i2c, provision_info, signer_cert, signer_key, activation_key, encryption_key)
  else
    IO.puts("Already provisioned.")
  end
else
  IO.puts("You have not confirmed that you understand the consequences :)")
end