Skip to content

Commit 6cfced8

Browse files
committed
fix(espsecure): Extract public key version 1 in RAW format
1 parent 0177d61 commit 6cfced8

File tree

2 files changed

+78
-5
lines changed

2 files changed

+78
-5
lines changed

espsecure/__init__.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,24 +1056,29 @@ def verify_signature_v2(hsm: bool, hsm_config: IO | None, keyfile: IO, datafile:
10561056
)
10571057

10581058

1059-
def extract_public_key(version: int, keyfile: IO, public_keyfile: IO):
1059+
def extract_public_key(version: str, keyfile: IO, public_keyfile: IO):
10601060
_check_output_is_not_input(keyfile, public_keyfile)
10611061
if version == "1":
10621062
"""
10631063
Load an ECDSA private key and extract the embedded public key
10641064
as raw binary data.
10651065
"""
10661066
sk = _load_ecdsa_signing_key(keyfile)
1067+
# For Secure Boot V1, output raw binary format (X and Y coordinates)
1068+
public_numbers = sk.public_key().public_numbers()
1069+
x_bytes = public_numbers.x.to_bytes(32, "big")
1070+
y_bytes = public_numbers.y.to_bytes(32, "big")
1071+
vk = x_bytes + y_bytes
10671072
elif version == "2":
10681073
"""
10691074
Load an RSA or an ECDSA private key and extract the public key
10701075
as raw binary data.
10711076
"""
10721077
sk = _load_sbv2_signing_key(keyfile.read())
1073-
vk = sk.public_key().public_bytes(
1074-
encoding=serialization.Encoding.PEM,
1075-
format=serialization.PublicFormat.SubjectPublicKeyInfo,
1076-
)
1078+
vk = sk.public_key().public_bytes(
1079+
encoding=serialization.Encoding.PEM,
1080+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
1081+
)
10771082
public_keyfile.write(vk)
10781083
log.print(f'"{keyfile.name}" public key extracted to "{public_keyfile.name}".')
10791084

test/test_espsecure.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,74 @@ def test_digest_rsa_public_key(self):
101101
finally:
102102
os.unlink(output_file.name)
103103

104+
def test_extract_public_key_v1(self):
105+
"""Test that extract-public-key CLI command produces raw output for version 1"""
106+
with tempfile.TemporaryDirectory() as keydir:
107+
# Generate a version 1 ECDSA256 key
108+
keyfile_name = os.path.join(keydir, "v1_key.pem")
109+
espsecure.generate_signing_key("1", "ecdsa256", keyfile_name)
110+
111+
output_file = os.path.join(keydir, "v1_public_key.bin")
112+
output = self.run_espsecure(
113+
f"extract-public-key --version 1 --keyfile {keyfile_name} {output_file}"
114+
)
115+
116+
# Check that the command succeeded
117+
assert "public key extracted" in output.lower()
118+
119+
# Read the output file
120+
with open(output_file, "rb") as f:
121+
v1_output = f.read()
122+
123+
# Version 1 should produce raw binary (64 bytes for ECDSA256)
124+
assert len(v1_output) == 64, (
125+
f"Expected 64 bytes for ECDSA256, got {len(v1_output)}"
126+
)
127+
128+
# Raw binary should not contain PEM markers
129+
assert b"-----BEGIN PUBLIC KEY-----" not in v1_output
130+
assert b"-----END PUBLIC KEY-----" not in v1_output
131+
assert b"PUBLIC KEY" not in v1_output
132+
133+
# Raw binary should contain only binary data (not text)
134+
printable_count = sum(1 for b in v1_output if 32 <= b <= 126)
135+
assert printable_count < len(v1_output), (
136+
"Raw binary should not be all printable ASCII"
137+
)
138+
139+
def test_extract_public_key_v2(self):
140+
"""Test that extract-public-key CLI command produces PEM output for version 2"""
141+
with tempfile.TemporaryDirectory() as keydir:
142+
# Generate a version 2 ECDSA256 key
143+
keyfile_name = os.path.join(keydir, "v2_key.pem")
144+
espsecure.generate_signing_key("2", "ecdsa256", keyfile_name)
145+
146+
output_file = os.path.join(keydir, "v2_public_key.pem")
147+
output = self.run_espsecure(
148+
f"extract-public-key --version 2 --keyfile {keyfile_name} {output_file}"
149+
)
150+
151+
# Check that the command succeeded
152+
assert "public key extracted" in output.lower()
153+
154+
# Read the output file
155+
with open(output_file, "rb") as f:
156+
v2_output = f.read()
157+
158+
# Version 2 should produce PEM format
159+
assert b"-----BEGIN PUBLIC KEY-----" in v2_output
160+
assert b"-----END PUBLIC KEY-----" in v2_output
161+
assert b"PUBLIC KEY" in v2_output
162+
163+
# PEM format should be longer than raw binary
164+
assert len(v2_output) > 64, "PEM format should be longer than raw binary"
165+
166+
# PEM format should be mostly printable ASCII
167+
printable_count = sum(1 for b in v2_output if 32 <= b <= 126)
168+
assert printable_count > len(v2_output) * 0.8, (
169+
"PEM format should be mostly printable ASCII"
170+
)
171+
104172

105173
class TestSigning(EspSecureTestCase):
106174
def test_key_generation_v1(self):

0 commit comments

Comments
 (0)