Use a Trusted Platform Module (TPM) with Elixir and Nerves.
A TPM can be used to secure the cryptographic keys used for things like SSL/TLS connections and disk encryption.
This library is mainly a wrapper around existing TPM libraries, which are linked in the module docs of this repo. Documentation on how a TPM works can be found there.
This package can be installed by adding tpm
to your list of dependencies
in mix.exs
:
def deps do
[
{:tpm, "~> 0.1.0"}
]
end
Add the following to nerves_defconfig
:
BR2_PACKAGE_TPM2_TOOLS=y
BR2_PACKAGE_TPM2_TOOLS_FAPI=y
BR2_PACKAGE_TPM2_TSS_ENGINE=y
Generate a private key. Unlike a traditional private key file, this one requires the TPM to function.
:ok = TPM.TSS.genkey("/data/.ssh/key")
A copy of the key can be stored in the TPM's non-volatile memory as a backup. For example, if the device were factory reset and the disk wiped, the key could be retrieved from the TPM's NV memory and its prior identity would be retained.
{:ok, address} = TPM.nvdefine(size: 1024)
:ok = TPM.nvwrite(address, "/data/.ssh/key")
The private key can be used within the BEAM VM by getting an engine_key_ref.
{:ok, privkey} = TPM.Crypto.privkey("/data/.ssh/key")
Some connections require the client to provide a signed certificate, or else the connection will be rejected.
Generate a public key from the private key reference.
public_key_pem =
privkey
|> TPM.Crypto.pubkey
|> X509.PublicKey.to_pem
File.write!("/data/.ssh/key.pub", public_key_pem)
Create the certificate signing request.
csr_pem =
privkey
|> TPM.Crypto.csr("Device ID", "My Organization")
|> X509.CSR.to_pem
File.write!("/data/.ssh/csr.pem", csr_pem)
Copy the CSR from the device with cat
on the device or scp
on the host and
sign the CSR with the root or intermediate CA. Copy the signed certificate back
to the device. For some connections, concatenating the entire certificate bundle
into a file may be necessary.
By default, the TPM Command Transmission Interface (TCTI) is set to
device:/dev/tpmrm0
since this is the default path for a hardware TPM.
This can be changed by setting the :tcti
application property in the
project's mix config.
config :tpm, :tcti, "device:/dev/tpmrm0"