Skip to content

Pylint 2.0.0 and a little bit of housekeeping #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 15, 2018
Merged
14 changes: 6 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,18 @@ matrix:
env: TOXENV=py36-examples
- python: 3.6
env: TOXENV=nocmk
# disabling Bandit run in Travis pending resolution of https://bugs.launchpad.net/bandit/+bug/1749603
# - python: 3.6
# env: TOXENV=bandit
- python: 3.6
env: TOXENV=bandit
- python: 3.6
env: TOXENV=doc8
- python: 3.6
env: TOXENV=docs
- python: 3.6
env: TOXENV=readme
# pending reorg of deserialize_header
# - python: 3.6
# env: TOXENV=flake8
# - python: 3.6
# env: TOXENV=pylint
- python: 3.6
env: TOXENV=flake8
- python: 3.6
env: TOXENV=pylint
- python: 3.6
env: TOXENV=flake8-tests
- python: 3.6
Expand Down
5 changes: 4 additions & 1 deletion examples/src/pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
# Disabling messages that either we don't care about we intentionally break.
#
# C0103 : invalid-name (we prefer long, descriptive, names for examples)
# C0330 : bad-continuation (we let black handle this)
# C0412 : ungrouped-imports (we let isort handle this)
# E1101 : no-member (breaks with attrs)
# R0201 : no-self-use (interesting to keep in mind for later refactoring, but not blocking)
# R0205 : useless-object-inheritance (we need to support Python 2, so no, not useless)
# R0801 : duplicate-code (some examples may be similar)
# R0903 : too-few-public-methods (does not allow value stores)
# R0914 : too-many-locals (examples may sometimes have more locals defined for clarity than would be appropriate in code)
# R1705 : no-else-return (we omit this on purpose for brevity where it would add no value)
# W0201 : attribute-defined-outside-init (breaks with attrs_post_init)
# W0223 : abstract-method (throws false positives on io.BaseIO grandchildren)
# W0621 : redefined-outer-name (we do this on purpose in multiple places)
disable = C0103, E1101, R0201, R0801, R0903, R0914, R1705, W0201, W0223, W0621
disable = C0103, C0330, C0412, E1101, R0201, R0205, R0801, R0903, R0914, R1705, W0201, W0223, W0621

[BASIC]
# Allow function names up to 50 characters
Expand Down
2 changes: 1 addition & 1 deletion src/aws_encryption_sdk/identifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ def __init__(self, algorithm, input_length, hash_algorithm):
self.hash_algorithm = hash_algorithm

def input_length(self, encryption):
# type: (EncryptionSuite) -> int
"""Determine the correct KDF input value length for this KDFSuite when used with
a specific EncryptionSuite.

:param encryption: EncryptionSuite to use
:type encryption: aws_encryption_sdk.identifiers.EncryptionSuite
:rtype: int
"""
# type: (EncryptionSuite) -> bool
if self._input_length is None:
return encryption.data_key_length

Expand Down
211 changes: 158 additions & 53 deletions src/aws_encryption_sdk/internal/formatting/deserialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from aws_encryption_sdk.exceptions import NotSupportedError, SerializationError, UnknownIdentityError
from aws_encryption_sdk.identifiers import (
Algorithm, ContentType, ObjectType, SequenceIdentifier, SerializationVersion
AlgorithmSuite, ContentType, ObjectType, SequenceIdentifier, SerializationVersion
)
from aws_encryption_sdk.internal.crypto.encryption import decrypt
from aws_encryption_sdk.internal.defaults import MAX_FRAME_SIZE
Expand Down Expand Up @@ -61,109 +61,214 @@ def validate_header(header, header_auth, raw_header, data_key):
raise SerializationError('Header authorization failed')


def deserialize_header(stream):
"""Deserializes the header from a source stream
def _verified_version_from_id(version_id):
# type: (int) -> SerializationVersion
"""Load a message :class:`SerializationVersion` for the specified version ID.

