TheAlgorithms/Python · error · ValueError
Invalid public key
Error message
Invalid public key
What it means
Raised by DiffieHellman.generate_shared_key when the peer's public key fails the NIST SP800-56 validity check in is_valid_public_key: it must lie in [2, p-2] and be a quadratic residue (key^((p-1)/2) mod p == 1). This blocks small-subgroup and invalid-key attacks.
Source
Thrown at ciphers/diffie_hellman.py:239
def get_private_key(self) -> str:
return hex(self.__private_key)[2:]
def generate_public_key(self) -> str:
public_key = pow(self.generator, self.__private_key, self.prime)
return hex(public_key)[2:]
def is_valid_public_key(self, key: int) -> bool:
# check if the other public key is valid based on NIST SP800-56
return (
2 <= key <= self.prime - 2
and pow(key, (self.prime - 1) // 2, self.prime) == 1
)
def generate_shared_key(self, other_key_str: str) -> str:
other_key = int(other_key_str, base=16)
if not self.is_valid_public_key(other_key):
raise ValueError("Invalid public key")
shared_key = pow(other_key, self.__private_key, self.prime)
return sha256(str(shared_key).encode()).hexdigest()
@staticmethod
def is_valid_public_key_static(remote_public_key_str: int, prime: int) -> bool:
# check if the other public key is valid based on NIST SP800-56
return (
2 <= remote_public_key_str <= prime - 2
and pow(remote_public_key_str, (prime - 1) // 2, prime) == 1
)
@staticmethod
def generate_shared_key_static(
local_private_key_str: str, remote_public_key_str: str, group: int = 14
) -> str:
local_private_key = int(local_private_key_str, base=16)
remote_public_key = int(remote_public_key_str, base=16)
prime = primes[group]["prime"]View on GitHub (pinned to f5988cc097)
Solutions
- Verify both parties constructed DiffieHellman with the same group
- Regenerate the peer key from a real generate_public_key() output (full hex, no 0x prefix)
- Call is_valid_public_key(int(peer_hex, 16)) yourself and drop the connection/abort on failure
Example fix
# before
shared = dh.generate_shared_key(peer_hex) # raises if peer_hex is invalid
# after
peer_int = int(peer_hex, 16)
if not dh.is_valid_public_key(peer_int):
raise SystemExit("peer sent an invalid public key")
shared = dh.generate_shared_key(peer_hex) Defensive patterns
Strategy: validation
Validate before calling
peer_int = int(peer_hex, 16)
if not dh.is_valid_public_key(peer_int):
raise ValueError("rejecting invalid peer public key") Try / catch
try:
shared = dh.generate_shared_key(peer_hex)
except ValueError:
abort_handshake("peer public key failed NIST SP800-56 validation") Prevention
- Pre-validate with is_valid_public_key before computing the shared key
- Ensure both peers use the same group number
- Treat invalid keys as a possible attack, not a retryable error — abort the handshake
When it happens
Trigger: Passing a hex string that decodes to 0, 1, or p-1; passing a value not generated by pow(g, private, p) in the same group; truncated or corrupted hex from the peer; wrong group on one side.
Common situations: Hand-rolled transports that mangle leading zeros in hex keys; peers using different group numbers; test code with arbitrary hex strings instead of real public keys; MITM/tampered payloads.
Related errors
- Unsupported Group
- number must be positive
- The value of input must be non-negative
- Input list must contain at least two elements
- Inputs and select signal must be 0 or 1
AI-assisted analysis of TheAlgorithms/Python@f5988cc097 (2026-08-14).
Data as JSON: /api/errors/a8771b9b815fbec6.
Report an issue: GitHub.