示例#1
0
    def __init__(self, file, chunksize=1 << 13):
        super().__init__(file, chunksize)
        self.source_mimetype = self.mimetype
        self.mimetype = "application/octet-stream"

        with open(self.file, "rb") as file:
            self.ciphertext, self.file_keys = encrypt_attachment(file.read())
示例#2
0
    def test_invalid_iv(self):
        data = b"Test bytes"

        cyphertext, keys = encrypt_attachment(data)

        with pytest.raises(EncryptionError):
            decrypt_attachment(cyphertext, keys["key"]["k"],
                               keys["hashes"]["sha256"], "Fake iv")
示例#3
0
    def test_hash_verification(self):
        data = b"Test bytes"

        cyphertext, keys = encrypt_attachment(data)

        with pytest.raises(EncryptionError):
            decrypt_attachment(cyphertext, keys["key"]["k"], "Fake hash",
                               keys["iv"])
示例#4
0
    def test_encrypt(self):
        data = b"Test bytes"

        cyphertext, keys = encrypt_attachment(data)

        plaintext = decrypt_attachment(cyphertext, keys["key"]["k"],
                                       keys["hashes"]["sha256"], keys["iv"])

        assert data == plaintext
示例#5
0
async def transfer_thumbnail_to_matrix(
        client: MautrixTelegramClient, intent: IntentAPI,
        thumbnail_loc: TypeLocation, video: bytes, mime: str,
        encrypt: bool) -> Optional[DBTelegramFile]:
    if not Image or not VideoFileClip:
        return None

    loc_id = _location_to_id(thumbnail_loc)
    if not loc_id:
        return None

    db_file = DBTelegramFile.get(loc_id)
    if db_file:
        return db_file

    video_ext = sane_mimetypes.guess_extension(mime)
    if VideoFileClip and video_ext and video:
        try:
            file, width, height = _read_video_thumbnail(video,
                                                        video_ext,
                                                        frame_ext="png")
        except OSError:
            return None
        mime_type = "image/png"
    else:
        file = await client.download_file(thumbnail_loc)
        width, height = None, None
        mime_type = magic.from_buffer(file, mime=True)

    decryption_info = None
    upload_mime_type = mime_type
    if encrypt:
        file, decryption_info_dict = encrypt_attachment(file)
        decryption_info = EncryptedFile.deserialize(decryption_info_dict)
        upload_mime_type = "application/octet-stream"
    content_uri = await intent.upload_media(file, upload_mime_type)
    if decryption_info:
        decryption_info.url = content_uri

    db_file = DBTelegramFile(id=loc_id,
                             mxc=content_uri,
                             mime_type=mime_type,
                             was_converted=False,
                             timestamp=int(time.time()),
                             size=len(file),
                             width=width,
                             height=height,
                             decryption_info=decryption_info)
    try:
        db_file.insert()
    except (IntegrityError, InvalidRequestError) as e:
        log.exception(
            f"{e.__class__.__name__} while saving transferred file thumbnail data. "
            "This was probably caused by two simultaneous transfers of the same file, "
            "and might (but probably won't) cause problems with thumbnails or something."
        )
    return db_file
示例#6
0
    def test_short_key(self):
        data = b"Test bytes"

        cyphertext, keys = encrypt_attachment(data)

        with pytest.raises(EncryptionError):
            decrypt_attachment(
                cyphertext,
                unpaddedbase64.encode_base64(b"Fake key", urlsafe=True),
                keys["hashes"]["sha256"], keys["iv"])
示例#7
0
    def test_fake_key(self):
        data = b"Test bytes"

        cyphertext, keys = encrypt_attachment(data)

        fake_key = Random.new().read(32)

        plaintext = decrypt_attachment(
            cyphertext, unpaddedbase64.encode_base64(fake_key, urlsafe=True),
            keys["hashes"]["sha256"], keys["iv"])
        assert plaintext != data
示例#8
0
    def test_short_iv(self):
        data = b"Test bytes"

        cyphertext, keys = encrypt_attachment(data)

        plaintext = decrypt_attachment(
            cyphertext,
            keys["key"]["k"],
            keys["hashes"]["sha256"],
            unpaddedbase64.encode_base64(b"F" + b"\x00" * 8),
        )
        assert plaintext != data
示例#9
0
    async def process_hangouts_attachments(
            self, event: ChatMessageEvent,
            intent: IntentAPI) -> Optional[EventID]:
        attachments_pb = event._event.chat_message.message_content.attachment

        if len(event.attachments) > 1:
            self.log.warning("Can't handle more that one attachment")
            return None

        attachment = event.attachments[0]
        attachment_pb = attachments_pb[0]

        embed_item = attachment_pb.embed_item

        # Get the filename from the headers
        async with self.az.http_session.request("GET", attachment) as resp:
            value, params = cgi.parse_header(
                resp.headers["Content-Disposition"])
            mime = resp.headers["Content-Type"]
            filename = params.get('filename', attachment.split("/")[-1])

        # TODO: This also catches movies, but I can't work out how they present
        #       differently to images
        if embed_item.type[0] == hangouts.ITEM_TYPE_PLUS_PHOTO:
            data = await self._get_remote_bytes(attachment)
            upload_mime = mime
            decryption_info = None
            if self.encrypted and encrypt_attachment:
                data, decryption_info_dict = encrypt_attachment(data)
                decryption_info = EncryptedFile.deserialize(
                    decryption_info_dict)
                upload_mime = "application/octet-stream"
            mxc_url = await intent.upload_media(data,
                                                mime_type=upload_mime,
                                                filename=filename)
            if decryption_info:
                decryption_info.url = mxc_url
            content = MediaMessageEventContent(url=mxc_url,
                                               file=decryption_info,
                                               body=filename,
                                               info=ImageInfo(size=len(data),
                                                              mimetype=mime),
                                               msgtype=MessageType.IMAGE)
            return await self._send_message(intent,
                                            content,
                                            timestamp=event.timestamp)
        return None
