Skip to content

Commit aa39be2

Browse files
aidangarskedanielinux
authored andcommitted
Address copilot review
1 parent 145256a commit aa39be2

File tree

1 file changed

+42
-19
lines changed

1 file changed

+42
-19
lines changed

tools/scripts/boot_status.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
A unified utility for managing wolfBoot images, boot status, and keystores.
66
77
Usage:
8-
boot_status.py status get <partition> --file <file> --config <config>
9-
boot_status.py status set <partition> <value> --file <file> --config <config>
8+
boot_status.py status --file <file> --config <config> get <partition>
9+
boot_status.py status --file <file> --config <config> set <partition> <value>
1010
boot_status.py image inspect <image> [--header-size SIZE]
1111
boot_status.py image verify <image> --pubkey <key> [--alg ALG] [--verify-hash]
1212
boot_status.py image dump <image> <output> [--header-size SIZE]
1313
boot_status.py keystore convert <input> [--curve CURVE]
1414
1515
Examples:
1616
# Boot status management
17-
boot_status.py status get BOOT --file internal_flash.dd --config .config
18-
boot_status.py status set UPDATE SUCCESS --file internal_flash.dd --config .config
17+
boot_status.py status --file internal_flash.dd --config .config get BOOT
18+
boot_status.py status --file internal_flash.dd --config .config set UPDATE SUCCESS
1919
2020
# Image inspection and verification
2121
boot_status.py image inspect test_v1_signed.bin
@@ -122,6 +122,7 @@ def read_config(config_path: str) -> dict[str, str]:
122122
# IMAGE INSPECTION (from image-peek.py)
123123
# ============================================================================
124124

