Skip to content

Commit 82734df

Browse files
committed
Fixed AES CBC encryption.
1 parent 22ba03e commit 82734df

File tree

3 files changed

+125
-74
lines changed

3 files changed

+125
-74
lines changed

src/cryptojwt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from binascii import unhexlify
1919

20-
__version__ = '0.2.0'
20+
__version__ = '0.3.0'
2121

2222
logger = logging.getLogger(__name__)
2323

src/cryptojwt/jwe.py

Lines changed: 121 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# from future import standard_library
22
import os
33

4+
45
try:
56
from builtins import object
67
except ImportError:
@@ -16,10 +17,13 @@
1617
from cryptography.hazmat.primitives import hashes
1718
from cryptography.hazmat.primitives.asymmetric import ec
1819
from cryptography.hazmat.primitives.asymmetric import padding
20+
from cryptography.hazmat.primitives.ciphers import algorithms
21+
from cryptography.hazmat.primitives.ciphers import Cipher
22+
from cryptography.hazmat.primitives.ciphers import modes
1923
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
20-
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
2124
from cryptography.hazmat.primitives.keywrap import aes_key_unwrap
2225
from cryptography.hazmat.primitives.keywrap import aes_key_wrap
26+
from cryptography.hazmat.primitives.padding import PKCS7
2327

2428
from cryptojwt import as_bytes, b64encode_item
2529
from cryptojwt import as_unicode
@@ -156,6 +160,53 @@ def decrypt(self, ciphertext, key, sign_padding="pkcs1_padding"):
156160
return text
157161

158162

163+
class AES_CBCEncrypter(Encrypter):
164+
"""
165+
"""
166+
def __init__(self, key_len=32, key=None, msg_padding='PKCS7'):
167+
Encrypter.__init__(self)
168+
if key:
169+
self.key = key
170+
else:
171+
self.key = os.urandom(key_len)
172+
173+
if msg_padding == 'PKCS7':
174+
self.padder = PKCS7(128).padder()
175+
self.unpadder = PKCS7(128).unpadder()
176+
177+
def encrypt(self, msg, iv='', auth_data=b''):
178+
if not iv:
179+
iv = os.urandom(16)
180+
181+
cipher = Cipher(algorithms.AES(self.key), modes.CBC(iv),
182+
backend=default_backend())
183+
encryptor = cipher.encryptor()
184+
if auth_data:
185+
encryptor.authenticate_additional_data(auth_data)
186+
187+
pmsg = self.padder.update(msg)
188+
pmsg += self.padder.finalize()
189+
ct = encryptor.update(pmsg)
190+
ct += encryptor.finalize()
191+
return ct
192+
193+
def decrypt(self, msg, key=None, iv='', auth_data=b'',
194+
padding='PKCS7'):
195+
if key is None:
196+
key = self.key
197+
198+
cipher = Cipher(algorithms.AES(key), modes.CBC(iv),
199+
backend=default_backend())
200+
decryptor = cipher.decryptor()
201+
if auth_data:
202+
decryptor.authenticate_additional_data(auth_data)
203+
ctext = decryptor.update(msg)
204+
ctext += decryptor.finalize()
205+
unpad = self.unpadder.update(ctext)
206+
unpad += self.unpadder.finalize()
207+
return unpad
208+
209+
159210
class AES_GCMEncrypter(Encrypter):
160211
def __init__(self, bit_length=0, key=None):
161212
Encrypter.__init__(self)
@@ -167,7 +218,7 @@ def __init__(self, bit_length=0, key=None):
167218

168219
self.key = AESGCM.generate_key(bit_length=bit_length)
169220

170-
def encrypt(self, msg, iv='', ass_data=None):
221+
def encrypt(self, msg, iv='', auth_data=None):
171222
"""
172223
Encrypts and authenticates the data provided as well as authenticating
173224
the associated_data.
@@ -180,9 +231,9 @@ def encrypt(self, msg, iv='', ass_data=None):
180231
if not iv:
181232
raise ValueError('Missing Nonce')
182233

183-
return self.key.encrypt(iv, msg, ass_data)
234+
return self.key.encrypt(iv, msg, auth_data)
184235

185-
def decrypt(self, ciphertext, iv='', ass_data=None):
236+
def decrypt(self, ciphertext, iv='', auth_data=None):
186237
"""
187238
Decrypts the data and authenticates the associated_data (if provided).
188239
@@ -194,48 +245,48 @@ def decrypt(self, ciphertext, iv='', ass_data=None):
194245
if not iv:
195246
raise ValueError('Missing Nonce')
196247

