mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-01 10:22:03 +00:00
fix(python): TOTP codes must honor the entry's period and digits
TotpManager built pyotp.TOTP(secret) with library defaults (6 digits, 30s) and ignored what the entry recorded, so any imported or derived entry using other values produced codes the issuing service rejects. The service was configured from the entry's values -- those govern the code. - current_code_from_secret and current_code take period/digits - EntryManager.get_totp_code passes the entry's values for both the derived and imported paths - display_service, menu_handler (totp_list now carries digits) and the GUI updated at every call site - regression tests for imported and deterministic entries with custom parameters, asserting against pyotp with the matching interval/digits, and confirming default entries are byte-identical to before Found by scripts/cross_impl_check.py, which flagged it as a divergence against the TypeScript port. That allowance is now removed: the suite asserts TOTP equality and reports 30/30 with zero divergences. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
b4f20d417a
commit
5dcd779a6f
9 files changed
+132
-62
No files matched your search
@@ -260,7 +260,12 @@ def py_secrets_for(index: dict, seed: str) -> dict[str, str]:
|
||||
secret = entry["secret"]
|
||||
else:
|
||||
secret = derive_totp_secret(seed, int(entry.get("index", 0)))
|
||||
out[label] = TotpManager.current_code_from_secret(secret, FIXED_TS)
|
||||
out[label] = TotpManager.current_code_from_secret(
|
||||
secret,
|
||||
FIXED_TS,
|
||||
period=int(entry.get("period", 30)),
|
||||
digits=int(entry.get("digits", 6)),
|
||||
)
|
||||
elif kind == "key_value":
|
||||
out[label] = entry["value"]
|
||||
elif kind == "document":
|
||||
@@ -332,21 +337,6 @@ def phase_a(tmp: Path) -> None:
|
||||
for k in py_secrets
|
||||
if py_secrets[k].rstrip("\n") != (ts_secrets.get(k) or "").rstrip("\n")
|
||||
]
|
||||
# Known divergence: Python generates TOTP codes with pyotp defaults
|
||||
# (6 digits / 30s) even when the entry records different values, so an
|
||||
# imported 8-digit/45s code differs. Tracked, not silently accepted.
|
||||
custom_totp = {
|
||||
e["label"]
|
||||
for e in py_index.get("entries", {}).values()
|
||||
if e.get("kind", e.get("type")) == "totp"
|
||||
and (int(e.get("period", 30)) != 30 or int(e.get("digits", 6)) != 6)
|
||||
}
|
||||
for label in sorted(custom_totp & set(mismatches)):
|
||||
note_divergence(
|
||||
f"TOTP code for {label!r}",
|
||||
"Python ignores the entry's period/digits (pyotp defaults); TS honors them",
|
||||
)
|
||||
mismatches = [k for k in mismatches if k not in custom_totp]
|
||||
check(
|
||||
"every secret derives identically",
|
||||
not mismatches,
|
||||
@@ -417,21 +407,6 @@ def phase_b(tmp: Path) -> None:
|
||||
for k in py_secrets
|
||||
if py_secrets[k].rstrip("\n") != (ts_secrets.get(k) or "").rstrip("\n")
|
||||
]
|
||||
# Known divergence: Python generates TOTP codes with pyotp defaults
|
||||
# (6 digits / 30s) even when the entry records different values, so an
|
||||
# imported 8-digit/45s code differs. Tracked, not silently accepted.
|
||||
custom_totp = {
|
||||
e["label"]
|
||||
for e in py_index.get("entries", {}).values()
|
||||
if e.get("kind", e.get("type")) == "totp"
|
||||
and (int(e.get("period", 30)) != 30 or int(e.get("digits", 6)) != 6)
|
||||
}
|
||||
for label in sorted(custom_totp & set(mismatches)):
|
||||
note_divergence(
|
||||
f"TOTP code for {label!r}",
|
||||
"Python ignores the entry's period/digits (pyotp defaults); TS honors them",
|
||||
)
|
||||
mismatches = [k for k in mismatches if k not in custom_totp]
|
||||
check("every secret derives identically", not mismatches, f"mismatched: {mismatches}")
|
||||
|
||||
# Python's schema validation should accept the TS-written index
|
||||
|
||||
Reference in new issue
Block a user