Skip to content

Commit 76b57a4

Browse files
committed
cipher: Remove default padding
mbedtls uses a PKCS7 padding by default but users may prefer to use their own function. Crypto.Util.Padding, for example, provides such a facility. There is also a PyPadding project on Pypi. This patch removes the default padding. A ValueError is now raised for modes that make use of padding if the data does not have the expected size. Closes #57.
1 parent 930f219 commit 76b57a4

File tree

4 files changed

+39
-4
lines changed

4 files changed

+39
-4
lines changed

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
[next]
22

3+
* cipher: CBC does not use PKCS7 padding by default and now requires a full
4+
block. Encoding will raise ValueError otherwise.
35
* tls: Add support for session caching.
46
* tls: Implement `context.getpeercert()`.
57
* tls: Add context manager to `TLSWrappedSocket`

src/mbedtls/cipher/_cipher.pxd

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ cdef:
1111

1212

1313
cdef extern from "mbedtls/cipher.h" nogil:
14+
ctypedef enum mbedtls_cipher_padding_t:
15+
MBEDTLS_PADDING_PKCS7 = 0
16+
MBEDTLS_PADDING_ONE_AND_ZEROS
17+
MBEDTLS_PADDING_ZEROS_AND_LEN
18+
MBEDTLS_PADDING_ZEROS
19+
MBEDTLS_PADDING_NONE
20+
1421
ctypedef enum mbedtls_operation_t:
1522
MBEDTLS_DECRYPT = 0, MBEDTLS_ENCRYPT = 1
1623

@@ -79,7 +86,10 @@ cdef extern from "mbedtls/cipher.h" nogil:
7986
const unsigned char* key,
8087
int key_bitlen,
8188
const mbedtls_operation_t operation)
82-
# mbedtls_cipher_set_padding_mode
89+
int mbedtls_cipher_set_padding_mode(
90+
mbedtls_cipher_context_t* ctx,
91+
mbedtls_cipher_padding_t mode
92+
)
8393
# mbedtls_cipher_set_iv
8494

8595
# mbedtls_cipher_reset

src/mbedtls/cipher/_cipher.pyx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,15 @@ cdef class _CipherBase:
189189
_exc.check_error(_cipher.mbedtls_cipher_setup(
190190
&self._dec_ctx,
191191
_cipher.mbedtls_cipher_info_from_string(cipher_name)))
192+
# Remove the default padding for the modes that support it and
193+
# ignore possible errors caused by a cipher mode that doesn't.
194+
#
195+
# Note: Padding is only supported by CBC (mbedtls 2.16.12).
196+
_cipher.mbedtls_cipher_set_padding_mode(
197+
&self._enc_ctx, _cipher.MBEDTLS_PADDING_NONE)
198+
_cipher.mbedtls_cipher_set_padding_mode(
199+
&self._dec_ctx, _cipher.MBEDTLS_PADDING_NONE)
200+
192201

193202
if key is not None:
194203
_exc.check_error(_cipher.mbedtls_cipher_setkey(
@@ -271,11 +280,14 @@ cdef class Cipher(_CipherBase):
271280
try:
272281
# We can call `check_error` directly here because we return a
273282
# python object.
274-
_exc.check_error(_cipher.mbedtls_cipher_crypt(
283+
err = _cipher.mbedtls_cipher_crypt(
275284
&self._enc_ctx if operation is _cipher.MBEDTLS_ENCRYPT else
276285
&self._dec_ctx,
277286
&iv[0] if iv.size else NULL, iv.size,
278-
&input[0], input.size, output, &olen))
287+
&input[0], input.size, output, &olen)
288+
if err == -0x6280:
289+
raise ValueError("expected a full block")
290+
_exc.check_error(err)
279291
return output[:olen]
280292
finally:
281293
free(output)

tests/test_cipher.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,11 @@ def cipher(self, module, key, mode, iv):
119119
@pytest.fixture
120120
def data(self, cipher, mode, randbytes):
121121
# `block_size` is limited for ECB because it is a block cipher.
122-
return randbytes(cipher.block_size if mode is mb.Mode.ECB else 20000)
122+
assert cipher.block_size
123+
return randbytes(
124+
cipher.block_size
125+
if mode is mb.Mode.ECB
126+
else cipher.block_size * 128)
123127

124128
def test_pickle(self, cipher):
125129
with pytest.raises(TypeError) as excinfo:
@@ -180,6 +184,13 @@ def cipher(self, module, key, mode, iv, ad):
180184
def ad(self, mode, randbytes, request):
181185
return randbytes(request.param)
182186

187+
def test_cbc_requires_padding(self, mode, cipher, data):
188+
data += b"\0"
189+
if mode is mb.Mode.CBC:
190+
with pytest.raises(ValueError):
191+
cipher.encrypt(data)
192+
assert cipher.decrypt(*cipher.encrypt(data)) == data
193+
183194
def test_encrypt_decrypt(self, cipher, data):
184195
msg, tag = cipher.encrypt(data)
185196
assert cipher.decrypt(msg, tag) == data

0 commit comments

Comments
 (0)