示例#10
0
 async def _reupload_fb_file(
     url: str,
     intent: IntentAPI,
     filename: Optional[str] = None,
     encrypt: bool = False
 ) -> Tuple[ContentURI, str, int, Optional[EncryptedFile]]:
     if not url:
         raise ValueError('URL not provided')
     async with aiohttp.ClientSession() as session:
         resp = await session.get(url)
         data = await resp.read()
     mime = magic.from_buffer(data, mime=True)
     upload_mime_type = mime
     decryption_info = None
     if encrypt and encrypt_attachment:
         data, decryption_info_dict = encrypt_attachment(data)
         decryption_info = EncryptedFile.deserialize(decryption_info_dict)
         upload_mime_type = "application/octet-stream"
     url = await intent.upload_media(data,
                                     mime_type=upload_mime_type,
                                     filename=filename)
     if decryption_info:
         decryption_info.url = url
     return url, mime, len(data), decryption_info
示例#11
0
async def _unlocked_transfer_file_to_matrix(
        client: MautrixTelegramClient, intent: IntentAPI, loc_id: str,
        location: TypeLocation, thumbnail: TypeThumbnail, is_sticker: bool,
        tgs_convert: Optional[dict], filename: Optional[str], encrypt: bool,
        parallel_id: Optional[int]) -> Optional[DBTelegramFile]:
    db_file = DBTelegramFile.get(loc_id)
    if db_file:
        return db_file

    if parallel_id and isinstance(location, Document) and (not is_sticker
                                                           or not tgs_convert):
        db_file = await parallel_transfer_to_matrix(client, intent, loc_id,
                                                    location, filename,
                                                    encrypt, parallel_id)
        mime_type = location.mime_type
        file = None
    else:
        try:
            file = await client.download_file(location)
        except (LocationInvalidError, FileIdInvalidError):
            return None
        except (AuthBytesInvalidError, AuthKeyInvalidError,
                SecurityError) as e:
            log.exception(f"{e.__class__.__name__} while downloading a file.")
            return None

        width, height = None, None
        mime_type = magic.from_buffer(file, mime=True)

        image_converted = False
        # A weird bug in alpine/magic makes it return application/octet-stream for gzips...
        if is_sticker and tgs_convert and (
                mime_type == "application/gzip" or
            (mime_type == "application/octet-stream"
             and magic.from_buffer(file).startswith("gzip"))):
            mime_type, file, width, height = await convert_tgs_to(
                file, tgs_convert["target"], **tgs_convert["args"])
            thumbnail = None
            image_converted = mime_type != "application/gzip"

        if mime_type == "image/webp":
            new_mime_type, file, width, height = convert_image(
                file,
                source_mime="image/webp",
                target_type="png",
                thumbnail_to=(256, 256) if is_sticker else None)
            image_converted = new_mime_type != mime_type
            mime_type = new_mime_type
            thumbnail = None

        decryption_info = None
        upload_mime_type = mime_type
        if encrypt and encrypt_attachment:
            file, decryption_info_dict = encrypt_attachment(file)
            decryption_info = EncryptedFile.deserialize(decryption_info_dict)
            upload_mime_type = "application/octet-stream"
        content_uri = await intent.upload_media(file, upload_mime_type)
        if decryption_info:
            decryption_info.url = content_uri

        db_file = DBTelegramFile(id=loc_id,
                                 mxc=content_uri,
                                 decryption_info=decryption_info,
                                 mime_type=mime_type,
                                 was_converted=image_converted,
                                 timestamp=int(time.time()),
                                 size=len(file),
                                 width=width,
                                 height=height)
    if thumbnail and (mime_type.startswith("video/")
                      or mime_type == "image/gif"):
        if isinstance(thumbnail, (PhotoSize, PhotoCachedSize)):
            thumbnail = thumbnail.location
        try:
            db_file.thumbnail = await transfer_thumbnail_to_matrix(
                client, intent, thumbnail, file, mime_type, encrypt)
        except FileIdInvalidError:
            log.warning(f"Failed to transfer thumbnail for {thumbnail!s}",
                        exc_info=True)

    try:
        db_file.insert()
    except (IntegrityError, InvalidRequestError) as e:
        log.exception(
            f"{e.__class__.__name__} while saving transferred file data. "
            "This was probably caused by two simultaneous transfers of the same file, "
            "and should not cause any problems.")
    return db_file