-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add cryptographically signed update support #5213
Conversation
Using a pluggable architecture, allow updates delivered via the Update class to be verified as signed by a certificate. By using plugins, avoid pulling either axTLS or BearSSL into normal builds. A signature is appended to a binary image, followed by the size of the signature as a 32-bit int. The updater takes a verification function and checks this signature using whatever method it chooses, and if it fails the update is not applied. A SHA256 hash class is presently implemented for the signing hash (since MD5 is a busted algorithm). A BearSSLPublicKey based verifier is implemented for RSA keys. The application only needs the Public Key, while to sign you can use OpenSSL and your private key (which should never leave your control or be deployed on any endpoints).
Signing example using /tmp/sk as the signing private key. Mostly for my benefit
The corresponding public key to the private one used should also, of course, be in the source code. |
5f62397
to
ddfa01b
Compare
Allow EC or RSA keys to be used to verify, as well as any RSA key length supported by BearSSL (currently 4096 bits).
ddfa01b
to
882e546
Compare
Please don't merge this yet, I have several comments. |
Sure, @devyte . Apologies in advance for the virtual functions and base classes. Given we've got stuck with a single, globally allocated Potential things I think still need checking for are 4-byte length alignment, but I think that's actually guaranteed by the binary format. OTW it's only a readme away from general usability, methinks. Tested three different signed uploads, all worked (and one unsigned and one signed but doctored binary failed with SIGN_ERROR as expected). |
So glad you have picked this up! You are correct about importing all of AxTLS, but sounds like BearSSL could be a winner. I'll be following along with interest (I wish I had more bandwidth to help more) |
Signed updates provide a better guarantee than unsigned MD5 checking, and the signature may change the MD5 value anyway. Because of this, don't even bother doing MD5 work when a signed update is expected.
There's been some discussion on how to expose this to users. Right now what's implemented is "manually sign," but each has positives and negatives. I'm inclined to go KISS on the implementation more than the user interface on this because only technically knowledgeable users should really be looking at signed exes. Lose your private key, you can't update without physical presence. Your key gets out in the wild (accidentally committing to a public repo, posting it, etc.), it's all open. And this isn't encryption, so it's not like it is protecting your code, either. Thoughts?
|
IMHO, KISSest would be 3/ and python. |
I'm also inclined towards 3. In order for it work, the user still has to have some knowledge. |
I'm pretty sure every Linux system will have Python installed, @d-a-v. No idea if it's on the Mac by default. But, if it might not be installed, I can't use Python, because it will crash all builds if it's not installed. So it's a bash script, then, I think. That's ugly, but I guess doable. Next problem is how to handle normal case when no key is present or the bash script fails (or is never run like under Windows). I basically need to overwrite a Overwriting a core include file on each build is a very ugly hack that I would really not want to do. I'm not even sure if I could do it safely, because you need to reset it to the default case at the start of each compile process. |
Mac's have python by default. 10.13 has Python 2.7. |
Great, that makes it a little easier. I also forgot the post-bingen step, actually signing the executable. Doing that in Python would be easier and saner than the bash I'm using now. Unfortunately I'm still stuck on how to conditionally change the compiled code. Any ideas that don't include overwriting include files? If I can run bash scripts in the build, I suppose it would be possilbe (but ugly) to have 2 copies of the compile command line prefixed with something like |
Does edit |
(3) is implemented. The latest push can now sign the executable automatically in the build process assuming that openssl is installed and there is a "public.key" and "private.key" file in the sketch directory. The manual method of installing a key and signing from the command line is also available. I'm not too happy about having to include BearSSL calls inside |
Looks like Platform.IO doesn't use platform.txt, so have to hack that as well to build the signing header. Using the existing Python script instead of bash looks like it'll turn out useful as I won't have to duplicate any weird bash in the pio build setup. |
defa89f
to
37ac8a7
Compare
37ac8a7
to
0ae91ae
Compare
@earlephilhower, thank you for you efforts. Greatly appreciated. |
27015a6
to
b3b7477
Compare
A couple error strings weren't in PMEM. Move them.
9158626
to
31b22fb
Compare
Users normally won't be signing their apps. In this case a message like "Not signing binaries" would make them worry over nothing. Silence it. Users who do care about signing will still get the Signing binaries and other output.
Saves ~600 bytes when in debug mode.
Windows can't run the signing script, nor does it normally have OpenSSL installed. When trying to build an automatically signed binary, warn and don't run the python.
@earlephilhower I'd like to ask for more details about PlatformIO and Windows 10. |
@Misiu , your best bet to get this followed up on is to open a new issue or else it's going to be lost on a closed PR. We need python and native openssl executables. #5635 adds a native, self-contained Python tool which is one part. Win32 OpenSSL executables are still needed and very rare among Arduino users (OpenSSL exes are part of most Linux (and I think Mac) installations by default). |
@earlephilhower OpenSSL isn't a problem, it is easily installable on Windows. |
It's more than just getting the OpenSSL binaries. Python needs to be there and the platform.txt needs to be updated to run under Windows. But please do open up an issue so it can be tracked after the other changes land. |
@earlephilhower I've already did - #5694. |
--edit 11/05-- Remove WIP
Using a pluggable architecture, allow updates delivered via the Update
class to be verified as signed by a certificate. By using plugins, avoid
pulling either axTLS or BearSSL into normal builds.
A signature is appended to a binary image, followed by the size of the
signature as a 32-bit int. The updater takes a verification function
and checks this signature using whatever method it chooses, and if it
fails the update is not applied.
A SHA256 hash class is presently implemented for the signing hash (since
MD5 is a busted algorithm).
A BearSSLPublicKey based verifier is implemented for RSA keys. The
application only needs the Public Key, while to sign you can use
OpenSSL and your private key (which should never leave your control
or be deployed on any endpoints).
This code has been tested by manually signing a Blink.ino.bin file using the BearSSL server example pub/private keypair, but it needs to have a better detailed example and be generalized for different RSA and EC keys.
Inspired by @madpilot's PR #3105 which, unfortunately seems to be abandoned and for some reason had to pull in the entire axTLS source tree and new binary. Maybe the functions weren't exported in axTLS. They are exported in BearSSL, so the changes are minimal here.