:param stream: Source data stream
:type stream: io.BytesIO
:returns: Deserialized MessageHeader object
:rtype: :class:`aws_encryption_sdk.structures.MessageHeader` and bytes
:raises NotSupportedError: if unsupported data types are found
:raises UnknownIdentityError: if unknown data types are found
:raises SerializationError: if IV length does not match algorithm
:param int version_id: Message format version ID
:return: Message format version
:rtype: SerializationVersion
:raises NotSupportedError: if unsupported version ID is received
"""
_LOGGER.debug('Starting header deserialization')
tee = io.BytesIO()
tee_stream = TeeStream(stream, tee)
version_id, message_type_id = unpack_values('>BB', tee_stream)
try:
message_type = ObjectType(message_type_id)
return SerializationVersion(version_id)
except ValueError as error:
raise NotSupportedError('Unsupported version {}'.format(version_id), error)


def _verified_message_type_from_id(message_type_id):
# type: (int) -> ObjectType
"""Load a message :class:`ObjectType` for the specified message type ID.

:param int message_type_id: Message type ID
:return: Message type
:rtype: ObjectType
:raises NotSupportedError: if unsupported message type ID is received
"""
try:
return ObjectType(message_type_id)
except ValueError as error:
raise NotSupportedError(
'Unsupported type {} discovered in data stream'.format(message_type_id),
error
)
try:
version = SerializationVersion(version_id)
except ValueError as error:
raise NotSupportedError('Unsupported version {}'.format(version_id), error)
header = {'version': version, 'type': message_type}

algorithm_id, message_id, ser_encryption_context_length = unpack_values('>H16sH', tee_stream)

def _verified_algorithm_from_id(algorithm_id):
# type: (int) -> AlgorithmSuite
"""Load a message :class:`AlgorithmSuite` for the specified algorithm suite ID.

:param int algorithm_id: Algorithm suite ID
:return: Algorithm suite
:rtype: AlgorithmSuite
:raises UnknownIdentityError: if unknown algorithm ID is received
:raises NotSupportedError: if unsupported algorithm ID is received
"""
try:
alg = Algorithm.get_by_id(algorithm_id)
algorithm_suite = AlgorithmSuite.get_by_id(algorithm_id)
except KeyError as error:
raise UnknownIdentityError('Unknown algorithm {}'.format(algorithm_id), error)
if not alg.allowed:
raise NotSupportedError('Unsupported algorithm: {}'.format(alg))
header['algorithm'] = alg
header['message_id'] = message_id

header['encryption_context'] = deserialize_encryption_context(
tee_stream.read(ser_encryption_context_length)
)
(encrypted_data_key_count,) = unpack_values('>H', tee_stream)
if not algorithm_suite.allowed:
raise NotSupportedError('Unsupported algorithm: {}'.format(algorithm_suite))

return algorithm_suite


def _deserialize_encrypted_data_keys(stream):
# type: (IO) -> Set[EncryptedDataKey]
"""Deserialize some encrypted data keys from a stream.

:param stream: Stream from which to read encrypted data keys
:return: Loaded encrypted data keys
:rtype: set of :class:`EncryptedDataKey`
"""
(encrypted_data_key_count,) = unpack_values('>H', stream)
encrypted_data_keys = set([])
for _ in range(encrypted_data_key_count):
(key_provider_length,) = unpack_values('>H', tee_stream)
(key_provider_length,) = unpack_values('>H', stream)
(key_provider_identifier,) = unpack_values(
'>{}s'.format(key_provider_length),
tee_stream
stream
)
(key_provider_information_length,) = unpack_values('>H', tee_stream)
(key_provider_information_length,) = unpack_values('>H', stream)
(key_provider_information,) = unpack_values(
'>{}s'.format(key_provider_information_length),
tee_stream
stream
)
(encrypted_data_key_length,) = unpack_values('>H', tee_stream)
encrypted_data_key = tee_stream.read(encrypted_data_key_length)
(encrypted_data_key_length,) = unpack_values('>H', stream)
encrypted_data_key = stream.read(encrypted_data_key_length)
encrypted_data_keys.add(EncryptedDataKey(
key_provider=MasterKeyInfo(
provider_id=to_str(key_provider_identifier),
key_info=key_provider_information
),
encrypted_data_key=encrypted_data_key
))
header['encrypted_data_keys'] = encrypted_data_keys
return encrypted_data_keys

(content_type_id,) = unpack_values('>B', tee_stream)

def _verified_content_type_from_id(content_type_id):
# type: (int) -> ContentType
"""Load a message :class:`ContentType` for the specified content type ID.

