Use HMAC DRNG for RSA PGP keys

This commit is contained in:
thePR0M3TH3AN
2025-08-03 09:24:50 -04:00
parent 4f09ad5c26
commit aad41929bf
2 changed files with 32 additions and 4 deletions

View File

@@ -482,7 +482,13 @@ def derive_seed_phrase(bip85: BIP85, idx: int, words: int = 24) -> str:
def derive_pgp_key(
bip85: BIP85, idx: int, key_type: str = "ed25519", user_id: str = ""
) -> tuple[str, str]:
"""Derive a deterministic PGP private key and return it with its fingerprint."""
"""Derive a deterministic PGP private key and return it with its fingerprint.
For RSA keys the randomness required during key generation is provided by
an HMAC-SHA256 based deterministic generator seeded from the BIP-85
entropy. This avoids use of Python's ``random`` module while ensuring the
output remains stable across Python versions.
"""
from pgpy import PGPKey, PGPUID
from pgpy.packet.packets import PrivKeyV4
@@ -514,14 +520,18 @@ def derive_pgp_key(
if key_type.lower() == "rsa":
class DRNG:
"""HMAC-SHA256 based deterministic random generator."""
def __init__(self, seed: bytes) -> None:
self.seed = seed
self.key = seed
self.counter = 0
def __call__(self, n: int) -> bytes: # pragma: no cover - deterministic
out = b""
while len(out) < n:
self.seed = hashlib.sha256(self.seed).digest()
out += self.seed
msg = self.counter.to_bytes(4, "big")
out += hmac.new(self.key, msg, hashlib.sha256).digest()
self.counter += 1
return out[:n]
rsa_key = RSA.generate(2048, randfunc=DRNG(entropy))

View File

@@ -39,3 +39,21 @@ def test_pgp_key_determinism():
entry = data["entries"][str(idx)]
assert entry["key_type"] == "ed25519"
assert entry["user_id"] == "Test"
def test_pgp_rsa_key_determinism():
"""RSA PGP keys should be derived deterministically."""
with TemporaryDirectory() as tmpdir:
tmp_path = Path(tmpdir)
vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD)
cfg_mgr = ConfigManager(vault, tmp_path)
backup_mgr = BackupManager(tmp_path, cfg_mgr)
entry_mgr = EntryManager(vault, backup_mgr)
idx = entry_mgr.add_pgp_key("pgp", TEST_SEED, key_type="rsa", user_id="Test")
key1, fp1 = entry_mgr.get_pgp_key(idx, TEST_SEED)
key2, fp2 = entry_mgr.get_pgp_key(idx, TEST_SEED)
assert fp1 == fp2
assert key1 == key2