Skip to content

Commit dd01ace

Browse files
committed
Port stack buffer overflow regression tests to python
Ports the regression tests added in ec1c1e5 (Merge PR #212, F 569: Fix stack buffer overflow in encryption setup) from enc-test.sh to the python enc-test.py. Covers both the AES/EVP inName path and the Camellia non-EVP outNameEnc/outNameDec paths for: - Filename supplied via stdin when -in/-out is omitted - Empty-line reprompt after invalid input - Long-input flush (255 chars) after overflow attempt
1 parent d88b319 commit dd01ace

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

tests/encrypt/enc-test.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,5 +275,181 @@ def test_legacy_aes_cbc_128_roundtrip(self):
275275
"legacy aes-cbc-128 round trip failed")
276276

277277

278+
def _camellia_available():
279+
"""Check if Camellia support is enabled in the wolfssl binary."""
280+
crl = os.path.join(CERTS_DIR, "crl.der")
281+
if not os.path.isfile(crl):
282+
return False
283+
probe = "test-cam-probe.enc"
284+
try:
285+
r = run_enc("enc", "-camellia-128-cbc", "-in", crl, "-out", probe,
286+
password="testpass")
287+
return r.returncode == 0
288+
finally:
289+
if os.path.exists(probe):
290+
os.remove(probe)
291+
292+
293+
class EncStdinInputTest(unittest.TestCase):
294+
"""Regression tests for stack buffer overflow fix (scanf -> fgets).
295+
296+
When -in or -out is omitted, wolfCLU prompts for the filename on stdin.
297+
These tests exercise the fgets-based input paths, including the
298+
empty-line re-prompt and the too-long-input flush branches.
299+
"""
300+
301+
@classmethod
302+
def setUpClass(cls):
303+
if not os.path.isdir(CERTS_DIR):
304+
raise unittest.SkipTest("certs directory not found")
305+
306+
config_log = os.path.join(".", "config.log")
307+
if os.path.isfile(config_log):
308+
with open(config_log, "r") as f:
309+
if "disable-filesystem" in f.read():
310+
raise unittest.SkipTest("filesystem support disabled")
311+
312+
cls.has_camellia = _camellia_available()
313+
314+
def _cleanup(self, *files):
315+
for f in files:
316+
self.addCleanup(lambda p=f: os.remove(p)
317+
if os.path.exists(p) else None)
318+
319+
def _run_enc_stdin(self, stdin_data, *args, password="testpass"):
320+
"""Run wolfssl enc with stdin input (for filename prompts)."""
321+
cmd = [WOLFSSL_BIN, "enc"] + list(args) + ["-k", password]
322+
return subprocess.run(cmd, input=stdin_data,
323+
capture_output=True, text=True, timeout=60)
324+
325+
# -- AES (EVP path) --
326+
327+
def test_aes_inname_via_stdin(self):
328+
"""-in omitted; filename supplied via stdin (inName path)."""
329+
enc = "test-stdin-in.enc"
330+
dec = "test-stdin-in.dec"
331+
orig = os.path.join(CERTS_DIR, "crl.der")
332+
self._cleanup(enc, dec)
333+
334+
r = self._run_enc_stdin(f"{orig}\n", "-aes-128-cbc", "-out", enc)
335+
self.assertEqual(r.returncode, 0,
336+
f"enc with stdin input failed: {r.stderr}")
337+
338+
r = run_enc("enc", "-d", "-aes-128-cbc", "-in", enc, "-out", dec,
339+
password="testpass")
340+
self.assertEqual(r.returncode, 0, r.stderr)
341+
self.assertTrue(filecmp.cmp(orig, dec, shallow=False),
342+
"stdin enc/dec roundtrip mismatch")
343+
344+
def test_aes_inname_empty_line_reprompt(self):
345+
"""Empty line on stdin is rejected; next valid filename accepted."""
346+
enc = "test-empty-in.enc"
347+
dec = "test-empty-in.dec"
348+
orig = os.path.join(CERTS_DIR, "crl.der")
349+
self._cleanup(enc, dec)
350+
351+
r = self._run_enc_stdin(f"\n{orig}\n", "-aes-128-cbc", "-out", enc)
352+
self.assertEqual(r.returncode, 0,
353+
f"enc should accept filename after empty line: "
354+
f"{r.stderr}")
355+
356+
r = run_enc("enc", "-d", "-aes-128-cbc", "-in", enc, "-out", dec,
357+
password="testpass")
358+
self.assertEqual(r.returncode, 0, r.stderr)
359+
self.assertTrue(filecmp.cmp(orig, dec, shallow=False),
360+
"enc/dec roundtrip mismatch after empty-line reprompt")
361+
362+
def test_aes_inname_too_long_reprompt(self):
363+
"""Overlong input is flushed; next valid filename accepted."""
364+
enc = "test-toolong-in.enc"
365+
dec = "test-toolong-in.dec"
366+
orig = os.path.join(CERTS_DIR, "crl.der")
367+
self._cleanup(enc, dec)
368+
369+
long_input = " " * 255
370+
r = self._run_enc_stdin(f"{long_input}\n{orig}\n",
371+
"-aes-128-cbc", "-out", enc)
372+
self.assertEqual(r.returncode, 0,
373+
f"enc should recover after too-long input: "
374+
f"{r.stderr}")
375+
376+
r = run_enc("enc", "-d", "-aes-128-cbc", "-in", enc, "-out", dec,
377+
password="testpass")
378+
self.assertEqual(r.returncode, 0, r.stderr)
379+
self.assertTrue(filecmp.cmp(orig, dec, shallow=False),
380+
"enc/dec roundtrip mismatch after too-long reprompt")
381+
382+
# -- Camellia (non-EVP path) --
383+
384+
def test_camellia_outname_via_stdin(self):
385+
"""-out omitted; output filename supplied via stdin (non-EVP path)."""
386+
if not self.has_camellia:
387+
self.skipTest("Camellia not available")
388+
enc = "test-cam-stdin.enc"
389+
dec = "test-cam-stdin.dec"
390+
orig = os.path.join(CERTS_DIR, "crl.der")
391+
self._cleanup(enc, dec)
392+
393+
r = self._run_enc_stdin(f"{enc}\n", "-camellia-128-cbc", "-in", orig)
394+
self.assertEqual(r.returncode, 0,
395+
f"Camellia enc with stdin output name failed: "
396+
f"{r.stderr}")
397+
398+
r = self._run_enc_stdin(f"{dec}\n", "-d", "-camellia-128-cbc",
399+
"-in", enc)
400+
self.assertEqual(r.returncode, 0,
401+
f"Camellia dec with stdin output name failed: "
402+
f"{r.stderr}")
403+
self.assertTrue(filecmp.cmp(orig, dec, shallow=False),
404+
"Camellia stdin outName roundtrip mismatch")
405+
406+
def test_camellia_outname_empty_line_reprompt(self):
407+
"""Empty line rejected; next valid output name accepted (non-EVP)."""
408+
if not self.has_camellia:
409+
self.skipTest("Camellia not available")
410+
enc = "test-cam-empty.enc"
411+
dec = "test-cam-empty.dec"
412+
orig = os.path.join(CERTS_DIR, "crl.der")
413+
self._cleanup(enc, dec)
414+
415+
r = self._run_enc_stdin(f"\n{enc}\n", "-camellia-128-cbc", "-in", orig)
416+
self.assertEqual(r.returncode, 0,
417+
f"Camellia enc should accept output name after "
418+
f"empty line: {r.stderr}")
419+
420+
r = self._run_enc_stdin(f"\n{dec}\n", "-d", "-camellia-128-cbc",
421+
"-in", enc)
422+
self.assertEqual(r.returncode, 0,
423+
f"Camellia dec should accept output name after "
424+
f"empty line: {r.stderr}")
425+
self.assertTrue(filecmp.cmp(orig, dec, shallow=False),
426+
"Camellia roundtrip mismatch after empty-line "
427+
"reprompt")
428+
429+
def test_camellia_outname_too_long_reprompt(self):
430+
"""Overlong input flushed; next valid output name accepted (non-EVP)."""
431+
if not self.has_camellia:
432+
self.skipTest("Camellia not available")
433+
enc = "test-cam-toolong.enc"
434+
dec = "test-cam-toolong.dec"
435+
orig = os.path.join(CERTS_DIR, "crl.der")
436+
self._cleanup(enc, dec)
437+
438+
long_input = " " * 255
439+
r = self._run_enc_stdin(f"{long_input}\n{enc}\n",
440+
"-camellia-128-cbc", "-in", orig)
441+
self.assertEqual(r.returncode, 0,
442+
f"Camellia enc should recover after too-long output "
443+
f"name: {r.stderr}")
444+
445+
r = self._run_enc_stdin(f"{long_input}\n{dec}\n",
446+
"-d", "-camellia-128-cbc", "-in", enc)
447+
self.assertEqual(r.returncode, 0,
448+
f"Camellia dec should recover after too-long output "
449+
f"name: {r.stderr}")
450+
self.assertTrue(filecmp.cmp(orig, dec, shallow=False),
451+
"Camellia roundtrip mismatch after too-long reprompt")
452+
453+
278454
if __name__ == "__main__":
279455
test_main()

0 commit comments

Comments
 (0)