Skip to content

Commit 3a5a55c

Browse files
committed
tests: generate DKIM RSA keys on the fly via cryptography
Drop the hardcoded 1024-bit PRIV_KEY/PUB_KEY constants and the dead _generate_key() fallback (Crypto.PublicKey was never installed in test deps). A session-scoped fixture now mints a fresh 2048-bit keypair using the cryptography package, fixing CodeQL py/weak-crypto-key alert #7.
1 parent e18d04b commit 3a5a55c

2 files changed

Lines changed: 35 additions & 58 deletions

File tree

emails/testsuite/message/test_dkim.py

Lines changed: 34 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,45 @@
1-
import os
2-
import email
31
import pytest
42
import emails
53
from emails import Message
64
from io import StringIO
75

86
from emails.exc import DKIMException
9-
from emails.utils import load_email_charsets
107
import dkim
8+
from cryptography.hazmat.primitives import serialization
9+
from cryptography.hazmat.primitives.asymmetric import rsa
1110
from .helpers import common_email_data
1211

1312

13+
@pytest.fixture(scope="session")
14+
def dkim_keys():
15+
"""Fresh 2048-bit RSA keypair for DKIM tests.
1416
15-
PRIV_KEY = b"""-----BEGIN RSA PRIVATE KEY-----
16-
MIICXAIBAAKBgQDKHKzbg7LwpSJVfy9h8YQciVuIiexJ6OKJcCc6akJuLx+qPJGr
17-
t0chdV92slT9Lm1DUAjQEd8r9kVKa8FrWrnThMWx5HoXkGOIW2NqC0vrTZUgvhWy
18-
mlnwiysIylCirStZvA2uszYiFQK8slYD3H25UFTIOqLgB6AvV6URo26iJQIDAQAB
19-
AoGAOHt5B0Ov3zaW+MO5byq6m+r7DJZW1XTi0jvoipelhvteYwnYP9/RXhVaH2bI
20-
/5RY7qXQQK2t67BAPwMMI79QDL+jWsgwE0hly/qloOgEuX1+D/yGBShlYNQXvjAY
21-
UgkNYtp5JBVr8byz7upzvIyDsWJGoUrBindYnEiAVgwzZuECQQDKsKRwQhTCOZjW
22-
tkrockxDKMlXyKRLpOdqmwH0hwUdcWklxlmE+IJz4NVlz5qCVJz/oT+TgBNex8I5
23-
spxWAmdNAkEA/0UdnlXYueGVDIe5SUQGlXb8U8fTYtA/NsduFwq8QEWMrVBXK+uH
24-
4upq70kFlyfP5mpTOZwUgY2jH/qrXD8qOQJAdx1L5bTP4jxa94N1jhjtfGJRwMbm
25-
1pV4cgvaIEvg06a8djiUjzJD57lvbz+Lu5/iC9BFPnd76q1WFPZELb+H2QJBAK8y
26-
DWDlBEiW5QfjgqwhDu+36PfLNm4kBK6g8xLHYGowEZvFfv56uRloz5mIoVibj1lR
27-
ceshDwXXYrSJAuDdzSkCQDkx2TeKLUqKSxJNUYSrakQIo/41AOFvFBTbJuH3RZoy
28-
W/1DFMld7rC2gVHYW3m/LNd1qbi5QR9/buGxE7Y8ylI=
29-
-----END RSA PRIVATE KEY-----"""
30-
31-
PUB_KEY = b"""MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKHKzbg7LwpSJVfy9h8YQciVuI
32-
iexJ6OKJcCc6akJuLx+qPJGrt0chdV92slT9Lm1DUAjQEd8r9kVKa8FrWrnThMWx
33-
5HoXkGOIW2NqC0vrTZUgvhWymlnwiysIylCirStZvA2uszYiFQK8slYD3H25UFTI
34-
OqLgB6AvV6URo26iJQIDAQAB"""
35-
36-
37-
def _generate_key(length=1024):
38-
# From: http://stackoverflow.com/questions/3504955/using-rsa-in-python
39-
try:
40-
from Crypto.PublicKey import RSA
41-
key = RSA.generate(length)
42-
return key.exportKey(), key.publickey().exportKey()
43-
except ImportError:
44-
return PRIV_KEY, PUB_KEY
45-
46-
47-
def _check_dkim(message, pub_key=PUB_KEY):
17+
Returns (private_key_pem, public_key_pem) as bytes.
18+
Generated once per test session — RSA keygen is slow (~100 ms).
19+
"""
20+
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
21+
priv_pem = key.private_bytes(
22+
encoding=serialization.Encoding.PEM,
23+
format=serialization.PrivateFormat.TraditionalOpenSSL,
24+
encryption_algorithm=serialization.NoEncryption(),
25+
)
26+
pub_pem = key.public_key().public_bytes(
27+
encoding=serialization.Encoding.PEM,
28+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
29+
)
30+
return priv_pem, pub_pem
31+
32+
33+
def _check_dkim(message, pub_key):
4834
def _plain_public_key(s):
4935
return b"".join([l for l in s.split(b'\n') if not l.startswith(b'---')])
5036
message = message.as_string()
5137
o = dkim.DKIM(message=message.encode())
5238
return o.verify(dnsfunc=lambda name, **kw: b"".join([b"v=DKIM1; p=", _plain_public_key(pub_key)]))
5339

