mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 07:48:57 +00:00
Use HMAC DRNG for RSA PGP keys
This commit is contained in:
@@ -482,7 +482,13 @@ def derive_seed_phrase(bip85: BIP85, idx: int, words: int = 24) -> str:
|
|||||||
def derive_pgp_key(
|
def derive_pgp_key(
|
||||||
bip85: BIP85, idx: int, key_type: str = "ed25519", user_id: str = ""
|
bip85: BIP85, idx: int, key_type: str = "ed25519", user_id: str = ""
|
||||||
) -> tuple[str, 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 import PGPKey, PGPUID
|
||||||
from pgpy.packet.packets import PrivKeyV4
|
from pgpy.packet.packets import PrivKeyV4
|
||||||
@@ -514,14 +520,18 @@ def derive_pgp_key(
|
|||||||
if key_type.lower() == "rsa":
|
if key_type.lower() == "rsa":
|
||||||
|
|
||||||
class DRNG:
|
class DRNG:
|
||||||
|
"""HMAC-SHA256 based deterministic random generator."""
|
||||||
|
|
||||||
def __init__(self, seed: bytes) -> None:
|
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
|
def __call__(self, n: int) -> bytes: # pragma: no cover - deterministic
|
||||||
out = b""
|
out = b""
|
||||||
while len(out) < n:
|
while len(out) < n:
|
||||||
self.seed = hashlib.sha256(self.seed).digest()
|
msg = self.counter.to_bytes(4, "big")
|
||||||
out += self.seed
|
out += hmac.new(self.key, msg, hashlib.sha256).digest()
|
||||||
|
self.counter += 1
|
||||||
return out[:n]
|
return out[:n]
|
||||||
|
|
||||||
rsa_key = RSA.generate(2048, randfunc=DRNG(entropy))
|
rsa_key = RSA.generate(2048, randfunc=DRNG(entropy))
|
||||||
|
@@ -39,3 +39,21 @@ def test_pgp_key_determinism():
|
|||||||
entry = data["entries"][str(idx)]
|
entry = data["entries"][str(idx)]
|
||||||
assert entry["key_type"] == "ed25519"
|
assert entry["key_type"] == "ed25519"
|
||||||
assert entry["user_id"] == "Test"
|
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
|
||||||
|
Reference in New Issue
Block a user