125+
# TLV type constants for wolfBoot image headers (for documentation/reference)
125126
TYPE_NAMES = {
126127
0x0001: "version",
127128
0x0002: "timestamp",
@@ -135,6 +136,8 @@ def parse_header(data: bytes, header_size: int = 0x100):
135136
"""Parse wolfBoot image header and TLVs."""
136137
if len(data) < 8:
137138
raise ValueError("Input too small to contain header")
139+
if len(data) < header_size:
140+
raise ValueError(f"Input size ({len(data)} bytes) is smaller than specified header_size ({header_size} bytes)")
138141
magic = data[0:4]
139142
size_le = struct.unpack("<I", data[4:8])[0]
140143
off = 8
@@ -166,6 +169,8 @@ def find_tlv(data: bytes, header_size: int, ttype: int):
166169
Scan the header TLV area and return (value_offset, value_len, tlv_start_offset)
167170
for the first TLV matching 'ttype'. Returns None if not found.
168171
"""
172+
if len(data) < header_size:
173+
return None
169174
off = 8 # skip magic(4) + size(4)
170175
while off + 4 <= header_size:
171176
# skip padding bytes 0xFF
@@ -188,7 +193,7 @@ def decode_timestamp(v: bytes):
188193
"""Decode wolfBoot timestamp TLV."""
189194
ts = struct.unpack("<Q", v)[0]
190195
try:
191-
utc = datetime.datetime.utcfromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S UTC")
196+
utc = datetime.datetime.fromtimestamp(ts, tz=datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
192197
except Exception:
193198
utc = "out-of-range"
194199
return ts, utc
@@ -244,11 +249,17 @@ def verify_signature(pubkey, alg: str, firmware_hash: bytes, signature: bytes):
244249
return False, f"ECDSA verify error: {e}"
245250

246251
if alg == "ed25519":
252+
# NOTE: wolfBoot uses Ed25519 in a non-standard way - it signs the SHA hash digest,
253+
# not the full message (see image.c:142). This matches wolfBoot's implementation where
254+
# wc_ed25519_verify_msg is called with img->sha_hash (the precomputed digest) as the
255+
# "message" parameter. While this differs from standard Ed25519 practice (which hashes
256+
# the full message internally), we implement it this way to match wolfBoot's behavior.
247257
try:
248258
if not hasattr(pubkey, "verify"):
249259
return False, "Public key object is not Ed25519-capable"
260+
# Verify the signature over the digest (matching wolfBoot's approach)
250261
pubkey.verify(signature, firmware_hash)
251-
return True, "Signature OK (Ed25519 over stored digest)"
262+
return True, "Signature OK (Ed25519 over stored digest - non-standard wolfBoot approach)"
252263
except Exception as e:
253264
return False, f"Ed25519 verify error: {e}"
254265

@@ -272,11 +283,17 @@ def cmd_image_inspect(args):
272283

273284
version = d.get(0x0001, [(None, None)])[0][1]
274285
if version is not None:
275-
print(f"Version: {struct.unpack('<I', version)[0]}")
286+
if len(version) >= 4:
287+
print(f"Version: {struct.unpack('<I', version[:4])[0]}")
288+
else:
289+
print(f"Version TLV too short ({len(version)} bytes), expected at least 4 bytes")
276290
if 0x0002 in d:
277291
ts_val = d[0x0002][0][1]
278-
ts, utc = decode_timestamp(ts_val)
279-
print(f"Timestamp: {ts} ({utc})")
292+
if ts_val is not None and len(ts_val) == 8:
293+
ts, utc = decode_timestamp(ts_val)
294+
print(f"Timestamp: {ts} ({utc})")
295+
else:
296+
print(f"Timestamp TLV has invalid length: expected 8 bytes, got {0 if ts_val is None else len(ts_val)}")
280297
hash_bytes = d.get(0x0003, [(None, None)])[0][1]
281298
if hash_bytes is not None:
282299
print(f"Hash ({len(hash_bytes)} bytes): {hash_bytes.hex()}")
@@ -285,7 +302,16 @@ def cmd_image_inspect(args):
285302
print(f"Pubkey hint: {hint}")
286303
sig = d.get(0x0020, [(None, None)])[0][1]
287304
if sig is not None:
288-
print(f"Signature ({len(sig)} bytes): {sig[:8].hex()}...{sig[-8:].hex()}")
305+
sig_len = len(sig)
306+
if sig_len >= 16:
307+
# Show first and last 8 bytes for typical full-length signatures
308+
print(f"Signature ({sig_len} bytes): {sig[:8].hex()}...{sig[-8:].hex()}")
309+
elif sig_len > 0:
310+
# For short signatures, show the entire value without an ellipsis
311+
print(f"Signature ({sig_len} bytes): {sig.hex()}")
312+
else:
313+
# Explicitly handle empty signatures
314+
print("Signature (0 bytes): <empty>")
289315

290316
if len(data) < header_size + size:
291317
print(f"[WARN] File shorter ({len(data)} bytes) than header+payload ({header_size+size}). Hash/signature verification may fail.")
@@ -353,7 +379,7 @@ def cmd_image_verify(args):
353379
if len(sig) == 64 and len(hash_bytes) in (32,48,64):
354380
alg = "ecdsa-p256"
355381
else:
356-
print(f"[SIG] Cannot infer algorithm (sig={len(sig)} bytes, hash={len(hash_bytes) if hash_bytes else 0})")
382+
print(f"[SIG] Cannot infer algorithm (sig={len(sig)} bytes, hash={len(hash_bytes) if hash_bytes else 0}); defaulting to ecdsa-p256. Specify --alg explicitly for best results.")
357383
alg = "ecdsa-p256"
358384
ok, msg = verify_signature(pubkey, alg, hash_bytes, sig)
359385
print(f"[SIG] {msg} (alg={alg})")
@@ -406,13 +432,10 @@ def cmd_keystore_convert(args):
406432
# 1) raw X||Y (64/96/132)
407433
# 2) SEC1 0x04||X||Y (65/97/133)
408434
# 3) wolfBoot 16+X||Y (80/112/148)
409-
data = raw
410-
is_sec1 = False
411435

412436
# Case 2: SEC1 uncompressed (leading 0x04, lengths 65/97/133)
413437
if ln in (65, 97, 133) and raw[0] == 0x04:
414438
sec1 = raw
415-
is_sec1 = True
416439
xy_len = ln - 1
417440
# Case 3: wolfBoot container 16+X||Y
418441
elif ln in (80, 112, 148):
@@ -422,12 +445,10 @@ def cmd_keystore_convert(args):
422445
print("ERROR: Unexpected container size after stripping 16 bytes:", len(data), file=sys.stderr)
423446
sys.exit(3)
424447
sec1 = b"\x04" + data
425-
is_sec1 = True
426448
xy_len = len(data)
427449
# Case 1: raw X||Y
428450
elif ln in (64, 96, 132):
429451
sec1 = b"\x04" + raw
430-
is_sec1 = True
431452
xy_len = ln
432453
else:
433454
print("ERROR: Unrecognized input size:", ln, file=sys.stderr)
@@ -452,8 +473,11 @@ def cmd_keystore_convert(args):
452473
crv = ec.SECP256R1()
453474
elif curve == "p384":
454475
crv = ec.SECP384R1()
455-
else:
476+
elif curve == "p521":
456477
crv = ec.SECP521R1()
478+
else:
479+
print("ERROR: Unsupported or unknown curve:", curve, file=sys.stderr)
480+
sys.exit(4)
457481

458482
try:
459483
key_obj = ec.EllipticCurvePublicKey.from_encoded_point(crv, sec1)
@@ -479,11 +503,10 @@ def cmd_keystore_convert(args):
479503

480504
# Print SPKI SHA-256 for pubkey-hint comparison
481505
try:
482-
import binascii
483506
h = hashlib.sha256(der).digest()
484507
print("Wrote:", out_der)
485508
print("Wrote:", out_pem)
486-
print("SPKI SHA-256 (hex):", binascii.hexlify(h).decode("ascii"))
509+
print("SPKI SHA-256 (hex):", h.hex())
487510
except Exception:
488511
print("Wrote:", out_der)
489512
print("Wrote:", out_pem)

0 commit comments

Comments
 (0)