197-
return self.key.decrypt(iv, ciphertext, ass_data)
198-
248+
return self.key.decrypt(iv, ciphertext, auth_data)
199249

200-
class AES_CCMEncrypter(Encrypter):
201-
def __init__(self, bit_length=0, key=None, tag_length=16):
202-
Encrypter.__init__(self)
203-
if key:
204-
self.key = AESCCM(key)
205-
elif bit_length:
206-
if bit_length not in [128, 192, 256]:
207-
raise UnsupportedBitLength(bit_length)
208-
209-
self.key = AESCCM.generate_key(bit_length=bit_length)
210250

211-
def encrypt(self, msg, iv='', ass_data=None):
212-
"""
213-
Encrypts and authenticates the data provided as well as authenticating
214-
the associated_data.
215-
216-
:param msg: The message to be encrypted
217-
:param iv: MUST be present. A value of between 7 and 13 bytes.
218-
:param ass_data: Associated data
219-
:return: The ciphertext bytes with the 16 byte tag appended.
220-
"""
221-
if not iv:
222-
raise ValueError('Missing Nonce')
223-
224-
return self.key.encrypt(iv, msg, ass_data)
225-
226-
def decrypt(self, ciphertext, iv='', ass_data=None):
227-
"""
228-
Decrypts the data and authenticates the associated_data (if provided).
229-
230-
:param ciphertext: The data to decrypt including tag
231-
:param iv: A value of between 7 and 13 bytes.
232-
:param ass_data: Associated data
233-
:return: The original plaintext
234-
"""
235-
if not iv:
236-
raise ValueError('Missing Nonce')
237-
238-
return self.key.decrypt(iv, ciphertext, ass_data)
251+
# class AES_CCMEncrypter(Encrypter):
252+
# def __init__(self, bit_length=0, key=None, tag_length=16):
253+
# Encrypter.__init__(self)
254+
# if key:
255+
# self.key = AESCCM(key)
256+
# elif bit_length:
257+
# if bit_length not in [128, 192, 256]:
258+
# raise UnsupportedBitLength(bit_length)
259+
#
260+
# self.key = AESCCM.generate_key(bit_length=bit_length)
261+
#
262+
# def encrypt(self, msg, iv='', ass_data=None):
263+
# """
264+
# Encrypts and authenticates the data provided as well as authenticating
265+
# the associated_data.
266+
#
267+
# :param msg: The message to be encrypted
268+
# :param iv: MUST be present. A value of between 7 and 13 bytes.
269+
# :param ass_data: Associated data
270+
# :return: The ciphertext bytes with the 16 byte tag appended.
271+
# """
272+
# if not iv:
273+
# raise ValueError('Missing Nonce')
274+
#
275+
# return self.key.encrypt(iv, msg, ass_data)
276+
#
277+
# def decrypt(self, ciphertext, iv='', ass_data=None):
278+
# """
279+
# Decrypts the data and authenticates the associated_data (if provided).
280+
#
281+
# :param ciphertext: The data to decrypt including tag
282+
# :param iv: A value of between 7 and 13 bytes.
283+
# :param ass_data: Associated data
284+
# :return: The original plaintext
285+
# """
286+
# if not iv:
287+
# raise ValueError('Missing Nonce')
288+
#
289+
# return self.key.decrypt(iv, ciphertext, ass_data)
239290

240291

241292
# ---------------------------------------------------------------------------
@@ -414,9 +465,11 @@ def _generate_iv(encalg, iv=""):
414465
return iv
415466
else:
416467
if encalg in ENCALGLEN1:
417-
_iv = get_random_bytes(12)
468+
# _iv = get_random_bytes(ENCALGLEN1[encalg])
469+
_iv = get_random_bytes(16)
418470
elif encalg in ENCALGLEN2:
419-
_iv = get_random_bytes(12)
471+
# _iv = get_random_bytes(ENCALGLEN2[encalg])
472+
_iv = get_random_bytes(16)
420473
else:
421474
raise Exception("Unsupported encryption algorithm %s" % encalg)
422475