5440

55-
def test_dkim():
56-
57-
priv_key, pub_key = _generate_key(length=1024)
41+
def test_dkim(dkim_keys):
42+
priv_key, pub_key = dkim_keys
5843

5944
DKIM_PARAMS = [dict(key=StringIO(priv_key.decode()),
6045
selector='_dkim',
@@ -75,21 +60,13 @@ def test_dkim():
7560
message.dkim(**dkimparams)
7661
# check DKIM header exist
7762
assert message.as_message()['DKIM-Signature']
78-
#print(__name__, "type message.as_string()==", type(message.as_string()))
79-
#print(message.as_string())
80-
#print(type(message.as_string()))
81-
#print(email.__file__)
82-
#print(email.charset.CHARSETS)
83-
#print('adding utf-8 charset...')
84-
#email.charset.add_charset('utf-8', email.charset.BASE64, email.charset.BASE64)
85-
#print(email.charset.CHARSETS)
8663
assert 'DKIM-Signature: ' in message.as_string()
8764
assert _check_dkim(message, pub_key)
88-
#assert 0
8965

9066

9167

92-
def test_dkim_error():
68+
def test_dkim_error(dkim_keys):
69+
priv_key, _ = dkim_keys
9370

9471
m = emails.html(**common_email_data())
9572

@@ -110,7 +87,7 @@ def test_dkim_error():
11087

11188
# Error on invalid dkim parameters
11289

113-
m.dkim(key=PRIV_KEY,
90+
m.dkim(key=priv_key,
11491
selector='_dkim',
11592
domain='somewhere.net',
11693
include_headers=['To'])
@@ -120,7 +97,7 @@ def test_dkim_error():
12097
m.as_string()
12198

12299
# Skip error on ignore_sign_errors=True
123-
m.dkim(key=PRIV_KEY,
100+
m.dkim(key=priv_key,
124101
selector='_dkim',
125102
domain='somewhere.net',
126103
ignore_sign_errors=True,
@@ -130,19 +107,18 @@ def test_dkim_error():
130107
m.as_message()
131108

132109

133-
def test_dkim_as_bytes():
134-
135-
priv_key, pub_key = _generate_key(length=1024)
110+
def test_dkim_as_bytes(dkim_keys):
111+
priv_key, _ = dkim_keys
136112
message = Message(**common_email_data())
137113
message.dkim(key=priv_key, selector='_dkim', domain='somewhere.net')
138114
result = message.as_bytes()
139115
assert isinstance(result, bytes)
140116
assert b'DKIM-Signature: ' in result
141117

142118

143-
def test_dkim_sign_after_error():
119+
def test_dkim_sign_after_error(dkim_keys):
144120
"""After a sign error with ignore_sign_errors, normal signing still works."""
145-
priv_key, pub_key = _generate_key(length=1024)
121+
priv_key, pub_key = dkim_keys
146122

147123
# First: sign with invalid include_headers (missing From), error ignored
148124
m1 = Message(**common_email_data())
@@ -156,14 +132,14 @@ def test_dkim_sign_after_error():
156132
assert _check_dkim(m2, pub_key)
157133

158134

159-
def test_dkim_sign_twice():
135+
def test_dkim_sign_twice(dkim_keys):
160136

161137
# Test #44:
162138
# " if you put the open there and send more than one messages it fails
163139
# (the first works but the next will not if you dont seek(0) the dkim file first)"
164140
# Actually not.
141+
priv_key, pub_key = dkim_keys
165142

166-
priv_key, pub_key = _generate_key(length=1024)
167143
message = Message(**common_email_data())
168144
message.dkim(key=StringIO(priv_key.decode()), selector='_dkim', domain='somewhere.net')
169145
for n in range(2):

requirements/tests-base.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pytest-cov
66
pytest-asyncio
77
html5lib
88
aiosmtplib
9+
cryptography

0 commit comments

Comments
 (0)