fix(tools): add key type validation and tighten test assertions in sign_firmware

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 06:50:51 -07:00
parent 031426e364
commit 87b30a64b2
2 changed files with 16 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Sign a firmware binary with ECDSA P-256. Outputs a raw 64-byte r||s .sig file.""" """Sign a firmware binary with ECDSA P-256. Outputs a raw 64-byte r||s .sig file."""
import argparse import argparse
import sys
from pathlib import Path from pathlib import Path
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
@@ -9,7 +10,12 @@ from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
def load_private_key(key_path: Path) -> ec.EllipticCurvePrivateKey: def load_private_key(key_path: Path) -> ec.EllipticCurvePrivateKey:
return serialization.load_pem_private_key(key_path.read_bytes(), password=None) key = serialization.load_pem_private_key(key_path.read_bytes(), password=None)
if not isinstance(key, ec.EllipticCurvePrivateKey):
raise ValueError("Key must be an EC private key")
if not isinstance(key.curve, ec.SECP256R1):
raise ValueError(f"Key must use SECP256R1 curve, got {key.curve.name}")
return key
def sign_firmware(firmware_path: Path, key_path: Path) -> bytes: def sign_firmware(firmware_path: Path, key_path: Path) -> bytes:
@@ -17,6 +23,7 @@ def sign_firmware(firmware_path: Path, key_path: Path) -> bytes:
data = firmware_path.read_bytes() data = firmware_path.read_bytes()
sig_der = key.sign(data, ec.ECDSA(hashes.SHA256())) sig_der = key.sign(data, ec.ECDSA(hashes.SHA256()))
r, s = decode_dss_signature(sig_der) r, s = decode_dss_signature(sig_der)
# Returns raw 64-byte r‖s (not DER) — mbedtls_ecdsa_verify expects this layout
return r.to_bytes(32, 'big') + s.to_bytes(32, 'big') return r.to_bytes(32, 'big') + s.to_bytes(32, 'big')
@@ -32,7 +39,11 @@ def main() -> None:
key_path = Path(args.key) key_path = Path(args.key)
out_path = Path(args.out) if args.out else firmware.with_suffix(".bin.sig") out_path = Path(args.out) if args.out else firmware.with_suffix(".bin.sig")
try:
sig = sign_firmware(firmware, key_path) sig = sign_firmware(firmware, key_path)
except (FileNotFoundError, ValueError) as e:
print(f"Error: {e}", file=sys.stderr)
raise SystemExit(1)
out_path.write_bytes(sig) out_path.write_bytes(sig)
print(f"Signed {firmware.name}{out_path} ({len(sig)} bytes)") print(f"Signed {firmware.name}{out_path} ({len(sig)} bytes)")

View File

@@ -1,7 +1,8 @@
import os, sys, tempfile import sys
from pathlib import Path from pathlib import Path
import pytest import pytest
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
@@ -59,5 +60,5 @@ def test_wrong_key_fails_verification(keypair, tmp_path):
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
sig_der = encode_dss_signature(r, s) sig_der = encode_dss_signature(r, s)
with pytest.raises(Exception): with pytest.raises(InvalidSignature):
other_key.public_key().verify(sig_der, b"firmware", ec.ECDSA(hashes.SHA256())) other_key.public_key().verify(sig_der, b"firmware", ec.ECDSA(hashes.SHA256()))