:param int content_type_id: Content type ID
:return: Message content type
:rtype: ContentType
:raises UnknownIdentityError: if unknown content type ID is received
"""
try:
content_type = ContentType(content_type_id)
return ContentType(content_type_id)
except ValueError as error:
raise UnknownIdentityError(
'Unknown content type {}'.format(content_type_id),
error
)
header['content_type'] = content_type

(content_aad_length,) = unpack_values('>I', tee_stream)

def _verified_content_aad_length(content_aad_length):
# type: (int) -> int
"""Verify that content aad length is ``0``.

:param int content_aad_length: Content aad length to verify
:return: ``0``
:rtype: int
:raises SerializationError: if ``content_aad_length`` is not ``0``
"""
if content_aad_length != 0:
raise SerializationError(
'Content AAD length field is currently unused, its value must be always 0'
)
header['content_aad_length'] = 0

(iv_length,) = unpack_values('>B', tee_stream)
if iv_length != alg.iv_len:
return 0


def _verified_iv_length(iv_length, algorithm_suite):
# type: (int, AlgorithmSuite) -> int
"""Verify an IV length for an algorithm suite.

:param int iv_length: IV length to verify
:param AlgorithmSuite algorithm_suite: Algorithm suite to verify against
:return: IV length
:rtype: int
:raises SerializationError: if IV length does not match algorithm suite
"""
if iv_length != algorithm_suite.iv_len:
raise SerializationError(
'Specified IV length ({length}) does not match algorithm IV length ({alg})'.format(
'Specified IV length ({length}) does not match algorithm IV length ({algorithm})'.format(
length=iv_length,
alg=alg
algorithm=algorithm_suite
)
)
header['header_iv_length'] = iv_length

(frame_length,) = unpack_values('>I', tee_stream)
return iv_length


def _verified_frame_length(frame_length, content_type):
# type: (int, ContentType) -> int
"""Verify a frame length value for a message content type.

:param int frame_length: Frame length to verify
:param ContentType content_type: Message content type to verify against
:return: frame length
:rtype: int
:raises SerializationError: if frame length is too large
:raises SerializationError: if frame length is not zero for unframed content type
"""
if content_type == ContentType.FRAMED_DATA and frame_length > MAX_FRAME_SIZE:
raise SerializationError('Specified frame length larger than allowed maximum: {found} > {max}'.format(
found=frame_length,
max=MAX_FRAME_SIZE
))
elif content_type == ContentType.NO_FRAMING and frame_length != 0:

if content_type == ContentType.NO_FRAMING and frame_length != 0:
raise SerializationError('Non-zero frame length found for non-framed message')
header['frame_length'] = frame_length

return frame_length


def deserialize_header(stream):
# type: (IO) -> MessageHeader
"""Deserializes the header from a source stream

