David Brown | 8f057ca | 2019-12-12 16:19:55 -0700 | [diff] [blame^] | 1 | # ECDSA signature format |
| 2 | |
| 3 | When ECDSA SECP256R1 (EC256) signature support was added to MCUboot, a |
| 4 | shortcut was taken, and these signatures were padded to make them |
| 5 | always a fixed length. Unfortunately, this padding was done in a way |
| 6 | that is not easily reversible. Some crypto libraries are fairly |
| 7 | strict about the formatting of the ECDSA signature (specifically, mbed |
| 8 | TLS). This currently means that the ECDSA SECP224R1 (EC) signature |
| 9 | checking code will fail to boot about 1 out of every 256 images, |
| 10 | because the signature itself will end in a 0x00 byte, and the code |
| 11 | will remove too much data, invalidating the signature. |
| 12 | |
| 13 | There are a couple of ways to fix this: |
| 14 | |
| 15 | 1. Use a reversible padding scheme. This will work, but requires |
| 16 | at least one pad byte always be added (to set the length). This |
| 17 | padding would be somewhat incompatible across versions (older |
| 18 | EC256 would work, newer mcuboot code would reject old |
| 19 | signatures. EC code would only reliably work in the new |
| 20 | combination). |
| 21 | |
| 22 | 2. Remove the padding entirely. Depending on which tool, this will |
| 23 | require some rethinking of how TLV generation is implemented so |
| 24 | that the length does not need to be known until the signature is |
| 25 | generated. These tools are all written in higher-level |
| 26 | languages and this change should not be difficult. |
| 27 | |
| 28 | However, this will also break compatibility with older version, |
| 29 | significantly in that images generated with newer tools will not |
| 30 | work with older versions of MCUboot. |
| 31 | |
| 32 | This document proposes a multi-stage approach, to give a transition |
| 33 | period. |
| 34 | |
| 35 | - First, add a `--valid-ecdsa` argument to the sign command in |
| 36 | `imgtool.py`. Without this, the images will be padded with the |
| 37 | existing scheme, and with the argument, the ecdsa will be encoded |
| 38 | without any padding. |
| 39 | |
| 40 | - MCUboot will be modified to allow unpadded signatures right away. |
| 41 | The existing EC256 implementations will still work (with or |
| 42 | without padding), and the existing EC implementation will begin |
| 43 | accepting all signatures. |
| 44 | |
| 45 | - An mbed TLS implementation of EC256 can be added, but will require |
| 46 | the `--valid-ecdsa` signature to be able to boot all generated |
| 47 | images (without the argument 3 of out 4 images generated will have |
| 48 | padding, and be considered invalid). |
| 49 | |
| 50 | After one or more MCUboot release cycles, and announcements over |
| 51 | relevant channels, the arguments to mcuboot will change: |
| 52 | |
| 53 | - `--valid-ecdsa` will still be accepted, but have no effect. |
| 54 | |
| 55 | - `--invalid-ecdsa` will now bring back the old padding behavior. |
| 56 | |
| 57 | This will require a change to any scripts that are relying on this |
| 58 | behavior, but not specifying a specific version of imgtool. |
| 59 | |
| 60 | The signature generation in the simulator can be changed at the same |
| 61 | time the boot code begins to accept unpadded signatures. The sim is |
| 62 | always run out of the same tree as the mcuboot code, so there should |
| 63 | not be any compatibility issues. |
| 64 | |
| 65 | ## Background |
| 66 | |
| 67 | ECDSA signatures are encoded as ASN.1, notably with the signature |
| 68 | itself being encoded as: |
| 69 | |
| 70 | ECDSA-Sig-Value ::= SEQUENCE { |
| 71 | r INTEGER, |
| 72 | s INTEGER |
| 73 | } |
| 74 | |
| 75 | where both `r` and `s` are 256-bit numbers. Because these are |
| 76 | unsigned numbers that are being encoded in ASN.1 as signed values, if |
| 77 | the high bit of the number is set, the DER encoded representation will |
| 78 | require 33 bytes instead of 32. This means that the length of the |
| 79 | signature will vary by a couple of bytes, depending on whether on of |
| 80 | both of these numbers has the high bit set. |
| 81 | |
| 82 | Originally, MCUboot added padding to the entire signature, and just |
| 83 | removed 0 bytes from the data block. This would be fine 255/256 |
| 84 | times, when the last byte of the signature was non-zero, but if the |
| 85 | signature ended in a zero, it would remove too many bytes, and the |
| 86 | signature would be considered invalid. |
| 87 | |
| 88 | The correct approach here is to accept that ECDSA signatures are |
| 89 | variable length, and make sure that we can handle them as such. |