aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2016-03-16 12:58:27 -0400
committerPaul Kehrer <paul.l.kehrer@gmail.com>2016-03-16 12:58:27 -0400
commitf790b4289ed026cab590fd98aca4d6777f62d719 (patch)
treea5a4a589545c9332fdd9f00c60781f57bb00fc8e
parent1c6e624631cb339b9e5e437083bca971530bba9f (diff)
parent70b3a7dd5ce2a953da1ce19534bcedbb53a8c2bf (diff)
downloadcryptography-f790b4289ed026cab590fd98aca4d6777f62d719.tar.gz
cryptography-f790b4289ed026cab590fd98aca4d6777f62d719.tar.bz2
cryptography-f790b4289ed026cab590fd98aca4d6777f62d719.zip
Merge pull request #2736 from cedk/ANSI_X.923
Added support for padding ANSI X.923
-rw-r--r--CHANGELOG.rst2
-rw-r--r--docs/hazmat/primitives/padding.rst45
-rw-r--r--src/_cffi_src/hazmat_src/padding.c24
-rw-r--r--src/_cffi_src/hazmat_src/padding.h1
-rw-r--r--src/cryptography/hazmat/primitives/padding.py172
-rw-r--r--tests/hazmat/primitives/test_padding.py92
-rw-r--r--tests/hypothesis/test_padding.py13
7 files changed, 301 insertions, 48 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index aad8d934..63dab998 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,8 @@ Changelog
.. note:: This version is not yet released and is under active development.
+* Added support for padding ANSI X.923 with
+ :class:`~cryptography.hazmat.primitives.padding.ANSIX923`.
* Deprecated support for OpenSSL 0.9.8. Support will be removed in
``cryptography`` 1.4.
* Added support for the :class:`~cryptography.x509.PolicyConstraints`
diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst
index a60f5ac8..0b76327e 100644
--- a/docs/hazmat/primitives/padding.rst
+++ b/docs/hazmat/primitives/padding.rst
@@ -54,6 +54,49 @@ multiple of the block size.
provider.
+.. class:: ANSIX923(block_size)
+
+ .. versionadded:: 1.3
+
+ `ANSI X.923`_ padding works by appending ``N-1`` bytes with the value of
+ ``0`` and a last byte with the value of ``chr(N)``, where ``N`` is the
+ number of bytes required to make the final block of data the same size as
+ the block size. A simple example of padding is:
+
+ .. doctest::
+
+ >>> padder = padding.ANSIX923(128).padder()
+ >>> padded_data = padder.update(b"11111111111111112222222222")
+ >>> padded_data
+ '1111111111111111'
+ >>> padded_data += padder.finalize()
+ >>> padded_data
+ '11111111111111112222222222\x00\x00\x00\x00\x00\x06'
+ >>> unpadder = padding.ANSIX923(128).unpadder()
+ >>> data = unpadder.update(padded_data)
+ >>> data
+ '1111111111111111'
+ >>> data + unpadder.finalize()
+ '11111111111111112222222222'
+
+ :param block_size: The size of the block in bits that the data is being
+ padded to.
+ :raises ValueError: Raised if block size is not a multiple of 8 or is not
+ between 0 and 256.
+
+ .. method:: padder()
+
+ :returns: A padding
+ :class:`~cryptography.hazmat.primitives.padding.PaddingContext`
+ provider
+
+ .. method:: unpadder()
+
+ :returns: An unpadding
+ :class:`~cryptography.hazmat.primitives.padding.PaddingContext`
+ provider.
+
+
.. class:: PaddingContext
When calling ``padder()`` or ``unpadder()`` the result will conform to the
@@ -82,3 +125,5 @@ multiple of the block size.
:raises TypeError: Raised if data is not bytes.
:raises ValueError: When trying to remove padding from incorrectly
padded data.
+
+.. _`ANSI X.923`: https://en.wikipedia.org/wiki/Padding_%28cryptography%29#ANSI_X.923
diff --git a/src/_cffi_src/hazmat_src/padding.c b/src/_cffi_src/hazmat_src/padding.c
index 570bad9f..1a0c869d 100644
--- a/src/_cffi_src/hazmat_src/padding.c
+++ b/src/_cffi_src/hazmat_src/padding.c
@@ -37,3 +37,27 @@ uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data,
/* Now check the low bit to see if it's set */
return (mismatch & 1) == 0;
}
+
+uint8_t Cryptography_check_ansix923_padding(const uint8_t *data,
+ uint8_t block_len) {
+ uint8_t i;
+ uint8_t pad_size = data[block_len - 1];
+ uint8_t mismatch = 0;
+ /* Skip the first one with the pad size */
+ for (i = 1; i < block_len; i++) {
+ unsigned int mask = Cryptography_constant_time_lt(i, pad_size);
+ uint8_t b = data[block_len - 1 - i];
+ mismatch |= (mask & b);
+ }
+
+ /* Check to make sure the pad_size was within the valid range. */
+ mismatch |= ~Cryptography_constant_time_lt(0, pad_size);
+ mismatch |= Cryptography_constant_time_lt(block_len, pad_size);
+
+ /* Make sure any bits set are copied to the lowest bit */
+ mismatch |= mismatch >> 4;
+ mismatch |= mismatch >> 2;
+ mismatch |= mismatch >> 1;
+ /* Now check the low bit to see if it's set */
+ return (mismatch & 1) == 0;
+}
diff --git a/src/_cffi_src/hazmat_src/padding.h b/src/_cffi_src/hazmat_src/padding.h
index 4d218b1a..fb023c17 100644
--- a/src/_cffi_src/hazmat_src/padding.h
+++ b/src/_cffi_src/hazmat_src/padding.h
@@ -3,3 +3,4 @@
// repository for complete details.
uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t);
+uint8_t Cryptography_check_ansix923_padding(const uint8_t *, uint8_t);
diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py
index f6491eb7..77fb8f83 100644
--- a/src/cryptography/hazmat/primitives/padding.py
+++ b/src/cryptography/hazmat/primitives/padding.py
@@ -28,14 +28,75 @@ class PaddingContext(object):
"""
-class PKCS7(object):
- def __init__(self, block_size):
- if not (0 <= block_size < 256):
- raise ValueError("block_size must be in range(0, 256).")
+def _byte_padding_check(block_size):
+ if not (0 <= block_size < 256):
+ raise ValueError("block_size must be in range(0, 256).")
+
+ if block_size % 8 != 0:
+ raise ValueError("block_size must be a multiple of 8.")
+
+
+def _byte_padding_update(buffer_, data, block_size):
+ if buffer_ is None:
+ raise AlreadyFinalized("Context was already finalized.")
+
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
+
+ buffer_ += data
+
+ finished_blocks = len(buffer_) // (block_size // 8)
+
+ result = buffer_[:finished_blocks * (block_size // 8)]
+ buffer_ = buffer_[finished_blocks * (block_size // 8):]
+
+ return buffer_, result
+
+
+def _byte_padding_pad(buffer_, block_size, paddingfn):
+ if buffer_ is None:
+ raise AlreadyFinalized("Context was already finalized.")
+
+ pad_size = block_size // 8 - len(buffer_)
+ return buffer_ + paddingfn(pad_size)
+
+
+def _byte_unpadding_update(buffer_, data, block_size):
+ if buffer_ is None:
+ raise AlreadyFinalized("Context was already finalized.")
+
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
- if block_size % 8 != 0:
- raise ValueError("block_size must be a multiple of 8.")
+ buffer_ += data
+ finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0)
+
+ result = buffer_[:finished_blocks * (block_size // 8)]
+ buffer_ = buffer_[finished_blocks * (block_size // 8):]
+
+ return buffer_, result
+
+
+def _byte_unpadding_check(buffer_, block_size, checkfn):
+ if buffer_ is None:
+ raise AlreadyFinalized("Context was already finalized.")
+
+ if len(buffer_) != block_size // 8:
+ raise ValueError("Invalid padding bytes.")
+
+ valid = checkfn(buffer_, block_size // 8)
+
+ if not valid:
+ raise ValueError("Invalid padding bytes.")
+
+ pad_size = six.indexbytes(buffer_, -1)
+ return buffer_[:-pad_size]
+
+
+class PKCS7(object):
+ def __init__(self, block_size):
+ _byte_padding_check(block_size)
self.block_size = block_size
def padder(self):
@@ -53,27 +114,16 @@ class _PKCS7PaddingContext(object):
self._buffer = b""
def update(self, data):
- if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized.")
-
- if not isinstance(data, bytes):
- raise TypeError("data must be bytes.")
-
- self._buffer += data
-
- finished_blocks = len(self._buffer) // (self.block_size // 8)
-
- result = self._buffer[:finished_blocks * (self.block_size // 8)]
- self._buffer = self._buffer[finished_blocks * (self.block_size // 8):]
-
+ self._buffer, result = _byte_padding_update(
+ self._buffer, data, self.block_size)
return result
- def finalize(self):
- if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized.")
+ def _padding(self, size):
+ return six.int2byte(size) * size
- pad_size = self.block_size // 8 - len(self._buffer)
- result = self._buffer + six.int2byte(pad_size) * pad_size
+ def finalize(self):
+ result = _byte_padding_pad(
+ self._buffer, self.block_size, self._padding)
self._buffer = None
return result
@@ -86,39 +136,67 @@ class _PKCS7UnpaddingContext(object):
self._buffer = b""
def update(self, data):
- if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized.")
+ self._buffer, result = _byte_unpadding_update(
+ self._buffer, data, self.block_size)
+ return result
+
+ def finalize(self):
+ result = _byte_unpadding_check(
+ self._buffer, self.block_size,
+ lib.Cryptography_check_pkcs7_padding)
+ self._buffer = None
+ return result
- if not isinstance(data, bytes):
- raise TypeError("data must be bytes.")
- self._buffer += data
+class ANSIX923(object):
+ def __init__(self, block_size):
+ _byte_padding_check(block_size)
+ self.block_size = block_size
+
+ def padder(self):
+ return _ANSIX923PaddingContext(self.block_size)
+
+ def unpadder(self):
+ return _ANSIX923UnpaddingContext(self.block_size)
- finished_blocks = max(
- len(self._buffer) // (self.block_size // 8) - 1,
- 0
- )
- result = self._buffer[:finished_blocks * (self.block_size // 8)]
- self._buffer = self._buffer[finished_blocks * (self.block_size // 8):]
+@utils.register_interface(PaddingContext)
+class _ANSIX923PaddingContext(object):
+ def __init__(self, block_size):
+ self.block_size = block_size
+ # TODO: more copies than necessary, we should use zero-buffer (#193)
+ self._buffer = b""
+ def update(self, data):
+ self._buffer, result = _byte_padding_update(
+ self._buffer, data, self.block_size)
return result
+ def _padding(self, size):
+ return six.int2byte(0) * (size - 1) + six.int2byte(size)
+
def finalize(self):
- if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized.")
+ result = _byte_padding_pad(
+ self._buffer, self.block_size, self._padding)
+ self._buffer = None
+ return result
- if len(self._buffer) != self.block_size // 8:
- raise ValueError("Invalid padding bytes.")
- valid = lib.Cryptography_check_pkcs7_padding(
- self._buffer, self.block_size // 8
- )
+@utils.register_interface(PaddingContext)
+class _ANSIX923UnpaddingContext(object):
+ def __init__(self, block_size):
+ self.block_size = block_size
+ # TODO: more copies than necessary, we should use zero-buffer (#193)
+ self._buffer = b""
- if not valid:
- raise ValueError("Invalid padding bytes.")
+ def update(self, data):
+ self._buffer, result = _byte_unpadding_update(
+ self._buffer, data, self.block_size)
+ return result
- pad_size = six.indexbytes(self._buffer, -1)
- res = self._buffer[:-pad_size]
+ def finalize(self):
+ result = _byte_unpadding_check(
+ self._buffer, self.block_size,
+ lib.Cryptography_check_ansix923_padding)
self._buffer = None
- return res
+ return result
diff --git a/tests/hazmat/primitives/test_padding.py b/tests/hazmat/primitives/test_padding.py
index 392ea737..e934c0ac 100644
--- a/tests/hazmat/primitives/test_padding.py
+++ b/tests/hazmat/primitives/test_padding.py
@@ -99,3 +99,95 @@ class TestPKCS7(object):
unpadder.update(b"")
with pytest.raises(AlreadyFinalized):
unpadder.finalize()
+
+
+class TestANSIX923(object):
+ @pytest.mark.parametrize("size", [127, 4096, -2])
+ def test_invalid_block_size(self, size):
+ with pytest.raises(ValueError):
+ padding.ANSIX923(size)
+
+ @pytest.mark.parametrize(("size", "padded"), [
+ (128, b"1111"),
+ (128, b"1111111111111111"),
+ (128, b"111111111111111\x06"),
+ (128, b"1111111111\x06\x06\x06\x06\x06\x06"),
+ (128, b""),
+ (128, b"\x06" * 6),
+ (128, b"\x00" * 16),
+ ])
+ def test_invalid_padding(self, size, padded):
+ unpadder = padding.ANSIX923(size).unpadder()
+ with pytest.raises(ValueError):
+ unpadder.update(padded)
+ unpadder.finalize()
+
+ def test_non_bytes(self):
+ padder = padding.ANSIX923(128).padder()
+ with pytest.raises(TypeError):
+ padder.update(u"abc")
+ unpadder = padding.ANSIX923(128).unpadder()
+ with pytest.raises(TypeError):
+ unpadder.update(u"abc")
+
+ @pytest.mark.parametrize(("size", "unpadded", "padded"), [
+ (
+ 128,
+ b"1111111111",
+ b"1111111111\x00\x00\x00\x00\x00\x06",
+ ),
+ (
+ 128,
+ b"111111111111111122222222222222",
+ b"111111111111111122222222222222\x00\x02",
+ ),
+ (
+ 128,
+ b"1" * 16,
+ b"1" * 16 + b"\x00" * 15 + b"\x10",
+ ),
+ (
+ 128,
+ b"1" * 17,
+ b"1" * 17 + b"\x00" * 14 + b"\x0F",
+ )
+ ])
+ def test_pad(self, size, unpadded, padded):
+ padder = padding.ANSIX923(size).padder()
+ result = padder.update(unpadded)
+ result += padder.finalize()
+ assert result == padded
+
+ @pytest.mark.parametrize(("size", "unpadded", "padded"), [
+ (
+ 128,
+ b"1111111111",
+ b"1111111111\x00\x00\x00\x00\x00\x06",
+ ),
+ (
+ 128,
+ b"111111111111111122222222222222",
+ b"111111111111111122222222222222\x00\x02",
+ ),
+ ])
+ def test_unpad(self, size, unpadded, padded):
+ unpadder = padding.ANSIX923(size).unpadder()
+ result = unpadder.update(padded)
+ result += unpadder.finalize()
+ assert result == unpadded
+
+ def test_use_after_finalize(self):
+ padder = padding.ANSIX923(128).padder()
+ b = padder.finalize()
+ with pytest.raises(AlreadyFinalized):
+ padder.update(b"")
+ with pytest.raises(AlreadyFinalized):
+ padder.finalize()
+
+ unpadder = padding.ANSIX923(128).unpadder()
+ unpadder.update(b)
+ unpadder.finalize()
+ with pytest.raises(AlreadyFinalized):
+ unpadder.update(b"")
+ with pytest.raises(AlreadyFinalized):
+ unpadder.finalize()
diff --git a/tests/hypothesis/test_padding.py b/tests/hypothesis/test_padding.py
index 21c9a234..29d726f1 100644
--- a/tests/hypothesis/test_padding.py
+++ b/tests/hypothesis/test_padding.py
@@ -5,7 +5,7 @@
from hypothesis import given
from hypothesis.strategies import binary, integers
-from cryptography.hazmat.primitives.padding import PKCS7
+from cryptography.hazmat.primitives.padding import ANSIX923, PKCS7
@given(integers(min_value=1, max_value=31), binary())
@@ -19,3 +19,14 @@ def test_pkcs7(block_size, data):
padded = padder.update(data) + padder.finalize()
assert unpadder.update(padded) + unpadder.finalize() == data
+
+
+@given(integers(min_value=1, max_value=31), binary())
+def test_ansix923(block_size, data):
+ a = ANSIX923(block_size=block_size * 8)
+ padder = a.padder()
+ unpadder = a.unpadder()
+
+ padded = padder.update(data) + padder.finalize()
+
+ assert unpadder.update(padded) + unpadder.finalize() == data