91 lines
3.7 KiB
Markdown
91 lines
3.7 KiB
Markdown
# ECDSA signature format
|
|
|
|
When the ECDSA SECP256R1 (EC256) signature support was added to MCUboot, a
|
|
shortcut was taken, and these signatures were padded to make them
|
|
always a fixed length. Unfortunately, this padding was done in a way
|
|
that is not easily reversible. Some crypto libraries (specifically, Mbed
|
|
TLS) are fairly strict about the formatting of the ECDSA signature.
|
|
|
|
There are two ways to fix this:
|
|
|
|
- Use a reversible padding scheme. This solution requires
|
|
at least one pad byte to always be added (to set the length). This
|
|
padding would be somewhat incompatible across versions (older
|
|
EC256 would work, while newer MCUboot code would reject old
|
|
signatures. The EC code would work reliably only in the new
|
|
combination).
|
|
|
|
- Remove the padding entirely. Depending on the tool used, this solution
|
|
requires some rethinking of how TLV generation is implemented so
|
|
that the length does not need to be known until the signature is
|
|
generated. These tools are usually written in higher-level
|
|
languages, so this change should not be difficult.
|
|
|
|
However, this will also break compatibility with older versions,
|
|
because images generated with newer tools will not
|
|
work with older versions of MCUboot.
|
|
|
|
This document proposes a multi-stage approach to give a transition
|
|
period:
|
|
|
|
1. Add a `--no-pad-sig` argument to the sign command in
|
|
`imgtool.py`.
|
|
|
|
Without this argument, the images are padded with the
|
|
existing scheme. With this argument, the ECDSA is encoded
|
|
without any padding. The `--pad-sig` argument is also
|
|
accepted, but it is already the default.
|
|
|
|
2. MCUboot will be modified to allow unpadded signatures right away.
|
|
The existing EC256 implementations will still work (with or
|
|
without padding), and the existing EC implementation will be able
|
|
to accept padded and unpadded signatures.
|
|
|
|
3. An Mbed TLS implementation of EC256 can be added, but it will require
|
|
the `--no-pad-sig` signature to be able to boot all generated
|
|
images. Without the argument, 3 out of 4 images generated will have
|
|
padding and will be considered invalid.
|
|
|
|
After one or more MCUboot release cycles and announcements in the
|
|
relevant channels, the arguments to `imgtool.py` will change:
|
|
|
|
- `--no-pad-sig` will still be accepted but will have no effect.
|
|
|
|
- `--pad-sig` will now bring back the old padding behavior.
|
|
|
|
This will require an update to any scripts that will rely on the default
|
|
behavior, but will not specify a specific version of imgtool.
|
|
|
|
The signature generation in the simulator can be changed at the same
|
|
time the boot code begins to accept unpadded signatures. The simulator is
|
|
always run out of the same tree as the MCUboot code, so there should
|
|
not be any compatibility issues.
|
|
|
|
## Background
|
|
|
|
ECDSA signatures are encoded as ASN.1, notably with the signature
|
|
itself encoded as follows:
|
|
|
|
```
|
|
ECDSA-Sig-Value ::= SEQUENCE {
|
|
r INTEGER,
|
|
s INTEGER
|
|
}
|
|
```
|
|
|
|
Both `r` and `s` are 256-bit numbers. Because these are
|
|
unsigned numbers that are being encoded in ASN.1 as signed values, if
|
|
the high bit of the number is set, the DER-encoded representation will
|
|
require 33 bytes instead of 32. This means that the length of the
|
|
signature will vary by a couple of bytes, depending on whether one or
|
|
both of these numbers have the high bit set.
|
|
|
|
Originally, MCUboot added padding to the entire signature and just
|
|
removed any trailing 0 bytes from the data block. This turned out to be fine 255 out of 256
|
|
times, each time the last byte of the signature was non-zero, but if the
|
|
signature ended in a zero, MCUboot would remove too many bytes and render the
|
|
signature invalid.
|
|
|
|
The correct approach here is to accept that ECDSA signatures are of
|
|
variable length, and to make sure that we can handle them as such.
|