diff --git a/emails/signers.py b/emails/signers.py index 47a61e9..a3e796f 100644 --- a/emails/signers.py +++ b/emails/signers.py @@ -28,7 +28,8 @@ def __init__(self, selector: str, domain: str, key: str | bytes | IO[bytes] | No # Normalize to bytes privkey_bytes = privkey if isinstance(privkey, bytes) else str(privkey).encode() - # Validate key by attempting to parse it + # Validate key upfront; dkim.sign() re-parses PEM on each call + # but the cost is negligible vs the RSA operation (~0ms vs ~2.5ms). try: dkim.crypto.parse_pem_private_key(privkey_bytes) except UnparsableKeyError as exc: diff --git a/emails/testsuite/message/test_dkim.py b/emails/testsuite/message/test_dkim.py index 87a7b89..1ad4821 100644 --- a/emails/testsuite/message/test_dkim.py +++ b/emails/testsuite/message/test_dkim.py @@ -140,6 +140,22 @@ def test_dkim_as_bytes(): assert b'DKIM-Signature: ' in result +def test_dkim_sign_after_error(): + """After a sign error with ignore_sign_errors, normal signing still works.""" + priv_key, pub_key = _generate_key(length=1024) + + # First: sign with invalid include_headers (missing From), error ignored + m1 = Message(**common_email_data()) + m1.dkim(key=priv_key, selector='_dkim', domain='somewhere.net', + ignore_sign_errors=True, include_headers=['To']) + m1.as_string() # should not raise + + # Second: normal sign with same key must still work + m2 = Message(**common_email_data()) + m2.dkim(key=priv_key, selector='_dkim', domain='somewhere.net') + assert _check_dkim(m2, pub_key) + + def test_dkim_sign_twice(): # Test #44: diff --git a/requirements/base.txt b/requirements/base.txt index 466b8f5..c70ac4a 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -5,3 +5,4 @@ python-dateutil requests premailer>=2.8.3 puremagic +dkimpy