예제 #1
0
def _ecc_decode_compressed_point(curve, compressed_point):
    """Decodes a compressed elliptic curve point
        as described in SEC-1 v2 section 2.3.4
        http://www.secg.org/sec1-v2.pdf

    :param curve: Elliptic curve type to generate
    :type curve: cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve
    :param bytes compressed_point: Encoded compressed elliptic curve point
    :returns: X and Y coordinates from compressed point
    :rtype: tuple of longs
    :raises NotSupportedError: for non-prime curves, unsupported prime curves, and points at infinity
    """
    if not compressed_point:
        raise NotSupportedError('Points at infinity are not allowed')
    yp_map = {b'\x02': 0, b'\x03': 1}
    X = compressed_point[1:]
    X = to_bytes(X)
    x = cryptography.utils.int_from_bytes(X, 'big')
    Y = compressed_point[0]
    # In Python3, bytes index calls return int values rather than strings
    if isinstance(Y, six.integer_types):
        Y = six.b(chr(Y))
    elif isinstance(Y, six.string_types):
        Y = six.b(Y)
    yp = yp_map[Y]
    # If curve in prime field.
    if curve.name.startswith('secp'):
        try:
            params = _ECC_CURVE_PARAMETERS[curve.name]
        except KeyError:
            raise NotSupportedError(
                'Curve {name} is not supported at this time'.format(
                    name=curve.name))
        alpha = (pow(x, 3, params.p) +
                 (params.a * x % params.p) + params.b) % params.p
        # Only works for p % 4 == 3 at this time.
        # TODO: This is the case for all currently supported algorithms
        # This will need to be expanded if curves which do not match this are added.
        #  Python-ecdsa has these algorithms implemented.  Copy or reference?
        #  https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
        #  Handbook of Applied Cryptography, algorithms 3.34 - 3.39
        if params.p % 4 == 3:
            beta = pow(alpha, (params.p + 1) // 4, params.p)
        else:
            raise NotSupportedError(
                'S not 1 :: Curve not supported at this time')
        if beta % 2 == yp:
            y = beta
        else:
            y = params.p - beta
    else:
        raise NotSupportedError(
            'Non-prime curves are not supported at this time')
    return x, y
예제 #2
0
    def _read_bytes(self, b):
        """Reads the requested number of bytes from a streaming message body.

        :param int b: Number of bytes to read
        :raises NotSupportedError: if content type is not supported
        """
        _LOGGER.debug('%s bytes requested from stream with content type: %s',
                      b, self.content_type)
        if b <= len(self.output_buffer) or self.source_stream.closed:
            _LOGGER.debug(
                'No need to read from source stream or source stream closed')
            return

        if self.content_type == ContentType.FRAMED_DATA:
            _LOGGER.debug('Reading to framed body')
            self.output_buffer += self._read_bytes_to_framed_body(b)
        elif self.content_type == ContentType.NO_FRAMING:
            _LOGGER.debug('Reading to non-framed body')
            self.output_buffer += self._read_bytes_to_non_framed_body(b)
        else:
            raise NotSupportedError('Unsupported content type')

        # To maintain backwards compatibility, only enforce this if a CMM is provided by the caller.
        if self.config.key_provider is None and self.config.source_length is not None:
            # Enforce that if the caller provided a source length value, the total bytes encrypted
            # must not exceed that value.
            if self._bytes_encrypted > self.config.source_length:
                raise CustomMaximumValueExceeded(
                    'Bytes encrypted has exceeded stated source length estimate:\n{actual} > {estimated}'
                    .format(actual=self._bytes_encrypted,
                            estimated=self.config.source))
def deserialize_header(stream, max_encrypted_data_keys=None):
    # type: (IO, Union[int, None]) -> MessageHeader
    """Deserializes the header from a source stream

    :param stream: Source data stream
    :type stream: io.BytesIO
    :param max_encrypted_data_keys: Maximum number of encrypted keys to deserialize
    :type max_encrypted_data_keys: None or positive int
    :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, ) = unpack_values(">B", tee_stream)
    version = _verified_version_from_id(version_id)
    header = {}
    header["version"] = version

    if version == SerializationVersion.V1:
        return _deserialize_header_v1(header, tee_stream,
                                      max_encrypted_data_keys), tee.getvalue()
    elif version == SerializationVersion.V2:
        return _deserialize_header_v2(header, tee_stream,
                                      max_encrypted_data_keys), tee.getvalue()
    else:
        raise NotSupportedError(
            "Unrecognized message format version: {}".format(version))
예제 #4
0
 def _set_signature_type(self):
     """Ensures that the algorithm signature type is a known type and sets a reference value."""
     try:
         cryptography.utils.verify_interface(
             ec.EllipticCurve, self.algorithm.signing_algorithm_info)
         return ec.EllipticCurve
     except cryptography.utils.InterfaceNotImplemented:
         raise NotSupportedError('Unsupported signing algorithm info')
예제 #5
0
 def stream_length(self):
     """Returns the length of the source stream, determining it if not already known."""
     if self._stream_length is None:
         try:
             current_position = self.source_stream.tell()
             self.source_stream.seek(0, 2)
             self._stream_length = self.source_stream.tell()
             self.source_stream.seek(current_position, 0)
         except Exception as error:
             # Catch-all for unknown issues encountered trying to seek for stream length
             raise NotSupportedError(error)
     return self._stream_length
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)
def _verified_version_from_id(version_id):
    # type: (int) -> SerializationVersion
    """Load a message :class:`SerializationVersion` for the specified version ID.

    :param int version_id: Message format version ID
    :return: Message format version
    :rtype: SerializationVersion
    :raises NotSupportedError: if unsupported version ID is received
    """
    try:
        return SerializationVersion(version_id)
    except ValueError as error:
        raise NotSupportedError("Unsupported version {}".format(version_id), error)
예제 #8
0
def generate_ecc_signing_key(algorithm):
    """Returns an ECC signing key.

    :param algorithm: Algorithm object which determines what signature to generate
    :type algorithm: aws_encryption_sdk.identifiers.Algorithm
    :returns: Generated signing key
    :raises NotSupportedError: if signing algorithm is not supported on this platform
    """
    try:
        cryptography.utils.verify_interface(ec.EllipticCurve,
                                            algorithm.signing_algorithm_info)
        return ec.generate_private_key(
            curve=algorithm.signing_algorithm_info(),
            backend=default_backend())
    except cryptography.utils.InterfaceNotImplemented:
        raise NotSupportedError('Unsupported signing algorithm info')
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:
        algorithm_suite = AlgorithmSuite.get_by_id(algorithm_id)
    except KeyError as error:
        raise UnknownIdentityError("Unknown algorithm {}".format(algorithm_id), error)

    if not algorithm_suite.allowed:
        raise NotSupportedError("Unsupported algorithm: {}".format(algorithm_suite))

    return algorithm_suite
예제 #10
0
    def _read_bytes(self, b):
        """Reads the requested number of bytes from a streaming message body.

        :param int b: Number of bytes to read
        :raises NotSupportedError: if content type is not supported
        """
        if hasattr(self, "footer"):
            _LOGGER.debug("Source stream processing complete")
            return

        buffer_length = len(self.output_buffer)
        if 0 <= b <= buffer_length:
            _LOGGER.debug("%d bytes requested less than or equal to current output buffer size %d", b, buffer_length)
            return

        if self._header.content_type == ContentType.FRAMED_DATA:
            self.output_buffer += self._read_bytes_from_framed_body(b)
        elif self._header.content_type == ContentType.NO_FRAMING:
            self.output_buffer += self._read_bytes_from_non_framed_body(b)
        else:
            raise NotSupportedError("Unsupported content type")
예제 #11
0
    def _read_bytes(self, b):
        """Reads the requested number of bytes from a streaming message body.

        :param int b: Number of bytes to read
        :raises NotSupportedError: if content type is not supported
        """
        _LOGGER.debug('%s bytes requested from stream with content type: %s',
                      b, self.content_type)
        if b <= len(self.output_buffer) or self.source_stream.closed:
            _LOGGER.debug(
                'No need to read from source stream or source stream closed')
            return

        if self.content_type == ContentType.FRAMED_DATA:
            _LOGGER.debug('Reading to framed body')
            self.output_buffer += self._read_bytes_to_framed_body(b)
        elif self.content_type == ContentType.NO_FRAMING:
            _LOGGER.debug('Reading to non-framed body')
            self.output_buffer += self._read_bytes_to_non_framed_body(b)
        else:
            raise NotSupportedError('Unsupported content type')
예제 #12
0
    def _read_bytes(self, b):
        """Reads the requested number of bytes from a streaming message body.

        :param int b: Number of bytes to read
        :raises NotSupportedError: if content type is not supported
        """
        if self.source_stream.closed:
            _LOGGER.debug('Source stream closed')
            return

        if b <= len(self.output_buffer):
            _LOGGER.debug(
                '%s bytes requested less than or equal to current output buffer size %s',
                b, len(self.output_buffer))
            return

        if self._header.content_type == ContentType.FRAMED_DATA:
            self.output_buffer += self._read_bytes_from_framed_body(b)
        elif self._header.content_type == ContentType.NO_FRAMING:
            self.output_buffer += self._read_bytes_from_non_framed_body(b)
        else:
            raise NotSupportedError('Unsupported content type')
예제 #13
0
def _ecc_encode_compressed_point(private_key):
    """Encodes a compressed elliptic curve point
        as described in SEC-1 v2 section 2.3.3
        http://www.secg.org/sec1-v2.pdf

    :param private_key: Private key from which to extract point data
    :type private_key: cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey
    :returns: Encoded compressed elliptic curve point
    :rtype: bytes
    :raises NotSupportedError: for non-prime curves
    """
    # key_size is in bits. Convert to bytes and round up
    byte_length = (private_key.curve.key_size + 7) // 8
    public_numbers = private_key.public_key().public_numbers()
    y_map = [b'\x02', b'\x03']
    # If curve in prime field.
    if private_key.curve.name.startswith('secp'):
        yp = public_numbers.y % 2
        Y = y_map[yp]
    else:
        raise NotSupportedError(
            'Non-prime curves are not supported at this time')
    return Y + cryptography.utils.int_to_bytes(public_numbers.x, byte_length)
예제 #14
0
def deserialize_header(stream):
    """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)
    try:
        message_type = 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)

    try:
        alg = Algorithm.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)

    encrypted_data_keys = set([])
    for _ in range(encrypted_data_key_count):
        (key_provider_length, ) = unpack_values('>H', tee_stream)
        (key_provider_identifier, ) = unpack_values(
            '>{}s'.format(key_provider_length), tee_stream)
        (key_provider_information_length, ) = unpack_values('>H', tee_stream)
        (key_provider_information, ) = unpack_values(
            '>{}s'.format(key_provider_information_length), tee_stream)
        (encrypted_data_key_length, ) = unpack_values('>H', tee_stream)
        encrypted_data_key = tee_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

    (content_type_id, ) = unpack_values('>B', tee_stream)
    try:
        content_type = 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)
    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:
        raise SerializationError(
            'Specified IV length ({length}) does not match algorithm IV length ({alg})'
            .format(length=iv_length, alg=alg))
    header['header_iv_length'] = iv_length

    (frame_length, ) = unpack_values('>I', tee_stream)
    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:
        raise SerializationError(
            'Non-zero frame length found for non-framed message')
    header['frame_length'] = frame_length

    return MessageHeader(**header), tee.getvalue()