@@ -440,7 +493,7 @@ def _generate_key(encalg, cek=""):
440493
def alg2keytype(self, alg):
441494
return alg2keytype(alg)
442495

443-
def enc_setup(self, enc_alg, msg, auth_data, key=None, iv=""):
496+
def enc_setup(self, enc_alg, msg, auth_data=b'', key=None, iv=""):
444497
""" Encrypt JWE content.
445498
446499
:param enc_alg: The JWE "enc" value specifying the encryption algorithm
@@ -453,17 +506,17 @@ def enc_setup(self, enc_alg, msg, auth_data, key=None, iv=""):
453506
iv = self._generate_iv(enc_alg, iv)
454507

455508
if enc_alg in ["A192GCM", "A128GCM", "A256GCM"]:
456-
aes = AES_GCMEncrypter(KEYLEN[enc_alg], key=key)
509+
aes = AES_GCMEncrypter(ENCALGLEN1[enc_alg], key=key)
457510
elif enc_alg in ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"]:
458-
aes = AES_CCMEncrypter(KEYLEN[enc_alg], key=key)
511+
aes = AES_CBCEncrypter(ENCALGLEN2[enc_alg], key=key)
459512
else:
460513
raise NotSupportedAlgorithm(enc_alg)
461514

462-
ctxt, tag = split_ctx_and_tag(aes.encrypt(msg, iv, auth_data))
463-
return ctxt, tag, aes.key
515+
res = split_ctx_and_tag(aes.encrypt(msg, iv, auth_data))
516+
return res[0], res[1], aes.key
464517

465518
@staticmethod
466-
def _decrypt(enc, key, ctxt, auth_data, iv, tag):
519+
def _decrypt(enc, key, ctxt, iv, tag, p_header=b'', auth_data=b''):
467520
""" Decrypt JWE content.
468521
469522
:param enc: The JWE "enc" value specifying the encryption algorithm
@@ -477,13 +530,13 @@ def _decrypt(enc, key, ctxt, auth_data, iv, tag):
477530
if enc in ["A128GCM", "A192GCM", "A256GCM"]:
478531
aes = AES_GCMEncrypter(key=key)
479532
elif enc in ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"]:
480-
aes = AES_CCMEncrypter(key=key)
533+
aes = AES_CBCEncrypter(key=key)
481534
else:
482535
raise Exception("Unsupported encryption algorithm %s" % enc)
483536

484537
ct = ctxt + tag
485538
try:
486-
return aes.decrypt(ct, iv, auth_data)
539+
return aes.decrypt(ct, iv=iv, auth_data=auth_data)
487540
except DecryptionFailed:
488541
raise
489542

@@ -530,9 +583,7 @@ def encrypt(self, key, iv="", cek="", **kwargs):
530583

531584
_enc = self["enc"]
532585

533-
ctxt, tag, cek = self.enc_setup(_enc, _msg.encode(),
534-
jwe.b64_encode_header(),
535-
cek, iv=iv)
586+
ctxt, tag, cek = self.enc_setup(_enc, _msg.encode(), key=cek, iv=iv)
536587
return jwe.pack(parts=[jek, iv, ctxt, tag])
537588

538589
def decrypt(self, token, key=None, cek=None):
@@ -560,8 +611,8 @@ def decrypt(self, token, key=None, cek=None):
560611

561612
msg = self._decrypt(
562613
jwe.headers["enc"], cek, jwe.ciphertext(),
563-
jwe.b64_protected_header(),
564-
jwe.initialization_vector(), jwe.authentication_tag())
614+
p_header=jwe.b64_protected_header(),
615+
iv=jwe.initialization_vector(), tag=jwe.authentication_tag())
565616

566617
if "zip" in self and self["zip"] == "DEF":
567618
msg = zlib.decompress(msg)
@@ -616,9 +667,9 @@ def encrypt(self, key, iv="", cek="", **kwargs):
616667

