feat: add post-quantum ML-DSA crypt module (PqcSigner and PqcVerifier) - #18130
feat: add post-quantum ML-DSA crypt module (PqcSigner and PqcVerifier)#18130ohmayr wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces post-quantum ML-DSA (Module-Lattice-Based Digital Signature Algorithm) signing and verification capabilities to the google-auth library, adding the PqcSigner and PqcVerifier classes under a new pqc module along with corresponding unit tests. The review feedback suggests optimizing the is_mldsa_key helper to avoid unnecessary parsing overhead on raw DER bytes, enhancing safety in __setstate__ by copying the state dictionary and using explicit keyword arguments, and improving test robustness by replacing a lambda mock with mock.Mock.
| if any(oid in key_bytes for oid in _MLDSA_OIDS): | ||
| return True | ||
|
|
||
| try: | ||
| import base64 | ||
|
|
||
| pem_body = b"".join( | ||
| line.strip() | ||
| for line in key_bytes.splitlines() | ||
| if not line.startswith(b"-----") | ||
| ) | ||
| der = base64.b64decode(pem_body) | ||
| return any(oid in der for oid in _MLDSA_OIDS) | ||
| except Exception: | ||
| return False |
There was a problem hiding this comment.
Optimizing is_mldsa_key by checking if the input contains b"-----BEGIN" before attempting to split lines and base64-decode it. This avoids unnecessary overhead and potential false positives/errors when the input is already raw DER bytes.
| if any(oid in key_bytes for oid in _MLDSA_OIDS): | |
| return True | |
| try: | |
| import base64 | |
| pem_body = b"".join( | |
| line.strip() | |
| for line in key_bytes.splitlines() | |
| if not line.startswith(b"-----") | |
| ) | |
| der = base64.b64decode(pem_body) | |
| return any(oid in der for oid in _MLDSA_OIDS) | |
| except Exception: | |
| return False | |
| if b"-----BEGIN" in key_bytes: | |
| try: | |
| import base64 | |
| pem_body = b"".join( | |
| line.strip() | |
| for line in key_bytes.splitlines() | |
| if not line.startswith(b"-----") | |
| ) | |
| der = base64.b64decode(pem_body) | |
| return any(oid in der for oid in _MLDSA_OIDS) | |
| except Exception: | |
| return False | |
| return any(oid in key_bytes for oid in _MLDSA_OIDS) |
| def __setstate__(self, state: Dict[str, Any]) -> None: | ||
| """Pickle helper that deserializes the _key attribute.""" | ||
| if mldsa is None: | ||
| raise RuntimeError(_UPGRADE_ERROR) | ||
| state["_key"] = serialization.load_pem_private_key(state["_key"], None) | ||
| self.__dict__.update(state) |
There was a problem hiding this comment.
Improve consistency and safety in __setstate__ by copying the state dictionary to avoid mutating the unpickled state in-place, and passing explicit keyword arguments (password=None and backend=_BACKEND) to load_pem_private_key to match the implementation in from_string.
| def __setstate__(self, state: Dict[str, Any]) -> None: | |
| """Pickle helper that deserializes the _key attribute.""" | |
| if mldsa is None: | |
| raise RuntimeError(_UPGRADE_ERROR) | |
| state["_key"] = serialization.load_pem_private_key(state["_key"], None) | |
| self.__dict__.update(state) | |
| def __setstate__(self, state: Dict[str, Any]) -> None: | |
| """Pickle helper that deserializes the _key attribute.""" | |
| if mldsa is None: | |
| raise RuntimeError(_UPGRADE_ERROR) | |
| state = state.copy() | |
| state["_key"] = serialization.load_pem_private_key( | |
| state["_key"], password=None, backend=_BACKEND | |
| ) | |
| self.__dict__.update(state) |
| monkeypatch.setattr( | ||
| pqc.serialization, | ||
| "load_pem_private_key", | ||
| lambda pem, pw: loaded_key, | ||
| ) |
There was a problem hiding this comment.
Update the mock for load_pem_private_key to use mock.Mock instead of a lambda with a fixed signature. This makes the test robust against changes to the arguments passed to load_pem_private_key (such as passing keyword arguments like password and backend).
| monkeypatch.setattr( | |
| pqc.serialization, | |
| "load_pem_private_key", | |
| lambda pem, pw: loaded_key, | |
| ) | |
| monkeypatch.setattr( | |
| pqc.serialization, | |
| "load_pem_private_key", | |
| mock.Mock(return_value=loaded_key), | |
| ) |
b03e9e1 to
f602d58
Compare
f602d58 to
fe7dbe4
Compare
WIP