Interpause / auto-sd-krita

AUTOMATIC1111 webUI + Krita Plugin with superb Inpainting
MIT License
88 stars 4 forks source link

Simple xor encryption support (manual activation required) #54

Closed danilw closed 2 years ago

danilw commented 2 years ago

Encrypted data:

  1. Negative and Positive prompts (from Client to Server)
  2. images - from Client to Server and on the Server before sending back to Client.

WebUI does work, when enable Encryption WebUI keep working.

Instruction for activation:

  1. on Client (Krita plugin) - Open (in text editor) krita_plugin/krita_diff/utils.py in your plugin location, and edit (line 14-15): test_key = "test_encryption_key" - set your_text_key (any text key) use_encryption = False - set to True
  2. on Server - create file xor_pass.txt with your_text_key - same as in plugin above (at krita_config.yaml file location, top level) on Colab - echo "your_text_key" > xor_pass.txt

As result youl see on Server in terminal-output: (instead text prompts) image

danilw commented 2 years ago

I have question - I dont understand this moment in code: https://github.com/danilw/auto-sd-krita/blob/master/krita_server/utils.py#L236 - parse_prompt function

        if isinstance(val, str):
            return val
        if isinstance(val, list):
            return ", ".join(val)
        if isinstance(val, dict):

Question - how val can be list or dict if on client it always string? https://github.com/danilw/auto-sd-krita/blob/master/krita_plugin/krita_diff/utils.py#L17

def fix_prompt(prompt: str):
    """Multiline tokens -> comma-separated tokens. Replace empty prompts with None."""
    joined = ", ".join(filter(bool, [x.strip() for x in prompt.splitlines()]))
    if use_encryption and joined != "":
        joined = encrypt_xor(joined,test_key)
    return joined if joined != "" else None

and fix_prompt used to "prepare" prompt before sending to server in client.py https://github.com/danilw/auto-sd-krita/blob/master/krita_plugin/krita_diff/client.py

danilw commented 2 years ago

requirements.txt fix for https://github.com/Interpause/auto-sd-krita/issues/55

it keep working with this fix - just tested

Interpause commented 2 years ago

I have question - I dont understand this moment in code: https://github.com/danilw/auto-sd-krita/blob/master/krita_server/utils.py#L236 - parse_prompt function

        if isinstance(val, str):
            return val
        if isinstance(val, list):
            return ", ".join(val)
        if isinstance(val, dict):

Question - how val can be list or dict if on client it always string? https://github.com/danilw/auto-sd-krita/blob/master/krita_plugin/krita_diff/utils.py#L17

def fix_prompt(prompt: str):
    """Multiline tokens -> comma-separated tokens. Replace empty prompts with None."""
    joined = ", ".join(filter(bool, [x.strip() for x in prompt.splitlines()]))
    if use_encryption and joined != "":
        joined = encrypt_xor(joined,test_key)
    return joined if joined != "" else None

and fix_prompt used to "prepare" prompt before sending to server in client.py https://github.com/danilw/auto-sd-krita/blob/master/krita_plugin/krita_diff/client.py

For the first, its a remnant of the original code which allowed to specify prompts via the config file instead of through the UI. It had some alternative formats for the prompt and that part of the code parsed those formats.

For the second, entering tokens/tags line by line instead of using commas yourself will join them with commas (also remnant behaviour I thought made sense for some people). Setting the prompt to None when empty is to allow using prompts from the config file instead.

Either ways, they don't really do much anymore I guess.

Interpause commented 2 years ago

Okay, I will merge it for now. When I merge it downstream to the new plugin though, I probably will change when the encryption is done (for example img_to_b64 should only convert image to base64, it shouldn't also encrypt). It should probably be done right before and after the network parts.

Interpause commented 2 years ago

btw, nearly forgot, but thanks.

Interpause commented 2 years ago

Actually, you missed out the fact the prompt used is included in the response from the server (and isn't encrypted).

In the new plugin, I have decided to do the encryption on the request level by directly modifying the request body and indicating the method used via the request header. I think its safer and also less disruptive for the rest of the code.

That said, I am re-writing XOR to use bytes instead of strings and not encode it to base64 (given request bodies are bytes). It is also possible to do in one-line, and XOR encryption actually allows the same function used for encryption to decrypt as well.

from itertools import cycle
def bytewise_xor(msg: bytes, key: bytes):
    return bytes(v ^ k for v, k in zip(msg, cycle(key)))
Interpause commented 2 years ago

https://github.com/Interpause/auto-sd-paint-ext/commit/fb2f330f9fe0af7e8d2b771a6cc9c241cdb12c59

Took longer than expected but new plugin supports XOR encryption on the entire request/response body.