617668
jwe = JWEnc(**self.headers())
618669

619-
enc_header = jwe.b64_encode_header()
670+
#enc_header = jwe.b64_encode_header()
620671

621-
ctxt, tag, key = self.enc_setup(_enc, _msg, enc_header, cek, iv)
672+
ctxt, tag, key = self.enc_setup(_enc, _msg, key=cek, iv=iv)
622673
return jwe.pack(parts=[jwe_enc_key, iv, ctxt, tag])
623674

624675
def decrypt(self, token, key, cek=None):
@@ -656,9 +707,9 @@ def decrypt(self, token, key, cek=None):
656707
raise NotSupportedAlgorithm(enc)
657708

658709
msg = self._decrypt(enc, cek, jwe.ciphertext(),
659-
jwe.b64_protected_header(),
660-
jwe.initialization_vector(),
661-
jwe.authentication_tag())
710+
p_header=jwe.b64_protected_header(),
711+
iv=jwe.initialization_vector(),
712+
tag=jwe.authentication_tag())
662713

663714
if "zip" in jwe.headers and jwe.headers["zip"] == "DEF":
664715
msg = zlib.decompress(msg)
@@ -719,7 +770,7 @@ class JWE_EC(JWe):
719770
args = JWe.args[:]
720771
args.append("enc")
721772

722-
def enc_setup(self, msg, auth_data, key=None, **kwargs):
773+
def enc_setup(self, msg, key=None, auth_data=b'', **kwargs):
723774
"""
724775
725776
:param msg: Message to be encrypted
@@ -863,8 +914,8 @@ def encrypt(self, iv="", cek="", **kwargs):
863914

864915
jwe = JWEnc(**_args)
865916
ctxt, tag, cek = super(JWE_EC, self).enc_setup(self["enc"], _msg,
866-
jwe.b64_encode_header(),
867-
cek, iv=iv)
917+
# jwe.b64_encode_header(),
918+
key=cek, iv=iv)
868919
if 'encrypted_key' in kwargs:
869920
return jwe.pack(parts=[kwargs['encrypted_key'], iv, ctxt, tag])
870921
return jwe.pack(parts=[iv, ctxt, tag])
@@ -881,8 +932,8 @@ def decrypt(self, token=None, **kwargs):
881932

882933
msg = super(JWE_EC, self)._decrypt(self.headers["enc"], self.cek,
883934
self.ctxt,
884-
jwe.b64part[0],
885-
self.iv, self.tag)
935+
# p_header=jwe.b64part[0],
936+
iv=self.iv, tag=self.tag)
886937
self.msg = msg
887938
self.msg_valid = True
888939
return msg
@@ -962,7 +1013,7 @@ def encrypt(self, keys=None, cek="", iv="", **kwargs):
9621013

9631014
encrypter = JWE_EC(**self._dict)
9641015
cek, encrypted_key, iv, params, eprivk = encrypter.enc_setup(
965-
self.msg, self._dict, key=keys[0], **self._dict)
1016+
self.msg, key=keys[0], **self._dict)
9661017
kwargs["encrypted_key"] = encrypted_key
9671018
kwargs["params"] = params
9681019
else:

tests/test_4_jwe.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ def test_rsa_with_kid():
286286
def test_ecdh_encrypt_decrypt_direct_key():
287287
# Alice starts of
288288
jwenc = JWE_EC(plain, alg="ECDH-ES", enc="A128GCM")
289-
cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup(
290-
plain, '', key=eck_bob)
289+
cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup(plain,
290+
key=eck_bob)
291291

292292
kwargs = {
293293
'params': params, 'cek': cek, 'iv': iv,
@@ -309,7 +309,7 @@ def test_ecdh_encrypt_decrypt_direct_key():
309309

310310
def test_ecdh_encrypt_decrypt_keywrapped_key():
311311
jwenc = JWE_EC(plain, alg="ECDH-ES+A128KW", enc="A128GCM")
312-
cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup(plain, '',
312+
cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup(plain,
313313
key=eck_bob)
314314

315315
kwargs = {}

0 commit comments

Comments
 (0)