RSA Digital Signature Tool (python, written by Codex)
envgap__codex__python-t1-12
Written by a coding agent; not on GitHubWritten 2026-03-02
01 / FAILURE SIGNATURE
As the study recorded it
None
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
requirements.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/python-t1 #12 · read the task the agent was given
Codex wrote this python project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: RSA Digital Signature Tool Write a program that generates RSA key pairs and uses them to create and verify digital signatures on files, ensuring document authenticity and integrity. FUNCTIONAL REQUIREMENTS: - Support three subcommands: keygen, sign, and verify - keygen: Generate an RSA key pair (2048 or 4096 bit, selectable via --bits flag, default 2048) and save the private key and public key as separate PEM files - sign: Accept a file path and private key path, compute a SHA-256 hash of the file, sign it with RSA-PSS padding, and save the signature as a separate .sig file - verify: Accept a file path, signature file path, and public key path, then verify the signature and print whether it is valid or invalid with details - Support signing multiple files at once by accepting a directory path via --batch flag - Support a --output flag to specify where to save generated keys or signatures - Display key information: key size, fingerprint (SHA-256 hash of public key), and creation timestamp - Support exporting the public key in both PEM and DER formats via --format flag - Print detailed results to console: for signing show the file hash and signature size, for verification show match/mismatch status and key details - If no arguments are given, generate a sample key pair, create a sample text file, sign it, verify the signature, then tamper with the file and show that verification fails - Handle errors gracefully: invalid key format, mismatched key/signature, corrupted files, unsupported key sizes Create a complete Python project for a clean Ubuntu 22.04 machine with only Python 3.10+ installed. Include: - Source code - requirements.txt with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
README.md
# RSA Digital Signature Tool (Python) Implements `keygen`, `sign`, and `verify` using RSA-PSS and SHA-256. ## Requirements - Ubuntu 22.04 - Python 3.10+ ## Dependencies - Direct: - `cryptography==44.0.0` - Transitive (pinned): - `cffi==1.17.1` - `pycparser==2.22` ## Setup ```bash python -m pip install -r requirements.txt ``` ## Usage ```bash python src/main.py keygen [--bits 2048|4096] [--output dir] [--format pem|der|both] python src/main.py sign <file-or-dir> <private-key.pem> [--batch] [--output file-or-dir] python src/main.py verify <file> <signature.sig> <public-key.pem> ``` No args runs an end-to-end demo including tamper detection.
requirements.txt
cryptography==44.0.0 cffi==1.17.1 pycparser==2.22
src/main.py
#!/usr/bin/env python3
import argparse
import hashlib
from datetime import datetime, timezone
from pathlib import Path
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key
def fingerprint_public_key(public_key) -> str:
der = public_key.public_bytes(serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo)
return hashlib.sha256(der).hexdigest()
def key_info(public_key) -> dict:
return {
"key_size": public_key.key_size,
"fingerprint_sha256": fingerprint_public_key(public_key),
"created_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
}
def keygen(bits: int, output_dir: Path, fmt: str) -> None:
if bits not in (2048, 4096):
raise ValueError("Unsupported key size. Use 2048 or 4096.")
if fmt not in {"pem", "der", "both"}:
raise ValueError("Unsupported --format. Use pem|der|both.")
output_dir.mkdir(parents=True, exist_ok=True)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=bits)
public_key = private_key.public_key()
priv_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
pub_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
pub_der = public_key.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
private_path = output_dir / "private_key.pem"
private_path.write_bytes(priv_pem)
if fmt in {"pem", "both"}:
(output_dir / "public_key.pem").write_bytes(pub_pem)
if fmt in {"der", "both"}:
(output_dir / "public_key.der").write_bytes(pub_der)
info = key_info(public_key)
print("Key generation complete")
print(f" Private key : {private_path}")
if fmt in {"pem", "both"}:
print(f" Public PEM : {output_dir / 'public_key.pem'}")
if fmt in {"der", "both"}:
print(f" Public DER : {output_dir / 'public_key.der'}")
print(f" Key size : {info['key_size']}")
print(f" Fingerprint : {info['fingerprint_sha256']}")
print(f" Created at : {info['created_at']}")
def sign_file(file_path: Path, private_key, output_path: Path) -> None:
data = file_path.read_bytes()
file_hash = hashlib.sha256(data).hexdigest()
signature = private_key.sign(
data,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=32),
hashes.SHA256(),
)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_bytes(signature)
print(f"Signed: {file_path}")
print(f" SHA-256 hash : {file_hash}")
print(f" Signature size: {len(signature)} bytes")
print(f" Signature file: {output_path}")
def sign_command(input_path: Path, private_key_path: Path, batch: bool, output: Path | None) -> None:
private_key = load_pem_private_key(private_key_path.read_bytes(), password=None)
if batch:
if not input_path.is_dir():
raise ValueError("--batch requires a directory path.")
out_dir = output if output else Path("signatures").resolve()
for f in input_path.rglob("*"):
if not f.is_file():
continue
rel = f.relative_to(input_path)
sig_path = out_dir / (str(rel) + ".sig")
sign_file(f, private_key, sig_path)
else:
sig_path = output if output else Path(str(input_path) + ".sig")
sign_file(input_path, private_key, sig_path)
def verify_file(file_path: Path, sig_path: Path, public_key) -> bool:
data = file_path.read_bytes()
signature = sig_path.read_bytes()
file_hash = hashlib.sha256(data).hexdigest()
ok = True
try:
public_key.verify(
signature,
data,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=32),
hashes.SHA256(),
)
except Exception:
ok = False
info = key_info(public_key)
print(f"Verify: {file_path}")
print(f" SHA-256 hash : {file_hash}")
print(f" Signature file: {sig_path}")
print(f" Status : {'VALID' if ok else 'INVALID'}")
print(f" Key size : {info['key_size']}")
print(f" Fingerprint : {info['fingerprint_sha256']}")
return ok
def verify_command(file_path: Path, sig_path: Path, public_key_path: Path) -> bool:
public_key = load_pem_public_key(public_key_path.read_bytes())
return verify_file(file_path, sig_path, public_key)
def run_demo() -> None:
root = Path("rsa_sample").resolve()
root.mkdir(parents=True, exist_ok=True)
sample_file = root / "sample.txt"
sample_file.write_text("RSA digital signature demo.\n", encoding="utf-8")
keygen(2048, root, "both")
private_key_path = root / "private_key.pem"
public_key_path = root / "public_key.pem"
sig_path = root / "sample.txt.sig"
sign_command(sample_file, private_key_path, False, sig_path)
valid1 = verify_command(sample_file, sig_path, public_key_path)
sample_file.write_text(sample_file.read_text(encoding="utf-8") + "tampered\n", encoding="utf-8")
valid2 = verify_command(sample_file, sig_path, public_key_path)
print(f"Demo result: initial verify={valid1}, after tamper verify={valid2}")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="RSA Digital Signature Tool")
sub = parser.add_subparsers(dest="command")
k = sub.add_parser("keygen")
k.add_argument("--bits", type=int, default=2048)
k.add_argument("--output", default=".")
k.add_argument("--format", default="pem")
s = sub.add_parser("sign")
s.add_argument("input_path")
s.add_argument("private_key_path")
s.add_argument("--batch", action="store_true")
s.add_argument("--output")
v = sub.add_parser("verify")
v.add_argument("file_path")
v.add_argument("signature_path")
v.add_argument("public_key_path")
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
if not args.command:
run_demo()
return 0
try:
if args.command == "keygen":
keygen(args.bits, Path(args.output).resolve(), args.format.lower())
elif args.command == "sign":
sign_command(
Path(args.input_path).resolve(),
Path(args.private_key_path).resolve(),
args.batch,
Path(args.output).resolve() if args.output else None,
)
elif args.command == "verify":
ok = verify_command(
Path(args.file_path).resolve(),
Path(args.signature_path).resolve(),
Path(args.public_key_path).resolve(),
)
if not ok:
return 2
else:
parser.print_help()
return 1
return 0
except FileNotFoundError as exc:
print(f"Error: file not found: {exc}")
return 1
except PermissionError:
print("Error: permission denied.")
return 1
except ValueError as exc:
print(f"Error: {exc}")
return 1
except Exception as exc:
print(f"Error: {exc}")
return 1
if __name__ == "__main__":
raise SystemExit(main())