with figerprint match option when encryption with asc file, extra assurance the file is the target asc file
This commit is contained in:
@@ -251,6 +251,19 @@ def interactive_mnemonic_word_by_word() -> str:
|
||||
# -----------------------------------------------------------------------------
|
||||
# Fingerprint
|
||||
# -----------------------------------------------------------------------------
|
||||
def normalize_fpr(s: str) -> str:
|
||||
s = (s or "").strip().replace(" ", "").replace(":", "").replace("-", "")
|
||||
if s.lower().startswith("0x"):
|
||||
s = s[2:]
|
||||
return s.upper()
|
||||
|
||||
def require_fingerprint_match(actual_fpr: str, expected_fpr: str, context: str) -> None:
|
||||
a = normalize_fpr(actual_fpr)
|
||||
e = normalize_fpr(expected_fpr)
|
||||
if not e:
|
||||
return
|
||||
if a != e:
|
||||
raise ValueError(f"{context}: PGP fingerprint mismatch (expected {e}, got {a})")
|
||||
|
||||
|
||||
def get_master_fingerprint(seed_bytes: bytes) -> str:
|
||||
@@ -315,12 +328,20 @@ def sha256_hex_bytes(data: bytes) -> str:
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def cmd_fetchkey(url: str, out_path: Optional[str], timeout: int, off_screen: bool) -> None:
|
||||
def cmd_fetchkey(
|
||||
url: str,
|
||||
out_path: Optional[str],
|
||||
timeout: int,
|
||||
off_screen: bool,
|
||||
expected_fingerprint: str = "",
|
||||
) -> None:
|
||||
|
||||
require_for_fetchkey()
|
||||
armored, raw = fetch_ascii_armored_text(url, timeout=timeout)
|
||||
|
||||
s256 = sha256_hex_bytes(raw)
|
||||
fpr = pgp_fingerprint(armored)
|
||||
require_fingerprint_match(fpr, expected_fingerprint, "fetchkey")
|
||||
|
||||
temp_file = None
|
||||
if off_screen:
|
||||
@@ -699,7 +720,8 @@ def cmd_gen(args) -> None:
|
||||
if is_pgp:
|
||||
with open(args.pgp_pubkey_file, "r", encoding="utf-8") as f:
|
||||
pub = f.read()
|
||||
|
||||
fpr = pgp_fingerprint(pub)
|
||||
require_fingerprint_match(fpr, args.expected_fingerprint, "encrypt(gen)")
|
||||
payload = build_payload_gen(
|
||||
mnemonic=mnemonic,
|
||||
passphrase_used=bool(passphrase),
|
||||
@@ -802,7 +824,11 @@ def cmd_recover(args) -> None:
|
||||
if is_pgp:
|
||||
with open(args.pgp_pubkey_file, "r", encoding="utf-8") as f:
|
||||
pub = f.read()
|
||||
fpr = pgp_fingerprint(pub)
|
||||
require_fingerprint_match(fpr, args.expected_fingerprint, "encrypt(gen)")
|
||||
|
||||
fpr = pgp_fingerprint(pub)
|
||||
require_fingerprint_match(fpr, args.expected_fingerprint, "encrypt")
|
||||
payload = build_payload_recover(
|
||||
fp=fp,
|
||||
sol_profile=args.sol_profile,
|
||||
@@ -902,6 +928,8 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
action="store_true",
|
||||
help="Enable off-screen mode: no printing of sensitive data to stdout.",
|
||||
)
|
||||
p_fetch.add_argument("--expected-fingerprint", default="", help="Refuse if downloaded key fingerprint does not match (40 hex chars).")
|
||||
|
||||
|
||||
def add_common(p: argparse.ArgumentParser) -> None:
|
||||
p.add_argument("--force", action="store_true", help="Allow printing sensitive output even when stdout is not a TTY (dangerous).")
|
||||
@@ -914,6 +942,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
p.add_argument("--pgp-pubkey-file", default=None)
|
||||
p.add_argument("--pgp-ignore-usage-flags", action="store_true")
|
||||
p.add_argument("--expected-fingerprint", default="", help="Refuse if PGP recipient key fingerprint does not match.")
|
||||
|
||||
p.add_argument(
|
||||
"--sol-profile",
|
||||
@@ -970,7 +999,7 @@ def main() -> None:
|
||||
|
||||
try:
|
||||
if args.cmd == "fetchkey":
|
||||
cmd_fetchkey(args.url, args.out, args.timeout, args.off_screen)
|
||||
cmd_fetchkey(args.url, args.out, args.timeout, args.off_screen, args.expected_fingerprint)
|
||||
return
|
||||
if args.cmd == "gen":
|
||||
cmd_gen(args)
|
||||
|
||||
Reference in New Issue
Block a user