Signing Apps with ECDSA Signatures
Tock applications can be signed with a cryptographic signature to verify the authenticity of the application. This guide describes how to use ECDSA with the P256R1 curve to sign Tock applications.
Generating an ECDSA Key Pair
The first step is creating a public-private key pair for the signature. We can
do that with openssl
:
$ openssl ecparam -name secp256r1 -genkey -noout -out ec-secp256r1-priv.pem
$ openssl ec -in ec-secp256r1-priv.pem -pubout -out ec-secp256r1-pub.pem
Once created, we can view the key:
$ openssl ec -in ec-secp256r1-priv.pem -text -noout
Note the "pub" key is encoded in sec1
format.
Signing a Tock App
There are two ways to sign a Tock app with the private key: (1) adding the
signature when the .tbf
is created with elf2tab
or (2) adding the signature
with tockloader.
Approach 1: Using elf2tab
First, we need to convert the private key from sec1
format to the pk8
format
that elf2tab
expects. We can do this with openssl
:
$ openssl pkcs8 -in ec-secp256r1-priv.pem -topk8 -nocrypt -outform der > ec-secp256r1-priv.p8
Now we can pass that key to elf2tab
as a command line argument. This works
best if you are running elf2tab
directly or if you have a custom build setup
for Tock applications.
$ elf2tab ... --ecdsa-nist-p256-private ec-secp256r1-priv.p8 ...
If you are using libtock-c
, you can have an app compiled and the signature
includes by modifying the app's Makefile. Add the following line to the
Makefile:
ELF2TAB_ARGS += --ecdsa-nist-p256-private ec-secp256r1-priv.p8
Approach 2: Using tockloader
If you have an existing Tock app compiled into the .tab
format, you can add
the ECDSA signature to the .tbf
files within the existing .tab
. To add the
signature, run the following command in the directory with your existing app:
$ tockloader tbf credential add ecdsap256 --private-key ec-secp256r1-priv.pem
Verifying the Signature in the Kernel
To instruct the kernel to verify signatures when loading apps, we need to use the signature verifier capsule with the ECDSA-P256 verifier.
First, we setup the types at the top of main.rs:
#![allow(unused)] fn main() { type Verifier = ecdsa_sw::p256_verifier::EcdsaP256SignatureVerifier<'static>; type SignatureVerifyInMemoryKeys = components::signature_verify_in_memory_keys::SignatureVerifyInMemoryKeysComponentType< Verifier, 1, 64, 32, 64, >; }
Next we have to setup the public key that we will use to verify the signature.
We need to convert the public key to a byte-array that we can include in the Rust source code.
An easy way to do that is to convert the original keypair to pk8
format (if
you didn't do this already in Approach 1 above):
$ openssl pkcs8 -in ec-secp256r1-priv-key.pem -topk8 -nocrypt -outform der > ec-secp256r1-priv-key.p8
The last 64 bytes of that file are the public key.
We can extract and format as-needed with:
$ tail -c 64 ec-secp256r1-priv-key.p8 | hexdump -v -e '1/1 "0x%02x "'
Create a buffer in main.rs with those bytes:
#![allow(unused)] fn main() { let verifying_key = kernel::static_init!( [u8; 64], [ // insert bytes here ] ); }
Then we can setup the remaining verification infrastructure:
#![allow(unused)] fn main() { let verifying_keys = kernel::static_init!([&'static mut [u8; 64]; 1], [verifying_key]); let ecdsa_p256_verifier = kernel::static_init!( ecdsa_sw::p256_verifier::EcdsaP256SignatureVerifier<'static>, ecdsa_sw::p256_verifier::EcdsaP256SignatureVerifier::new() ); ecdsa_p256_verifier.register(); let verifier_multiple_keys = components::signature_verify_in_memory_keys::SignatureVerifyInMemoryKeysComponent::new( ecdsa_p256_verifier, verifying_keys, ) .finalize( components::signature_verify_in_memory_keys_component_static!(Verifier, 1, 64, 32, 64,), ); let checking_policy = components::appid::checker_signature::AppCheckerSignatureComponent::new( sha, verifier_multiple_keys, tock_tbf::types::TbfFooterV2CredentialsType::EcdsaNistP256, ) .finalize(components::app_checker_signature_component_static!( SignatureVerifyInMemoryKeys, capsules_extra::sha256::Sha256Software<'static>, 32, 64, )); }
The checking_policy
can then be used when loading processes.