fix(espsecure): Extract public key version 1 in RAW format

This commit is contained in:
Peter Dragun
2025-08-19 15:42:56 +02:00
parent 0177d611ac
commit 6cfced81f9
2 changed files with 78 additions and 5 deletions

View File

@@ -1056,7 +1056,7 @@ def verify_signature_v2(hsm: bool, hsm_config: IO | None, keyfile: IO, datafile:
)
def extract_public_key(version: int, keyfile: IO, public_keyfile: IO):
def extract_public_key(version: str, keyfile: IO, public_keyfile: IO):
_check_output_is_not_input(keyfile, public_keyfile)
if version == "1":
"""
@@ -1064,16 +1064,21 @@ def extract_public_key(version: int, keyfile: IO, public_keyfile: IO):
as raw binary data.
"""
sk = _load_ecdsa_signing_key(keyfile)
# For Secure Boot V1, output raw binary format (X and Y coordinates)
public_numbers = sk.public_key().public_numbers()
x_bytes = public_numbers.x.to_bytes(32, "big")
y_bytes = public_numbers.y.to_bytes(32, "big")
vk = x_bytes + y_bytes
elif version == "2":
"""
Load an RSA or an ECDSA private key and extract the public key
as raw binary data.
"""
sk = _load_sbv2_signing_key(keyfile.read())
vk = sk.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
vk = sk.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
public_keyfile.write(vk)
log.print(f'"{keyfile.name}" public key extracted to "{public_keyfile.name}".')

View File

@@ -101,6 +101,74 @@ class TestESP32SecureBootloader(EspSecureTestCase):
finally:
os.unlink(output_file.name)
def test_extract_public_key_v1(self):
"""Test that extract-public-key CLI command produces raw output for version 1"""
with tempfile.TemporaryDirectory() as keydir:
# Generate a version 1 ECDSA256 key
keyfile_name = os.path.join(keydir, "v1_key.pem")
espsecure.generate_signing_key("1", "ecdsa256", keyfile_name)
output_file = os.path.join(keydir, "v1_public_key.bin")
output = self.run_espsecure(
f"extract-public-key --version 1 --keyfile {keyfile_name} {output_file}"
)
# Check that the command succeeded
assert "public key extracted" in output.lower()
# Read the output file
with open(output_file, "rb") as f:
v1_output = f.read()
# Version 1 should produce raw binary (64 bytes for ECDSA256)
assert len(v1_output) == 64, (
f"Expected 64 bytes for ECDSA256, got {len(v1_output)}"
)
# Raw binary should not contain PEM markers
assert b"-----BEGIN PUBLIC KEY-----" not in v1_output
assert b"-----END PUBLIC KEY-----" not in v1_output
assert b"PUBLIC KEY" not in v1_output
# Raw binary should contain only binary data (not text)
printable_count = sum(1 for b in v1_output if 32 <= b <= 126)
assert printable_count < len(v1_output), (
"Raw binary should not be all printable ASCII"
)
def test_extract_public_key_v2(self):
"""Test that extract-public-key CLI command produces PEM output for version 2"""
with tempfile.TemporaryDirectory() as keydir:
# Generate a version 2 ECDSA256 key
keyfile_name = os.path.join(keydir, "v2_key.pem")
espsecure.generate_signing_key("2", "ecdsa256", keyfile_name)
output_file = os.path.join(keydir, "v2_public_key.pem")
output = self.run_espsecure(
f"extract-public-key --version 2 --keyfile {keyfile_name} {output_file}"
)
# Check that the command succeeded
assert "public key extracted" in output.lower()
# Read the output file
with open(output_file, "rb") as f:
v2_output = f.read()
# Version 2 should produce PEM format
assert b"-----BEGIN PUBLIC KEY-----" in v2_output
assert b"-----END PUBLIC KEY-----" in v2_output
assert b"PUBLIC KEY" in v2_output
# PEM format should be longer than raw binary
assert len(v2_output) > 64, "PEM format should be longer than raw binary"
# PEM format should be mostly printable ASCII
printable_count = sum(1 for b in v2_output if 32 <= b <= 126)
assert printable_count > len(v2_output) * 0.8, (
"PEM format should be mostly printable ASCII"
)
class TestSigning(EspSecureTestCase):
def test_key_generation_v1(self):