Skip to content

Commit 7323f5c

Browse files
committed
Always generate a random IV for AES operations
Quoting @obi1kenobi: > Initialization vector reuse like this is a security concern, since it leaks > information about the encrypted data to attackers, regardless of the > encryption mode used. > Instead of relying on a fixed, randomly-generated IV, it would be better to > randomly-generate a new IV for every encryption operation. Breaks AESCipher ECB support Reported as CVE-2017-1000246 Fixes #417 Signed-off-by: Ivan Kanakarakis <[email protected]>
1 parent d5e4e1b commit 7323f5c

File tree

3 files changed

+12
-31
lines changed

3 files changed

+12
-31
lines changed

src/saml2/aes.py

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,27 @@
1111
POSTFIX_MODE = {
1212
'cbc': modes.CBC,
1313
'cfb': modes.CFB,
14-
'ecb': modes.ECB,
1514
}
1615

1716
AES_BLOCK_SIZE = int(algorithms.AES.block_size / 8)
1817

1918

2019
class AESCipher(object):
21-
def __init__(self, key, iv=None):
20+
def __init__(self, key):
2221
"""
2322
:param key: The encryption key
24-
:param iv: Init vector
2523
:return: AESCipher instance
2624
"""
2725
self.key = key
28-
self.iv = iv
2926

30-
def build_cipher(self, iv=None, alg='aes_128_cbc'):
27+
def build_cipher(self, alg='aes_128_cbc'):
3128
"""
32-
:param iv: init vector
3329
:param alg: cipher algorithm
3430
:return: A Cipher instance
3531
"""
3632
typ, bits, cmode = alg.lower().split('_')
3733
bits = int(bits)
38-
39-
if not iv:
40-
if self.iv:
41-
iv = self.iv
42-
else:
43-
iv = os.urandom(AES_BLOCK_SIZE)
34+
iv = os.urandom(AES_BLOCK_SIZE)
4435

4536
if len(iv) != AES_BLOCK_SIZE:
4637
raise Exception('Wrong iv size: {}'.format(len(iv)))
@@ -63,11 +54,10 @@ def build_cipher(self, iv=None, alg='aes_128_cbc'):
6354

6455
return cipher, iv
6556

66-
def encrypt(self, msg, iv=None, alg='aes_128_cbc', padding='PKCS#7',
67-
b64enc=True, block_size=AES_BLOCK_SIZE):
57+
def encrypt(self, msg, alg='aes_128_cbc', padding='PKCS#7', b64enc=True,
58+
block_size=AES_BLOCK_SIZE):
6859
"""
6960
:param key: The encryption key
70-
:param iv: init vector
7161
:param msg: Message to be encrypted
7262
:param padding: Which padding that should be used
7363
:param b64enc: Whether the result should be base64encoded
@@ -87,7 +77,7 @@ def encrypt(self, msg, iv=None, alg='aes_128_cbc', padding='PKCS#7',
8777
c = chr(plen).encode()
8878
msg += c * plen
8979

90-
cipher, iv = self.build_cipher(iv, alg)
80+
cipher, iv = self.build_cipher(alg)
9181
encryptor = cipher.encryptor()
9282
cmsg = iv + encryptor.update(msg) + encryptor.finalize()
9383

@@ -98,20 +88,15 @@ def encrypt(self, msg, iv=None, alg='aes_128_cbc', padding='PKCS#7',
9888

9989
return enc_msg
10090

101-
def decrypt(self, msg, iv=None, alg='aes_128_cbc', padding='PKCS#7',
102-
b64dec=True):
91+
def decrypt(self, msg, alg='aes_128_cbc', padding='PKCS#7', b64dec=True):
10392
"""
10493
:param key: The encryption key
105-
:param iv: init vector
10694
:param msg: Base64 encoded message to be decrypted
10795
:return: The decrypted message
10896
"""
10997
data = b64decode(msg) if b64dec else msg
11098

111-
_iv = data[:AES_BLOCK_SIZE]
112-
if iv:
113-
assert iv == _iv
114-
cipher, iv = self.build_cipher(iv, alg=alg)
99+
cipher, iv = self.build_cipher(alg=alg)
115100
decryptor = cipher.decryptor()
116101
res = decryptor.update(data)[AES_BLOCK_SIZE:] + decryptor.finalize()
117102
if padding in ['PKCS#5', 'PKCS#7']:
@@ -122,20 +107,19 @@ def decrypt(self, msg, iv=None, alg='aes_128_cbc', padding='PKCS#7',
122107

123108
def run_test():
124109
key = b'1234523451234545' # 16 byte key
125-
iv = os.urandom(AES_BLOCK_SIZE)
126110
# Iff padded, the message doesn't have to be multiple of 16 in length
127111
original_msg = b'ToBeOrNotTobe W.S.'
128112
aes = AESCipher(key)
129113

130-
encrypted_msg = aes.encrypt(original_msg, iv)
131-
decrypted_msg = aes.decrypt(encrypted_msg, iv)
114+
encrypted_msg = aes.encrypt(original_msg)
115+
decrypted_msg = aes.decrypt(encrypted_msg)
132116
assert decrypted_msg == original_msg
133117

134118
encrypted_msg = aes.encrypt(original_msg)
135119
decrypted_msg = aes.decrypt(encrypted_msg)
136120
assert decrypted_msg == original_msg
137121

138-
aes = AESCipher(key, iv)
122+
aes = AESCipher(key)
139123
encrypted_msg = aes.encrypt(original_msg)
140124
decrypted_msg = aes.decrypt(encrypted_msg)
141125
assert decrypted_msg == original_msg

src/saml2/authn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def __init__(self, srv, mako_template, template_lookup, pwd, return_to):
120120
self.return_to = return_to
121121
self.active = {}
122122
self.query_param = "upm_answer"
123-
self.aes = AESCipher(self.srv.symkey.encode(), srv.iv)
123+
self.aes = AESCipher(self.srv.symkey.encode())
124124

125125
def __call__(self, cookie=None, policy_url=None, logo_url=None,
126126
query="", **kwargs):

src/saml2/server.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,9 @@ def __init__(self, config_file="", config=None, cache=None, stype="idp",
8383
self.init_config(stype)
8484
self.cache = cache
8585
self.ticket = {}
86-
#
8786
self.session_db = self.choose_session_storage()
88-
# Needed for
8987
self.symkey = symkey
9088
self.seed = rndstr()
91-
self.iv = os.urandom(16)
9289
self.lock = threading.Lock()
9390

9491
def getvalid_certificate_str(self):

0 commit comments

Comments
 (0)