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
|
# 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:
|
def get_master_fingerprint(seed_bytes: bytes) -> str:
|
||||||
@@ -315,12 +328,20 @@ def sha256_hex_bytes(data: bytes) -> str:
|
|||||||
return hashlib.sha256(data).hexdigest()
|
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()
|
require_for_fetchkey()
|
||||||
armored, raw = fetch_ascii_armored_text(url, timeout=timeout)
|
armored, raw = fetch_ascii_armored_text(url, timeout=timeout)
|
||||||
|
|
||||||
s256 = sha256_hex_bytes(raw)
|
s256 = sha256_hex_bytes(raw)
|
||||||
fpr = pgp_fingerprint(armored)
|
fpr = pgp_fingerprint(armored)
|
||||||
|
require_fingerprint_match(fpr, expected_fingerprint, "fetchkey")
|
||||||
|
|
||||||
temp_file = None
|
temp_file = None
|
||||||
if off_screen:
|
if off_screen:
|
||||||
@@ -699,7 +720,8 @@ def cmd_gen(args) -> None:
|
|||||||
if is_pgp:
|
if is_pgp:
|
||||||
with open(args.pgp_pubkey_file, "r", encoding="utf-8") as f:
|
with open(args.pgp_pubkey_file, "r", encoding="utf-8") as f:
|
||||||
pub = f.read()
|
pub = f.read()
|
||||||
|
fpr = pgp_fingerprint(pub)
|
||||||
|
require_fingerprint_match(fpr, args.expected_fingerprint, "encrypt(gen)")
|
||||||
payload = build_payload_gen(
|
payload = build_payload_gen(
|
||||||
mnemonic=mnemonic,
|
mnemonic=mnemonic,
|
||||||
passphrase_used=bool(passphrase),
|
passphrase_used=bool(passphrase),
|
||||||
@@ -802,7 +824,11 @@ def cmd_recover(args) -> None:
|
|||||||
if is_pgp:
|
if is_pgp:
|
||||||
with open(args.pgp_pubkey_file, "r", encoding="utf-8") as f:
|
with open(args.pgp_pubkey_file, "r", encoding="utf-8") as f:
|
||||||
pub = f.read()
|
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(
|
payload = build_payload_recover(
|
||||||
fp=fp,
|
fp=fp,
|
||||||
sol_profile=args.sol_profile,
|
sol_profile=args.sol_profile,
|
||||||
@@ -902,6 +928,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Enable off-screen mode: no printing of sensitive data to stdout.",
|
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:
|
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).")
|
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-pubkey-file", default=None)
|
||||||
p.add_argument("--pgp-ignore-usage-flags", action="store_true")
|
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(
|
p.add_argument(
|
||||||
"--sol-profile",
|
"--sol-profile",
|
||||||
@@ -970,7 +999,7 @@ def main() -> None:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if args.cmd == "fetchkey":
|
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
|
return
|
||||||
if args.cmd == "gen":
|
if args.cmd == "gen":
|
||||||
cmd_gen(args)
|
cmd_gen(args)
|
||||||
|
|||||||
Reference in New Issue
Block a user