seal: ECDSA signature r/s shorter than 32 bytes is written wrong (der_to_raw copies 32-len bytes), ~1 in 256 seals do not verify

Aperta Adatta ai principianti
#365 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
72/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
c

Direzione di ricerca

Il bug si trova in der_to_raw() in bintool/mbedtls_wrapper.c, nei rami INTEGER corti vicino alle righe 169 e 179, dove la lunghezza della copia è 32 - b invece di b. Leggi quei rami e la gestione della lunghezza DER sopra di essi, poi verifica se la PR #358 riscrive già questa funzione prima di modificarla. È completato quando r e s sono allineati a destra con i byte corretti per gli interi DER corti e una firma con r o s accorciato da uno zero iniziale viene verificata rispetto all'hash dell'immagine.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

picotool seal --sign intermittently writes a SIGNATURE block item that does not verify. It happens whenever the DER encoding of ECDSA's r or s is shorter than 32 bytes, i.e. the 32-byte value has a leading zero byte and the next byte is below 0x80. For a random 256-bit value that is 1/512; with two values per signature about 1 seal in 256 is affected. The ECDSA nonce is random, so re-running the same seal command on the same input gives a different signature and the failure looks random.

The HASH_VALUE item is correct in every bad image; only the signature is wrong. An image sealed this way does not pass signature verification, so it will not boot on an RP2350 with secure boot enabled.

Version: picotool 2.3.0 (tag 2.3.0, 6f6458d). The same code is on master and develop today (bintool/mbedtls_wrapper.c lines 169 and 179 at ba3df40).

Repro
  1. Any RP2350 ELF and a secp256k1 private key in PEM form.
  2. Seal it repeatedly; each run draws a fresh nonce:
    for i in $(seq 2000); do
      picotool seal --quiet in.elf out_$i.elf key.pem --sign --hash
    done
    
  3. Verify each signature independently, e.g. with Python cryptography: take the image's SHA-256 (the HASH_VALUE item, which is correct) and the 64-byte r || s from the SIGNATURE item, and call public_key.verify(encode_dss_signature(r, s), digest, ec.ECDSA(Prehashed(SHA256()))).
Observed

Windows builds (GCC 16.1, mbedtls from pico-sdk 2.3.0 lib/mbedtls, 0bebf8b), same input ELF and key throughout:

build seals fail verification
2.3.0, built from source 2000 9 (0.45%; expected 1/256 = 0.39%)
2.3.0, pico-sdk-tools Windows release binary 2000 7
develop at ba3df40, built from source 2000 7
develop at ba3df40 + the fix below 2000 0

An earlier independent run of 150 seals of 2.3.0 showed 2 failures.

Every failing signature has the same shape: one half is 00 XX followed by 30 zero bytes, e.g.

r = 007f000000000000000000000000000000000000000000000000000000000000
s = efc6bedd550f00ebc968cfbba229c34573a58cbb914fe7a27d304e8ac667eb1c
Root cause

der_to_raw() in bintool/mbedtls_wrapper.c, the branch for a DER INTEGER shorter than 32 bytes:

} else {
    memset(r, 0, sizeof(r));
    memcpy(r + (32 - b2), sig->der + 4, (32 - b2));   // copies 32-b2 bytes, should copy b2
}
...
} else {
    memset(s, 0, sizeof(r));
    memcpy(s + (32 - b3), sig->der + 6 + b2, (32 - b3));   // same, for s
}

The destination offset 32 - b is right (right-align, zero-pad on the left), but the length is 32 - b instead of b. For the usual short case b == 31 this copies one byte instead of 31, which produces exactly the 00 XX 00..00 pattern above.

A two-line fix PR follows. Note that #358 rewrites der_to_raw() with a read_der_integer() helper that copies len bytes, so it would also fix this in passing; the separate PR is the minimal standalone change in case a point release wants it on its own.

Lingua principale
C++
Stelle
1k
Fork
194
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

  • Nessun Dockerfile né file Docker Compose
  • Ha un modello di pull request
  • Nessuna guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di raspberrypi/picotool

Tutte le issue di raspberrypi/picotool

Issue simili

Altre issue su C++

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.