:param stream: Source data stream
:type stream: io.BytesIO
:returns: Deserialized MessageHeader object
:rtype: :class:`aws_encryption_sdk.structures.MessageHeader` and bytes
:raises NotSupportedError: if unsupported data types are found
:raises UnknownIdentityError: if unknown data types are found
:raises SerializationError: if IV length does not match algorithm
"""
_LOGGER.debug('Starting header deserialization')
tee = io.BytesIO()
tee_stream = TeeStream(stream, tee)
version_id, message_type_id = unpack_values('>BB', tee_stream)
header = dict()
header['version'] = _verified_version_from_id(version_id)
header['type'] = _verified_message_type_from_id(message_type_id)

algorithm_id, message_id, ser_encryption_context_length = unpack_values('>H16sH', tee_stream)

header['algorithm'] = _verified_algorithm_from_id(algorithm_id)
header['message_id'] = message_id

header['encryption_context'] = deserialize_encryption_context(
tee_stream.read(ser_encryption_context_length)
)

header['encrypted_data_keys'] = _deserialize_encrypted_data_keys(tee_stream)

(content_type_id,) = unpack_values('>B', tee_stream)
header['content_type'] = _verified_content_type_from_id(content_type_id)

(content_aad_length,) = unpack_values('>I', tee_stream)
header['content_aad_length'] = _verified_content_aad_length(content_aad_length)

(iv_length,) = unpack_values('>B', tee_stream)
header['header_iv_length'] = _verified_iv_length(iv_length, header['algorithm'])

(frame_length,) = unpack_values('>I', tee_stream)
header['frame_length'] = _verified_frame_length(frame_length, header['content_type'])

return MessageHeader(**header), tee.getvalue()

Expand All @@ -173,8 +278,8 @@ def deserialize_header_auth(stream, algorithm, verifier=None):

:param stream: Source data stream
:type stream: io.BytesIO
:param algorithm: The Algorithm object type contained in the header
:type algorith: aws_encryption_sdk.identifiers.Algorithm
:param algorithm: The AlgorithmSuite object type contained in the header
:type algorith: aws_encryption_sdk.identifiers.AlgorithmSuite
:param verifier: Signature verifier object (optional)
:type verifier: aws_encryption_sdk.internal.crypto.Verifier
:returns: Deserialized MessageHeaderAuthentication object
Expand Down Expand Up @@ -370,7 +475,7 @@ def deserialize_wrapped_key(wrapping_algorithm, wrapping_key_id, wrapped_encrypt
raise SerializationError('Malformed key info: key info missing data')
tag_len //= 8 # Tag Length is stored in bits, not bytes
if iv_len != wrapping_algorithm.algorithm.iv_len:
raise SerializationError('Wrapping Algorithm mismatch for wrapped data key')
raise SerializationError('Wrapping AlgorithmSuite mismatch for wrapped data key')
iv = _key_info[8:]
if len(iv) != iv_len:
raise SerializationError('Malformed key info: incomplete iv')
Expand Down
2 changes: 1 addition & 1 deletion src/aws_encryption_sdk/internal/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def prepare_data_keys(primary_master_key, master_keys, algorithm, encryption_con
try:
_FILE_TYPE = file # Python 2
except NameError:
_FILE_TYPE = io.IOBase # Python 3 # pylint: disable=invalid-name
_FILE_TYPE = io.IOBase # Python 3 pylint: disable=invalid-name


def prep_stream_data(data):
Expand Down
13 changes: 11 additions & 2 deletions src/aws_encryption_sdk/streaming_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,19 @@ class _EncryptionStream(io.IOBase):
# def _config_class(self):
# Configuration class for this class

line_length = LINE_LENGTH
line_length = LINE_LENGTH # type: int
config = None # type: _ClientConfig
bytes_read = None # type: int
output_buffer = None # type: bytes
_message_prepped = None # type: bool
source_stream = None
_stream_length = None # type: int

def __new__(cls, **kwargs):
"""Patch for abstractmethod-like enforcement in io.IOBase grandchildren."""
"""Perform necessary handling for _EncryptionStream instances that should be
applied to all children.
"""
# Patch for abstractmethod-like enforcement in io.IOBase grandchildren.
if (
not (hasattr(cls, '_read_bytes') and callable(cls._read_bytes))
or not (hasattr(cls, '_prep_message') and callable(cls._read_bytes))
Expand Down
Loading