예제 #15
0
def prepare_data_keys(key_provider,
                      algorithm,
                      encryption_context,
                      plaintext_rostream,
                      plaintext_length=None,
                      data_key=None):
    """Prepares a DataKey to be used for encrypting message and list
    of EncryptedDataKey objects to be serialized into header.

    :param key_provider: Master Key Provider to use
    :type key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider
    :param algorithm: Algorithm to use for encryption
    :type algorithm: aws_encryption_sdk.identifiers.Algorithm
    :param dict encryption_context: Encryption context to use when generating data key
    :param plaintext_stream: Source plaintext read-only stream
    :type plaintext_rostream: aws_encryption_sdk.internal.utils.ROStream
    :param int plaintext_length: Length of source plaintext (optional)
    :param data_key: Object containing data key to use (if not supplied, a new key will be generated)
    :type data_key: :class:`aws_encryption_sdk.structure.DataKey`
        or :class:`aws_encryption_sdk.structure.RawDataKey`
    :rtype: tuple containing :class:`aws_encryption_sdk.structure.RawDataKey`
        and set of :class:`aws_encryption_sdk.structure.EncryptedDataKey`
    :raises SerializationError: if primary master key is not a member of supplied MasterKeyProvider
    :raises NotSupportedError: if data_key is not a supported data type
    :raises MasterKeyProviderError: if no Master Keys are returned from key_provider
    """
    primary_master_key, master_keys = key_provider.master_keys_for_encryption(
        encryption_context=encryption_context,
        plaintext_rostream=plaintext_rostream,
        plaintext_length=plaintext_length)
    if not master_keys:
        raise MasterKeyProviderError(
            'No Master Keys available from Master Key Provider')
    if primary_master_key not in master_keys:
        raise MasterKeyProviderError(
            'Primary Master Key not in provided Master Keys')
    encrypted_data_keys = set()
    encrypted_encryption_data_key = None
    if not data_key:
        encryption_data_key = primary_master_key.generate_data_key(
            algorithm, encryption_context)
        _LOGGER.debug('encryption data key generated from primary master key')
    elif isinstance(data_key, RawDataKey):
        encryption_data_key = data_key
        _LOGGER.debug('raw encryption data key provided')
    elif isinstance(data_key, DataKey):
        encryption_data_key = data_key
        _LOGGER.debug('full encryption data key provided')
    else:
        raise NotSupportedError('Unsupported data_key type: {}'.format(
            type(data_key)))
    _LOGGER.debug('encryption data key provider: %s',
                  encryption_data_key.key_provider)
    for master_key in master_keys:
        encrypted_key = master_key.encrypt_data_key(
            data_key=encryption_data_key,
            algorithm=algorithm,
            encryption_context=encryption_context)
        encrypted_data_keys.add(encrypted_key)
        _LOGGER.debug('encryption key encrypted with master key: %s',
                      master_key.key_provider)
        if master_key is primary_master_key:
            encrypted_encryption_data_key = encrypted_key
    # Normalize output to DataKey
    encryption_data_key = DataKey(
        key_provider=encryption_data_key.key_provider,
        data_key=encryption_data_key.data_key,
        encrypted_data_key=encrypted_encryption_data_key.encrypted_data_key)
    return encryption_data_key, encrypted_data_keys