• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Шифрование через нейросети.

raoulduke666

RAID-массив
Пользователь
Регистрация
08.10.2024
Сообщения
74
Реакции
102
Автор: raoulduke666
Написано специально для xss.pro (c)

Привет, читатель.
Статья больше про исследование и тестирование метода, я не пытаюсь навязать все нижеописанное и не заявляю что это 100% рабочее шифрование. Но как по мне идея шифрования через обобщение нейросетей достойна внимания.

Обзор метода.
Само сообщение шифруется через симметричное шифрование AES-CBC, где нужен ключ (16 байт) и IV (начальный вектор, чтобы шифр был разным). Ключ генерируется из эмбеддингов, полученных моделью. Но особенность метода в том, что изображение не является секретом, любой желающий может увидеть изображение и зашифрованное сообщение. Но при этом изображение не является публичным ключом. Ключ может получить только конкретная модель нейросети.

Поэтому чтобы это работало, yolo-seg должен быть обучен на своем датасете. Одна и та же модель должна быть у обоих участников. Веса модели и датасет должны храниться в строгом секрете.

Любой человек в здравом уме скажет, что yolo не сможет уникально сегментировать изображения и на этом моменте закроет статью.
Эмбеддинги из 4 и 16 слоя это 192 числа, типа float32, обычно в диапазоне от -10 до 10. Самый важный момент, который позволяет создать уникальный ключ для каждого изображения. По крайней мере это позволяет учитывать малейшие изменения в изображении. Учитывается все изображение, сам класс и его фон. Даже если меняется всего 1 пиксель, создается уникальный ключ.

Провел тест из 1000 одинаковых изображений где рандомно заменены 1-5 пикселей. Результат в статье.

На 2 разных системах всегда генерируются одинаковые ключи к одним и тем же изображениям. Самое важное условие чтобы на изображении был класс, который может быть сегментирован, иначе ключа просто не будет и сообщение нельзя будет отправить.

Чтобы расшифровать сообщение нужно получить изображение, на основе которого был получен ключ и само зашифрованное сообщение. Через такую же модель yolo-seg получаются эмбеддинги чтобы повторить ключ.

Эмбеддинги yolo11n-seg.
В основе метода лежит использование эмбеддингов, полученных из модели YOLO11n-seg, для генерации ключа из изображения. Эмбеддинги представляют собой числовое описание изображения, которое позволяет учесть как общие характеристики (например, объект на изображении), так и мелкие детали (изменение одного пикселя). В этом разделе я подробно объясню, что такое эмбеддинги, как они формируются в модели YOLO11n-seg, почему выбраны слои 4 и 16, как обеспечивается их чувствительность к изменениям изображения и как они используются для создания уникального ключа.

Эмбеддинги — это компактное числовое представление изображения, которое извлекается из внутренних слоёв нейронной сети. В контексте метода они являются результатом обработки изображения моделью yolo11n-seg и представляют собой массив из 192 чисел с плавающей запятой (float32). Эти числа описывают различные аспекты изображения: объект (например, кот), фон, текстуры, края и даже отдельные пиксели. Каждое число отражает вклад определённой особенности изображения, выявленной нейросетью. Но самое важное что каждое число имеет свой диапазон влияния. И этот уровень влияния определяется датасетом, обучением, разметкой, пайлпайном обучения и инференса.

В моём методе эмбеддинги состоят из двух частей:
  • 128 чисел из 4 слоя. Отвечает за мелкие детали изображения, фон
  • 64 числа из 16 слоя, Описывает более крупные структуры, такие как форма класса, его цвет.
Диапазон значений чисел обычно находится в пределах от -10 до 10, хотя в зависимости от изображения и характеристик модели значения могут быть больше или меньше. Например, типичный фрагмент эмбеддингов для изображения кота может выглядеть следующим образом:
[0.1234, -0.5678, 1.2345, -0.0987, 2.3456, 0.6789, -1.5432, 0.4321, ...]
Если изменить один пиксель изображения, например, с [100, 100, 100] на [101, 100, 100], некоторые числа в эмбеддингах слегка изменятся:
[0.1235, -0.5678, 1.2344, -0.0987, 2.3457, 0.6789, -1.5431, 0.4321, ...]
Эти небольшие изменения играют ключевую роль: они обеспечивают чувствительность метода к минимальным различиям в изображении. Это позволяет генерировать уникальный ключ для каждой версии изображения.

Формирование эмбеддингов.
YOLO11n-seg изначально нейронная сеть, предназначенная для обнаружения и сегментации объектов. Она состоит из 23 слоёв, каждый из которых обрабатывает изображение на разном уровне абстракции. В моём методе я использую не итоговые маски сегментации, а промежуточные активации двух слоёв — 4 и 16, которые извлекаются во время обработки изображения.

Сперва загружается изображение.
Изображение (maryJane.png размером 1088x1920 пикселей) загружается и преобразуется в тензор с тремя каналами (RGB). Тензор имеет размер [1, 3, 1088, 1920], где:
  • 1 количество изображений,
  • 3 каналы цвета,
  • 1088 потому что 1080 не кратно 32, а самое близкое число 1088. Это связано с архитектурой модели.
Обработка через yolo11n-seg.
Модель прогоняет тензор через свои слои. Каждый слой выполняет операции свёртки, нормализации и активации, создавая всё более абстрактные представления изображения.
На выходе слоя 4 получается тензор размером [1, 128, 34, 60], где:
  • 128 — количество каналов,
  • 34x60 — уменьшенное пространственное разрешение изображения.
На выходе слоя 16 — тензор [1, 64, 17, 30], где:
  • 64 — каналы,
  • 17x30 — ещё меньшее разрешение.
Извлечение активаций.
Я использую механизм хуков (register_forward_hook) для получения активаций слоёв 4 и 16 во время обработки изображения. Это позволяет заглянуть внутрь модели и извлечь промежуточные данные, которые более уникальны чем итоговый результат сегментации.

Python:
activations = {}
layers[4].register_forward_hook(lambda m, i, o: activations.update({"layer_4": o}))
layers[16].register_forward_hook(lambda m, i, o: activations.update({"layer_16": o}))
with torch.no_grad():
    model(image, conf=0.4, imgsz=(1088, 1920))

Усреднение активаций.
Чтобы получить массив из 192 чисел (128 + 64), активации каждого слоя усредняются по пространственным измерениям (высоте и ширине).
  • Для слоя 4: тензор [1, 128, 34, 60] → массив [128] Усреднение по 34x60,
  • Для слоя 16: тензор [1, 64, 17, 30] → массив [64] Усреднение по 17x30
Python:
embeddings = [activations[k].mean(dim=[2, 3]) for k in ["layer_4", "layer_16"]]

Округление.
На разных системах результат может отличаться. Вычисления на GPU и CPU дают разные результаты. Поэтому они округляются до 4 знаков после запятой. Это по прежнему сохраняет устойчивость к изменению даже 1 пикселя.
Python:
flat_embeddings = np.round(torch.cat(embeddings).cpu().numpy(), decimals=4)

Почему выбраны слои 4 и 16?
Выбор слоёв 4 и 16 не случаен.
  • Слой 4: Ранний слой модели, который обрабатывает изображение на низком уровне. Он чувствителен к мелким деталям, таким как отдельные пиксели, края, текстуры и локальные цветовые переходы. Активации слоя 4 содержат 128 каналов, что позволяет уловить широкий спектр низкоуровневых признаков.
  • Слой 16: Более глубокий слой, который анализирует изображение на высоком уровне. Он распознаёт крупные структуры, такие как форма объекта (например, кота), его поза и общий контекст сцены. Активации слоя 16 содержат 64 канала, что достаточно для описания объектов, но менее детализировано, чем слой 4.
Возможно стоит попробовать комбинации других слоев, но с этими все в порядке пока что. Меня результат удовлетворил.

Чувствительность YOLO11n-seg.
Изменение одного пикселя, например с [100, 100, 100] на [101, 100, 100]) влияет на активации слоя 4, потому что он анализирует локальные детали. Это изменение распространяется на тензор [1, 128, 34, 60], слегка сдвигая некоторые значения.

Слой 16 менее чувствителен к отдельным пикселям, но может уловить изменения, если они влияют на форму объекта или контекст

После усреднения сдвиг в активациях приводит к изменению одного или нескольких чисел в эмбеддингах.

Работа с SHA-256.
Эмбеддинги (192 числа) преобразуются в байты и хешируются с помощью SHA-256.

SHA-256 чувствителен к входным данным, изменение одного числа в эмбеддингах на 0.0001 приводит к совершенно другому хешу. Это гарантирует, что ключ и IV для каждой версии изображения будут уникальными.

Python:
hash_digest = hashlib.sha256(flat_embeddings.tobytes()).digest()
key = hash_digest[:16]  # Ключ (16 байт)
iv = hash_digest[16:32]  # IV (16 байт)

Пример.
  • Исходное изображение, эмбеддинги [0.1234, -0.5678, 1.2345, ...]. Ключ c87bce1c05dd27f6f9c256399c43aade.
  • Изображение с изменённым пикселем, эмбеддинги [0.1235, -0.5678, 1.2344, ...]. Ключ 1279567999291717328cd268d7bde881.​
Тестирование уникальности ключа.
Как обсуждалось выше, ключевая проблема, уникальность ключей для каждого изображения.
Чтобы проверить метод я сгенерировал 1000 копий изображений своей кошки, заменив на каждом по 1-5 пикселей рандомно, RGB-значения выбирались случайно.

Python:
import os
import random
from PIL import Image

def modify_random_pixels(image_path, output_dir, iterations):
    # Открываем исходное изображение
    original_img = Image.open(image_path)
    width, height = original_img.size
    img_format = original_img.format  # Переименовано, чтобы избежать конфликта с функцией format()
 
    # Создаем папку для результатов, если её нет
    os.makedirs(output_dir, exist_ok=True)
 
    for i in range(iterations):
        # Создаем копию исходного изображения
        modified_img = original_img.copy()
        pixels = modified_img.load()
 
        # Определяем количество пикселей для изменения (1-5)
        num_pixels_to_change = random.randint(1, 5)
 
        for _ in range(num_pixels_to_change):
            # Выбираем случайный пиксель
            x = random.randint(0, width - 1)
            y = random.randint(0, height - 1)
    
            # Генерируем случайный цвет
            if modified_img.mode == 'RGB':
                new_color = (
                    random.randint(0, 255),
                    random.randint(0, 255),
                    random.randint(0, 255)
                )
            elif modified_img.mode == 'RGBA':
                new_color = (
                    random.randint(0, 255),
                    random.randint(0, 255),
                    random.randint(0, 255),
                    pixels[x, y][3]  # Сохраняем альфа-канал
                )
            else:
                # Для других режимов (например, grayscale) преобразуем в RGB
                modified_img = modified_img.convert('RGB')
                pixels = modified_img.load()
                new_color = (
                    random.randint(0, 255),
                    random.randint(0, 255),
                    random.randint(0, 255)
                )
    
            # Изменяем пиксель
            pixels[x, y] = new_color
 
        # Сохраняем результат
        base_name = os.path.splitext(os.path.basename(image_path))[0]
        output_path = os.path.join(output_dir, f"{base_name}_mod_{i+1}.{img_format.lower()}")
        modified_img.save(output_path, format=img_format)
 
        print(f"Создано: {output_path} (изменено {num_pixels_to_change} пикселей)")

if __name__ == "__main__":
    input_image = "images/maryJane.png"  # Путь к исходному изображению
    output_directory = "new_images"  # Папка для сохранения результатов
    num_iterations = 1000  # Количество изображений для генерации
 
    modify_random_pixels(input_image, output_directory, num_iterations)

Как только изображения сгенерировались, запускаю код который симулирует работу шифрования, а в конце сравнивает результаты. Если хотя бы 1 раз результаты повторятся, метод не рабочий. Можно попробовать учесть эмбеддинги из еще одного слоя, но пока нет нужды делать этого не нужно.

Мы все прекрасно понимаем, что метод и так не рабочий, рано или поздно копии ключей случатся, но ведь интересно когда это случится. По большей части это обусловлено обучением и датасетом, чем больше и разнообразнее тем лучше результат в плане уникальности в теории. Но есть еще очень большое количество параметров, которые могут влиять на это.

Размер обучаемой модели.
Разнообразие датасета и общее количество изображений.
Качество разметки.
Пайплайн обучения.
Пайплайн инференса.

Теперь проведем тест.
Python:
import cv2
import numpy as np
import torch
from ultralytics import YOLO
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import hashlib
import os
from collections import defaultdict
import time

# Настройка детерминизма
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

# Ограничение числа ядер CPU (например, 2 ядра)
torch.set_num_threads(4)  # Уменьшает нагрузку на CPU

def generate_key_and_iv(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    hash_digest = hashlib.sha256(flat_embeddings.tobytes()).digest()
    return hash_digest[:16], hash_digest[16:32]

def encrypt_data(data, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct_bytes = cipher.encrypt(pad(data, AES.block_size))
    return ct_bytes

def process_image(image_path, model):
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError(f"Не удалось загрузить изображение: {image_path}")
    original_shape = image.shape[:2]
    yolo_size = (max(32, (original_shape[0] + 31) // 32 * 32), max(32, (original_shape[1] + 31) // 32 * 32))
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.uint8)
    image_tensor = torch.from_numpy(image_rgb).permute(2, 0, 1).float() / 255.0
    image_tensor = image_tensor.clamp(0.0, 1.0).unsqueeze(0).to(model.device)
    activations = {}
    def get_activation(name):
        def hook(module, input, output):
            activations[name] = output
        return hook
    layers = model.model.model
    layers[4].register_forward_hook(get_activation("layer_4"))
    layers[16].register_forward_hook(get_activation("layer_16"))
    with torch.no_grad():
        results = model(image, conf=0.4, imgsz=yolo_size)
    embeddings = []
    for layer_name in ["layer_4", "layer_16"]:
        activation = activations.get(layer_name, torch.zeros(1))
        embedding = activation.mean(dim=[2, 3]) if len(activation.shape) == 4 else torch.zeros(1)
        embeddings.append(embedding)
    return embeddings

def test_images(image_folder, model, message, delay=0.5):
    results = []
    image_files = [f for f in os.listdir(image_folder) if f.endswith('.png')]
    if len(image_files) != 1000:
        print(f"Предупреждение: Найдено {len(image_files)} изображений, ожидалось 1000")

    # Прогоняем каждое изображение
    for idx, image_file in enumerate(image_files, 1):
        image_path = os.path.join(image_folder, image_file)
        print(f"Обработка изображения {idx}/1000: {image_file}")
        try:
            embeddings = process_image(image_path, model)
            key, iv = generate_key_and_iv(embeddings)
            ct_bytes = encrypt_data(message, key, iv)
            results.append({
                'image': image_file,
                'key': key.hex(),
                'iv': iv.hex(),
                'ciphertext': ct_bytes.hex()
            })
            # Задержка для снижения нагрузки
            time.sleep(delay)  # Пауза 0.5 секунды между изображениями
        except Exception as e:
            print(f"Ошибка при обработке {image_file}: {str(e)}")

    # Проверяем уникальность ключей
    key_to_images = defaultdict(list)
    for result in results:
        key_to_images[result['key']].append(result['image'])

    # Выводим результаты
    print("\n=== Результаты теста ===")
    print(f"Обработано изображений: {len(results)}")
    print(f"Уникальных ключей: {len(key_to_images)}")

    duplicates_found = False
    for key, images in key_to_images.items():
        if len(images) > 1:
            duplicates_found = True
            print(f"\nОШИБКА: Одинаковый ключ найден для {len(images)} изображений!")
            print(f"Ключ: {key}")
            print("Изображения:")
            for img in images:
                print(f"  - {img}")

    if not duplicates_found:
        print("\nУСПЕХ: Все ключи уникальны! Метод прошёл тест.")
    else:
        print("\nПРОВАЛ: Найдены одинаковые ключи. Метод недостаточно чувствителен!")

    # Выводим все результаты
    print("\nПодробные результаты:")
    for result in results:
        print(f"Изображение: {result['image']}")
        print(f"  Ключ: {result['key']}")
        print(f"  IV: {result['iv']}")
        print(f"  Шифротекст: {result['ciphertext']}\n")

    return results, duplicates_found

# Основной код
device = torch.device("cpu")
model = YOLO("yolo11n-seg.pt").to(device)
image_folder = "modified_images"  # Папка с 1000 изображениями
message = "XSS,is".encode()
delay = 0.5  # Задержка в секундах

results, duplicates_found = test_images(image_folder, model, message, delay)

Результат ?
Снимок экрана 2025-05-04 125803.png

Тестирование пройдено успешно, 0 копий. В следующем попробую собрать просто разные, но похожие изображения, без замены пикселей. Для меня такой результат говорит о том что метод имеет место быть или как минимум еще одно тестирование можно провести.

Про безопасность.
В своей статье я опираюсь на результаты предобученной модели yolo11n-seg. В боевых условия учить ее нужно самому, на кастомном датасете, объяснять думаю не нужно.
Датасет частично должен быть собран в реальной жизни буквально. Можно шифроваться на чем угодно. Какие самые популярные изображения в сети ? Голые девки и кошки. Уловили ? Идете на улице и делаете фотографии котов :) Затем мешаете с фотографиями из сети, желательно так чтобы зацепить в датасет как можно больше пород.

Понимаю, что это та еще задница, но метод считается "околобезопасным" только при таких условиях.
Если размечать лень берите самую маленькую модель и делайте сегментацию на 500 изображениях. Тут можно сделать разметку намеренно криво, в теории признаки должны стать более непредсказуемыми. А чтобы модель сегментировала все изображения, то нужно выкрутить conf=0.2, так появится неуверенная, кривая, непредсказуемая сегментация.
Даже если второе тестирование будет тоже успешным и получится доказать решение проблем с коллизией окончательно. То это не отменяет того факта, что если атакующий получит доступ к модели, то вся переписка тут же будет расшифрована. Поэтому ему нужна не только модель, но и пайплайн инференса и вообще код в целом. Иначе взлом шифра займет какое то время. Откуда атакующий узнает про 4 и 16 слой ? Из этой статьи :D, но это уже другая история.

Реализация.

В предыдущих разделах я описал, как эмбеддинги, полученные из модели yolo11n-seg, используются для генерации ключа. Теперь я опишу полный код шифрования и расшифровки, а затем подробно объясню, как он работает, опираясь на принципы, описанные ранее.
Код показывает, как изображение превращается в ключ для шифрования сообщения через AES-CBC, и как то же изображение используется для расшифровки.

Шифрование.

Python:
import cv2
import numpy as np
import torch
from ultralytics import YOLO
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import hashlib

# Настройка детерминизма
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

def generate_key_and_iv(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    hash_digest = hashlib.sha256(flat_embeddings.tobytes()).digest()
    return hash_digest[:16], hash_digest[16:32]

def encrypt_data(data, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct_bytes = cipher.encrypt(pad(data, AES.block_size))
    return ct_bytes

def process_image(image_path, model):
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError("Не удалось загрузить изображение!")
    original_shape = image.shape[:2]
    yolo_size = (max(32, (original_shape[0] + 31) // 32 * 32), max(32, (original_shape[1] + 31) // 32 * 32))
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.uint8)
    image_tensor = torch.from_numpy(image_rgb).permute(2, 0, 1).float() / 255.0
    image_tensor = image_tensor.clamp(0.0, 1.0).unsqueeze(0).to(model.device)
    activations = {}
    def get_activation(name):
        def hook(module, input, output):
            activations[name] = output
        return hook
    layers = model.model.model
    layers[4].register_forward_hook(get_activation("layer_4"))
    layers[16].register_forward_hook(get_activation("layer_16"))
    with torch.no_grad():
        results = model(image, conf=0.4, imgsz=yolo_size)
    embeddings = []
    for layer_name in ["layer_4", "layer_16"]:
        activation = activations.get(layer_name, torch.zeros(1))
        embedding = activation.mean(dim=[2, 3]) if len(activation.shape) == 4 else torch.zeros(1)
        embeddings.append(embedding)
    return embeddings

# Шифрование
device = torch.device("cpu")
model = YOLO("yolo11n-seg.pt").to(device)
image_path = "images/maryJane.png"
message = "xss.pro".encode()

embeddings = process_image(image_path, model)
key, iv = generate_key_and_iv(embeddings)
ct_bytes = encrypt_data(message, key, iv)

# Сохранение шифротекста и IV
with open("ciphertext.bin", "wb") as f:
    f.write(iv + ct_bytes)  # IV (16 байт) + шифротекст

print("Ключ:", key.hex())
print("IV:", iv.hex())
print("Шифротекст:", ct_bytes.hex())

Код реализует метод шифрования, описанный в статье, и состоит из трёх ключевых функций:
  • Обработка изображения (process_image),
  • Генерация ключа и IV (generate_key_and_iv),
  • Шифрование и расшифровка (encrypt_data, decrypt_data). Я разберу каждую часть, связав её с предыдущими разделами.
Настройка детерминизма.
В начале кода включается детерминизм для PyTorch, чтобы эмбеддинги были одинаковыми на разных системах.

Python:
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

Как писал ранее в разделе про эмбеддинги, это устраняет вычислительные различия между CPU и GPU, обеспечивая одинаковые ключи для одного изображения. Без детерминизма эмбеддинги могут слегка отличаться, что приведёт к неверному ключу при расшифровке.

Обработка изображения (process_image)
Функция process_image загружает изображение и извлекает эмбеддинги из слоёв 4 и 16 модели YOLO11n-seg.

Загрузка. Изображение (maryJane.png, 1088x1920) читается с помощью OpenCV и преобразуется в тензор [1, 3, 1088, 1920]. Размер 1088 кратен 32, так требует архитектура yolo.

Извлечение активаций. Хуки (register_forward_hook) захватывают активации слоёв 4 и 16

Python:
layers[4].register_forward_hook(get_activation("layer_4"))
layers[16].register_forward_hook(get_activation("layer_16"))

На выходе. Слой 4 - тензор [1, 128, 34, 60], слой 16 - [1, 64, 17, 30].

Усреднение. Активации усредняются по высоте и ширине, давая 128 чисел (слой 4) и 64 числа (слой 16). В итоге 192 числа (float32, диапазон -10 до 10).

Функция возвращает эмбеддинги, которые затем используются для генерации ключа.

Генерация ключа и IV (generate_key_and_iv)
Функция преобразует эмбеддинги в ключ (16 байт) и IV (16 байт) для AES-CBC.

Python:
def generate_key_and_iv(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    hash_digest = hashlib.sha256(flat_embeddings.tobytes()).digest()
    return hash_digest[:16], hash_digest[16:32]

Объединение. Эмбеддинги (128 + 64 числа) объединяются в массив из 192 чисел.

Округление. Числа округляются до 4 знаков (decimals=4), чтобы устранить шум, сохраняя чувствительность к пикселям. Еще я это делал для того чтобы запустить на разных системах.

Хеширование. Массив преобразуется в байты и хешируется через SHA-256, давая 32 байта. Первые 16 байт — ключ, следующие 16 — IV.
SHA-256 обеспечивает уникальность хэша. Изменение одного пикселя меняет эмбеддинги.

Шифрование (encrypt_data)
Функция шифрует сообщение через AES-CBC.

Код.
Python:
def encrypt_data(data, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct_bytes = cipher.encrypt(pad(data, AES.block_size))
    return ct_bytes

Вход. Сообщение ("xss.pro".encode()), ключ и IV.
Паддинг. Сообщение дополняется до кратности 16 байт.
Шифрование. AES-CBC шифрует сообщение, используя ключ и IV
Выход. "9c901f2b17cd0a91a9ef3995310db530"

Расшифровка.
Используется то же самое изображение, модель ИИ и зашифрованное сообщение.
Python:
import cv2
import numpy as np
import torch
from ultralytics import YOLO
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlib

# Настройка детерминизма
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

def generate_key_and_iv(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    hash_digest = hashlib.sha256(flat_embeddings.tobytes()).digest()
    return hash_digest[:16], hash_digest[16:32]

def decrypt_data(ct_bytes, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    pt_bytes = unpad(cipher.decrypt(ct_bytes), AES.block_size)
    return pt_bytes

def process_image(image_path, model):
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError("Не удалось загрузить изображение!")
    original_shape = image.shape[:2]
    height, width = original_shape
    yolo_size = (max(32, (height + 31) // 32 * 32), max(32, (width + 31) // 32 * 32))
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.uint8)
    image_tensor = torch.from_numpy(image_rgb).permute(2, 0, 1).float() / 255.0
    image_tensor = image_tensor.clamp(0.0, 1.0).unsqueeze(0).to(model.device)
    activations = {}
    def get_activation(name):
        def hook(module, input, output):
            activations[name] = output
        return hook
    layers = model.model.model
    layers[4].register_forward_hook(get_activation("layer_4"))
    layers[16].register_forward_hook(get_activation("layer_16"))
    with torch.no_grad():
        results = model(image, conf=0.4, imgsz=yolo_size)
    embeddings = []
    for layer_name in ["layer_4", "layer_16"]:
        activation = activations.get(layer_name, torch.zeros(1))
        if len(activation.shape) == 4:
            embedding = activation.mean(dim=[2, 3])
        else:
            embedding = torch.zeros(1)
        embeddings.append(embedding)
    return embeddings

# Основной код расшифровки
device = torch.device("cpu")
model = YOLO("yolo11n-seg.pt").to(device)
image_path = "images/mary1_1_modified_10.png"
ct_bytes = bytes.fromhex("9c901f2b17cd0a91a9ef3995310db530")  # Замените на реальное значение

# Расшифровка
embeddings = process_image(image_path, model)
key, iv = generate_key_and_iv(embeddings)
decrypted_message = decrypt_data(ct_bytes, key, iv)
print("Расшифрованное сообщение:", decrypted_message.decode())

Настройка детерминизма
Как и в шифровании, код начинается с настройки детерминизма PyTorch.

Python:
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

Это гарантирует, что эмбеддинги, полученные из изображения (maryJane.png), будут такими же, как при шифровании. Без детерминизма даже незначительные вычислительные различия при вычислениях на CPU и GPU дадут разные эмбеддинги, что приведёт к неверному ключу и провалу расшифровки.

Обработка изображения (process_image)
Функция process_image идентична той, что используется в шифровании. Она загружает изображение и извлекает эмбеддинги из слоёв 4 и 16 модели.

Python:
layers[4].register_forward_hook(get_activation("layer_4"))
layers[16].register_forward_hook(get_activation("layer_16"))

Функция возвращает эмбеддинги, которые должны совпадать с теми, что использовались при шифровании, если изображение не изменилось.

Генерация ключа и IV (generate_key_and_iv)
Функция идентична шифрованию.

Python:
def generate_key_and_iv(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    hash_digest = hashlib.sha256(flat_embeddings.tobytes()).digest()
    return hash_digest[:16], hash_digest[16:32]

Расшифровка (decrypt_data)
Функция восстанавливает сообщение через AES-CBC.

Python:
def decrypt_data(ct_bytes, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    pt_bytes = unpad(cipher.decrypt(ct_bytes), AES.block_size)
    return pt_bytes

Вход. Зашифрованное сообщение (например, 9c901f2b17cd0a91a9ef3995310db530), ключ и IV.
Расшифровка. AES-CBC декодирует данные, используя ключ и IV, полученные от эмбеддингов.
Удаление паддинга. Функция unpad убирает дополнение, добавленное при шифровании.
Выход. Исходное сообщение. "xss.pro"

Возможно будет 2 часть с автоматизацией разметки и изоляцией ИИ.
 
Последнее редактирование:
шиза конечно, но за старания лайк.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Поэтому чтобы это работало, yolo-seg должен быть обучен на своем датасете. Одна и та же модель должна быть у обоих участников. Веса модели и датасет должны храниться в строгом секрете
Оу шит, хир ви гой эген. От этих вот творческих подходов вокруг криптографии, вы все не делаете ничего лучше, чем уже сделано в криптографии.

Ты вместо того, чтобы просто использовать AES ключ просто сделал веса нейронки ключом, от этого ничего не произошло, кроме того, что вместо 16/32 байт ключа тебе нужно держать в секрете веса нейронки. То есть просто менее удобно, без каких либо преимуществ.

Но большая проблема в том, что как бы криптостойкость AES проверена и доказана, а "криптостойкость" твоей нейронки никто не проверял. Ты ничего не усилил, тк ключ все равно 16/32 байта в конце, а скорее ослабил, поскольку эти преобразования наиболее вероятно имеют какую-либо уязвимость
 
По факту с такими же целями можно сделать свою блочную хэшфункцию, передавать в нее байты картинки и на выходе получать значение фиксированной длины (например, 256 для aes), которое может быть использовано как ключ
 
Оу шит, хир ви гой эген. От этих вот творческих подходов вокруг криптографии, вы все не делаете ничего лучше, чем уже сделано в криптографии.

Ты вместо того, чтобы просто использовать AES ключ просто сделал веса нейронки ключом, от этого ничего не произошло, кроме того, что вместо 16/32 байт ключа тебе нужно держать в секрете веса нейронки. То есть просто менее удобно, без каких либо преимуществ.

Но большая проблема в том, что как бы криптостойкость AES проверена и доказана, а "криптостойкость" твоей нейронки никто не проверял. Ты ничего не усилил, тк ключ все равно 16/32 байта в конце, а скорее ослабил, поскольку эти преобразования наиболее вероятно имеют какую-либо уязвимость
Так в начале указываю, что это не больше чем исследование и тд.

Мне изначально казалось, что пролблема только в коллизии и что это невозможно исправить, а получилось что через эмбеддинги можно получить более уникальный путь обобщения чем сам результат сегментации. То есть статья не совсем про шифрование, а скорее про нейронку в контексте шифрования.

А насчет уязвимости из за преобразований с 4 и 16 слоем тут сложно сказать. Скорее атакующий может просто угадать какой объект сегментируется, но каким образом это делается ему только гадать, скорее всего очень долго.

Просто забавно, что это работает и ключ уникален даже при малейших изменениях в изображении. А в остальном из описания метода понятно что это не юзабильно. Даже сам факт, что тебе нужно делать сегментацию около суток на своем датасете уже смущает.
 
Последнее редактирование:
Хочу рассказать о паре моментов которые мне кажется в твоей статье либо не особо акцентированы либо могли бы быть покруче. Первое что хочу сказать подход у тебя огонь но есть один косяк который ты похоже сам не особо заметил. Идея использовать эмбеддинги модели YOLO11nseg для генерации ключей AESCBC это просто бомба. Но есть загвоздка если я сяду это реализовывать в какойто момент могу упереться в стену. Почему. Потому что твой метод сильно зависит от датасета и обученной модели. Если датасет чуть менее разнообразный или скажем ты обучишь другую модель на том же датасете есть крошечный шанс что ключи начнут повторяться. Ты в статье об этом както вскользь упомянул а я бы на твоём месте добавил целую секцию про впрыскивание квантового шума в датасет.

Вот как это работает ты берёшь свой датасет где допустим у тебя котики для генерации ключей и добавляешь к пикселям квантовый шум. Например пишешь алгоритм который рандомно меняет каждый пиксель с помощью квантовых фишек. Что такое квантовое изменение. Это типа изменение основанное на принципах квантовой механики. Можно например использовать квантовую волновую функцию которая добавляет каждому пикселю случайную фазу. В итоге твой датасет получает такой квантовый вайб и эмбеддинги в слоях 4 и 16 становятся более непредсказуемыми. Это почти сводит вероятность коллизий ключей к нулю.

Ты просто сказал что датасет должен быть разнообразным но мой подход это на уровень глубже. Никто в криптографии ещё такого не пробовал потому что все думают что квантовка это только для квантовых компов. А я говорю можно и в классической системе это заюзать. Ещё одна штука которую ты не упомянул твой метод может быть уязвим к атакам обратного моделирования. Смотри если я хакер и хочу взломать твою систему я не буду копаться в твоем датасете или модели. Я просто сделаю обратную модель которая угадает эмбеддинги из слоёв 4 и 16. Это можно провернуть с помощью дифференциального анализа эмбеддингов. Я буду смотреть как мелкие изменения в картинках влияют на твой ключ. Если я смогу натренировать модель которая это предскажет твоя система всё кранты. Ты подумал что достаточно спрятать модель и датасет но я говорю это не панацея. На твоём месте я бы добавил защитный слой. Например можно прикрутить функцию квантового рассеяния к выходным эмбеддингам. Она разбрасывает выходы в пространство большей размерности так что даже если кто попробует сделать обратку он просто застрянет. Это ещё одна идея до которой в криптографии пока никто не додумался как мне кажется.

Ещё момент где можно было бы улучшить ты слишком зациклился на слоях 4 и 16. Я бы на твоём месте сделал динамический выбор слоёв. Например написал бы алгоритм который сам решает какие слои использовать для эмбеддингов в зависимости от входной картинки. Если картинка сложная с кучей деталей я бы брал нижние слои типа 3 или 4. А если картинка простая просто кот посередине то верхние типа 16 или 18. Так ключи станут адаптивными и безопасность взлетит. Это тоже момент над которым стоит подумать.

И последнее в части реализации ты немного упростил. Сказал что используешь SHA256 для генерации ключа и IV. Можно было бы задизайнить квантовую хешфункцию. Это типа хешфункция которая вместо классических алгоритмов вроде SHA256 использует квантовую модель для генерации хеша. Например можно заюзать квантовую сеть из квантовых гейтов которая берёт эмбеддинги на вход и выдаёт 256битный хеш. Этот хеш будет настолько непредсказуемым что даже атака на коллизии станет почти невозможной. Ещё одна идея которой в криптографии пока не пахнет.

Короче твой метод крутой но если бы я его дорабатывал я бы добавил всё что выше сказал. Тогда твоя система стала бы вообще неубиваемой и супернстоящей.

Теперь когда я всё это вывалил и добавил кучу идей к твоей статье хочу часть из них закодить. Особенно впрыскивание квантового шума в датасет и динамический выбор слоёв для эмбеддингов. Плюс немного подкручу основной код шифрования чтобы добавить простую квантовую хешфункцию. Я хочу чтобы каждый пиксель картинки получил квантовое изменение чтобы эмбеддинги стали ещё более непредсказуемыми. Напишу функцию которая использует простую квантовую волновую функцию и добавляет случайную фазу к каждому пикселю. Потом разберёмся с динамическим выбором слоёв и в конце добавлю простую квантовую хешфункцию....
Python:
import cv2
import numpy as np
import torch
from ultralytics import YOLO
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import hashlib
import cmath

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

def apply_quantum_noise(image):
    height, width, _ = image.shape
    noisy_image = image.copy().astype(np.float32)
    for i in range(height):
        for j in range(width):
            for c in range(3):
                pixel_value = noisy_image[i, j, c]
                phase = np.random.uniform(0, 2 * np.pi)
                wave_function = cmath.rect(1, phase)
                noise = (wave_function.real + wave_function.imag) * 0.1
                noisy_image[i, j, c] = np.clip(pixel_value + noise * pixel_value, 0, 255)
    return noisy_image.astype(np.uint8)

def analyze_image_complexity(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)
    edge_density = np.sum(edges) / (edges.shape[0] * edges.shape[1])
    return edge_density

def select_dynamic_layers(edge_density):
    if edge_density > 0.1:
        return [3, 4, 5]
    else:
        return [15, 16, 18]

def quantum_hash(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    quantum_state = 0
    for val in flat_embeddings:
        phase = (val + 10) * np.pi / 20
        quantum_state += cmath.rect(1, phase).real
    quantum_state = (quantum_state % 1) * 1e9
    hash_input = str(quantum_state).encode()
    hash_digest = hashlib.sha256(hash_input).digest()
    return hash_digest[:16], hash_digest[16:32]

def encrypt_data(data, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct_bytes = cipher.encrypt(pad(data, AES.block_size))
    return ct_bytes

def process_image(image_path, model):
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError("Изображение не загружено!")
    image_with_noise = apply_quantum_noise(image)
    edge_density = analyze_image_complexity(image_with_noise)
    selected_layers = select_dynamic_layers(edge_density)
    print(f"Выбранные слои: {selected_layers}")
    original_shape = image.shape[:2]
    yolo_size = (max(32, (original_shape[0] + 31) // 32 * 32), max(32, (original_shape[1] + 31) // 32 * 32))
    image_rgb = cv2.cvtColor(image_with_noise, cv2.COLOR_BGR2RGB).astype(np.uint8)
    image_tensor = torch.from_numpy(image_rgb).permute(2, 0, 1).float() / 255.0
    image_tensor = image_tensor.clamp(0.0, 1.0).unsqueeze(0).to(model.device)
    activations = {}
    def get_activation(name):
        def hook(module, input, output):
            activations[name] = output
        return hook
    layers = model.model.model
    for layer_idx in selected_layers:
        layers[layer_idx].register_forward_hook(get_activation(f"layer_{layer_idx}"))
    with torch.no_grad():
        results = model(image, conf=0.4, imgsz=yolo_size)
    embeddings = []
    for layer_idx in selected_layers:
        layer_name = f"layer_{layer_idx}"
        activation = activations.get(layer_name, torch.zeros(1))
        embedding = activation.mean(dim=[2, 3]) if len(activation.shape) == 4 else torch.zeros(1)
        embeddings.append(embedding)
    return embeddings

device = torch.device("cpu")
model = YOLO("yolo11n-seg.pt").to(device)
image_path = "images/maryJane.png"
message = "xss.pro".encode()

embeddings = process_image(image_path, model)
key, iv = quantum_hash(embeddings)
ct_bytes = encrypt_data(message, key, iv)

print("Ключ:", key.hex())
print("IV:", iv.hex())
print("Зашифрованное сообщение:", ct_bytes.hex())
Окей давай разберём этот код по косточкам! Функция apply_quantum_noise добавляет к изображению квантовый шум. Я использовал квантовую волновую функцию с рандомной фазой которая слегка изменяет каждый пиксель. Это делает эмбеддинги которые генерит модель YOLO более непредсказуемыми и шансов на коллизии ключей становится меньше.


Потом функция analyze_image_complexity оценивает сложность изображения с помощью алгоритма Canny для детекции краёв. Если картинка сложная с кучей краёв она выбирает нижние слои модели 3 4 5. Если простая то верхние 15 16 18. Это делает ключ адаптивным и безопасность растёт.


А quantum_hash это вообще крутая штука! Вместо обычного SHA-256 я сделал что-то вроде квантового подхода для хеширования. Простая модель которая переводит эмбеддинги в квантовое пространство и потом хеширует. Это делает хеш ещё более непредсказуемым.


Если запустишь код увидишь что ключи и IV получаются другие по сравнению с твоим методом. За счёт квантового шума и динамического выбора слоёв безопасность на уровень выше. Что думаешь?
 
Хочу рассказать о паре моментов которые мне кажется в твоей статье либо не особо акцентированы либо могли бы быть покруче. Первое что хочу сказать подход у тебя огонь но есть один косяк который ты похоже сам не особо заметил. Идея использовать эмбеддинги модели YOLO11nseg для генерации ключей AESCBC это просто бомба. Но есть загвоздка если я сяду это реализовывать в какойто момент могу упереться в стену. Почему. Потому что твой метод сильно зависит от датасета и обученной модели. Если датасет чуть менее разнообразный или скажем ты обучишь другую модель на том же датасете есть крошечный шанс что ключи начнут повторяться. Ты в статье об этом както вскользь упомянул а я бы на твоём месте добавил целую секцию про впрыскивание квантового шума в датасет.

Вот как это работает ты берёшь свой датасет где допустим у тебя котики для генерации ключей и добавляешь к пикселям квантовый шум. Например пишешь алгоритм который рандомно меняет каждый пиксель с помощью квантовых фишек. Что такое квантовое изменение. Это типа изменение основанное на принципах квантовой механики. Можно например использовать квантовую волновую функцию которая добавляет каждому пикселю случайную фазу. В итоге твой датасет получает такой квантовый вайб и эмбеддинги в слоях 4 и 16 становятся более непредсказуемыми. Это почти сводит вероятность коллизий ключей к нулю.

Ты просто сказал что датасет должен быть разнообразным но мой подход это на уровень глубже. Никто в криптографии ещё такого не пробовал потому что все думают что квантовка это только для квантовых компов. А я говорю можно и в классической системе это заюзать. Ещё одна штука которую ты не упомянул твой метод может быть уязвим к атакам обратного моделирования. Смотри если я хакер и хочу взломать твою систему я не буду копаться в твоем датасете или модели. Я просто сделаю обратную модель которая угадает эмбеддинги из слоёв 4 и 16. Это можно провернуть с помощью дифференциального анализа эмбеддингов. Я буду смотреть как мелкие изменения в картинках влияют на твой ключ. Если я смогу натренировать модель которая это предскажет твоя система всё кранты. Ты подумал что достаточно спрятать модель и датасет но я говорю это не панацея. На твоём месте я бы добавил защитный слой. Например можно прикрутить функцию квантового рассеяния к выходным эмбеддингам. Она разбрасывает выходы в пространство большей размерности так что даже если кто попробует сделать обратку он просто застрянет. Это ещё одна идея до которой в криптографии пока никто не додумался как мне кажется.

Ещё момент где можно было бы улучшить ты слишком зациклился на слоях 4 и 16. Я бы на твоём месте сделал динамический выбор слоёв. Например написал бы алгоритм который сам решает какие слои использовать для эмбеддингов в зависимости от входной картинки. Если картинка сложная с кучей деталей я бы брал нижние слои типа 3 или 4. А если картинка простая просто кот посередине то верхние типа 16 или 18. Так ключи станут адаптивными и безопасность взлетит. Это тоже момент над которым стоит подумать.

И последнее в части реализации ты немного упростил. Сказал что используешь SHA256 для генерации ключа и IV. Можно было бы задизайнить квантовую хешфункцию. Это типа хешфункция которая вместо классических алгоритмов вроде SHA256 использует квантовую модель для генерации хеша. Например можно заюзать квантовую сеть из квантовых гейтов которая берёт эмбеддинги на вход и выдаёт 256битный хеш. Этот хеш будет настолько непредсказуемым что даже атака на коллизии станет почти невозможной. Ещё одна идея которой в криптографии пока не пахнет.

Короче твой метод крутой но если бы я его дорабатывал я бы добавил всё что выше сказал. Тогда твоя система стала бы вообще неубиваемой и супернстоящей.

Теперь когда я всё это вывалил и добавил кучу идей к твоей статье хочу часть из них закодить. Особенно впрыскивание квантового шума в датасет и динамический выбор слоёв для эмбеддингов. Плюс немного подкручу основной код шифрования чтобы добавить простую квантовую хешфункцию. Я хочу чтобы каждый пиксель картинки получил квантовое изменение чтобы эмбеддинги стали ещё более непредсказуемыми. Напишу функцию которая использует простую квантовую волновую функцию и добавляет случайную фазу к каждому пикселю. Потом разберёмся с динамическим выбором слоёв и в конце добавлю простую квантовую хешфункцию....
Python:
import cv2
import numpy as np
import torch
from ultralytics import YOLO
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import hashlib
import cmath

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)

def apply_quantum_noise(image):
    height, width, _ = image.shape
    noisy_image = image.copy().astype(np.float32)
    for i in range(height):
        for j in range(width):
            for c in range(3):
                pixel_value = noisy_image[i, j, c]
                phase = np.random.uniform(0, 2 * np.pi)
                wave_function = cmath.rect(1, phase)
                noise = (wave_function.real + wave_function.imag) * 0.1
                noisy_image[i, j, c] = np.clip(pixel_value + noise * pixel_value, 0, 255)
    return noisy_image.astype(np.uint8)

def analyze_image_complexity(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)
    edge_density = np.sum(edges) / (edges.shape[0] * edges.shape[1])
    return edge_density

def select_dynamic_layers(edge_density):
    if edge_density > 0.1:
        return [3, 4, 5]
    else:
        return [15, 16, 18]

def quantum_hash(embeddings):
    flat_embeddings = torch.cat([e.flatten() for e in embeddings]).cpu().numpy()
    flat_embeddings = np.round(flat_embeddings, decimals=4)
    quantum_state = 0
    for val in flat_embeddings:
        phase = (val + 10) * np.pi / 20
        quantum_state += cmath.rect(1, phase).real
    quantum_state = (quantum_state % 1) * 1e9
    hash_input = str(quantum_state).encode()
    hash_digest = hashlib.sha256(hash_input).digest()
    return hash_digest[:16], hash_digest[16:32]

def encrypt_data(data, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct_bytes = cipher.encrypt(pad(data, AES.block_size))
    return ct_bytes

def process_image(image_path, model):
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError("Изображение не загружено!")
    image_with_noise = apply_quantum_noise(image)
    edge_density = analyze_image_complexity(image_with_noise)
    selected_layers = select_dynamic_layers(edge_density)
    print(f"Выбранные слои: {selected_layers}")
    original_shape = image.shape[:2]
    yolo_size = (max(32, (original_shape[0] + 31) // 32 * 32), max(32, (original_shape[1] + 31) // 32 * 32))
    image_rgb = cv2.cvtColor(image_with_noise, cv2.COLOR_BGR2RGB).astype(np.uint8)
    image_tensor = torch.from_numpy(image_rgb).permute(2, 0, 1).float() / 255.0
    image_tensor = image_tensor.clamp(0.0, 1.0).unsqueeze(0).to(model.device)
    activations = {}
    def get_activation(name):
        def hook(module, input, output):
            activations[name] = output
        return hook
    layers = model.model.model
    for layer_idx in selected_layers:
        layers[layer_idx].register_forward_hook(get_activation(f"layer_{layer_idx}"))
    with torch.no_grad():
        results = model(image, conf=0.4, imgsz=yolo_size)
    embeddings = []
    for layer_idx in selected_layers:
        layer_name = f"layer_{layer_idx}"
        activation = activations.get(layer_name, torch.zeros(1))
        embedding = activation.mean(dim=[2, 3]) if len(activation.shape) == 4 else torch.zeros(1)
        embeddings.append(embedding)
    return embeddings

device = torch.device("cpu")
model = YOLO("yolo11n-seg.pt").to(device)
image_path = "images/maryJane.png"
message = "xss.pro".encode()

embeddings = process_image(image_path, model)
key, iv = quantum_hash(embeddings)
ct_bytes = encrypt_data(message, key, iv)

print("Ключ:", key.hex())
print("IV:", iv.hex())
print("Зашифрованное сообщение:", ct_bytes.hex())
Окей давай разберём этот код по косточкам! Функция apply_quantum_noise добавляет к изображению квантовый шум. Я использовал квантовую волновую функцию с рандомной фазой которая слегка изменяет каждый пиксель. Это делает эмбеддинги которые генерит модель YOLO более непредсказуемыми и шансов на коллизии ключей становится меньше.


Потом функция analyze_image_complexity оценивает сложность изображения с помощью алгоритма Canny для детекции краёв. Если картинка сложная с кучей краёв она выбирает нижние слои модели 3 4 5. Если простая то верхние 15 16 18. Это делает ключ адаптивным и безопасность растёт.


А quantum_hash это вообще крутая штука! Вместо обычного SHA-256 я сделал что-то вроде квантового подхода для хеширования. Простая модель которая переводит эмбеддинги в квантовое пространство и потом хеширует. Это делает хеш ещё более непредсказуемым.


Если запустишь код увидишь что ключи и IV получаются другие по сравнению с твоим методом. За счёт квантового шума и динамического выбора слоёв безопасность на уровень выше. Что думаешь?
hackerGPT? mind sharing a prompt?
 
I don’t know if this works in ChatGPT or not, but write it like this:


I want an encryption system that works with the YOLO11n-seg model and generates a key and IV for AES-CBC. My idea is:


Write a function apply_quantum_noise that adds quantum noise to each pixel of an image. The noise should be generated using a quantum wave function (random phase shift with cmath.rect) to make the model’s embeddings more unpredictable.Write a function analyze_image_complexity that calculates image complexity using the Canny algorithm (based on edge density). If the image is complex, select lower layers (3, 4, 5); if simple, select higher layers (15, 16, 18).Write a function quantum_hash that takes embeddings, uses a simple quantum model (combining quantum phases) to generate a 256-bit hash, and produces a 16-byte key and 16-byte IV.The main function process_image should load the image, add quantum noise, select dynamic layers, extract embeddings from YOLO11n-seg, and use quantum_hash to generate the key and IV. Then, encrypt a message using AES-CBC.Write complete Python code using the libraries cv2, numpy, torch, ultralytics, Crypto.Cipher, hashlib, and cmath. Ensure the code runs on CPU and torch settings are deterministic.The output should include the key, IV, and encrypted message (all in hex format). Write clean code with comments, but don’t provide explanations outside the code...


Python:
# Import required libraries
import cv2
import numpy as np
import torch
from ultralytics import YOLO
from Crypto.Cipher import AES
import hashlib
import cmath
import random

# Set torch deterministic settings for reproducibility
torch.manual_seed(42)
torch.set_deterministic(True)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

def apply_quantum_noise(image):
    """Add quantum-inspired noise to image pixels using random phase shifts."""
    height, width, channels = image.shape
    noisy_image = image.copy().astype(np.float32)
    
    for i in range(height):
        for j in range(width):
            for c in range(channels):
                # Generate random phase for quantum-like noise
                phase = random.uniform(0, 2 * np.pi)
                amplitude = random.uniform(0.9, 1.1)
                wave = cmath.rect(amplitude, phase)
                # Apply noise to pixel value
                noisy_image[i, j, c] = min(max(image[i, j, c] * wave.real, 0), 255)
    
    return noisy_image.astype(np.uint8)

def analyze_image_complexity(image):
    """Analyze image complexity using Canny edge detection and edge density."""
    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # Apply Canny edge detection
    edges = cv2.Canny(gray, 100, 200)
    # Calculate edge density
    edge_density = np.sum(edges > 0) / (edges.shape[0] * edges.shape[1])
    
    # Select layers based on complexity
    if edge_density > 0.1:  # Threshold for complex images
        return [3, 4, 5]   # Lower layers for complex images
    return [15, 16, 18]    # Higher layers for simple images

def quantum_hash(embeddings):
    """Generate a 256-bit hash from embeddings using quantum-inspired phase combining."""
    # Flatten embeddings
    flat_embeddings = embeddings.flatten()
    hash_input = b""
    
    for val in flat_embeddings:
        # Simulate quantum phase combination
        phase = (val * 2 * np.pi) % (2 * np.pi)
        wave = cmath.rect(1.0, phase)
        # Convert to bytes
        byte_val = int(abs(wave.real) * 255).to_bytes(1, byteorder='big')
        hash_input += byte_val
    
    # Generate 256-bit hash
    hash_obj = hashlib.sha256(hash_input)
    hash_bytes = hash_obj.digest()
    
    # Extract 16-byte key and 16-byte IV
    key = hash_bytes[:16]
    iv = hash_bytes[16:32]
    
    return key, iv

def process_image(image_path, message):
    """Process image to generate AES-CBC key and IV, and encrypt message."""
    # Load YOLO11n-seg model
    model = YOLO('yolo11n-seg.pt')
    model.to('cpu')  # Ensure CPU usage
    
    # Load and preprocess image
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError("Failed to load image")
    
    # Apply quantum noise
    noisy_image = apply_quantum_noise(image)
    
    # Analyze image complexity to select layers
    selected_layers = analyze_image_complexity(noisy_image)
    
    # Get embeddings from YOLO11n-seg
    results = model(noisy_image, device='cpu', verbose=False)
    embeddings = None
    
    # Extract embeddings from selected layers
    for layer_idx in selected_layers:
        try:
            layer_output = results[0].features[layer_idx].cpu().numpy()
            if embeddings is None:
                embeddings = layer_output
            else:
                embeddings += layer_output
        except (IndexError, AttributeError):
            continue
    
    if embeddings is None:
        raise ValueError("Failed to extract embeddings")
    
    # Generate key and IV
    key, iv = quantum_hash(embeddings)
    
    # Prepare message for encryption
    message_bytes = message.encode('utf-8')
    # Pad message to multiple of 16 bytes
    padding_length = 16 - (len(message_bytes) % 16)
    padded_message = message_bytes + bytes([padding_length] * padding_length)
    
    # Encrypt message with AES-CBC
    cipher = AES.new(key, AES.MODE_CBC, iv)
    ciphertext = cipher.encrypt(padded_message)
    
    # Convert outputs to hex
    key_hex = key.hex()
    iv_hex = iv.hex()
    ciphertext_hex = ciphertext.hex()
    
    return key_hex, iv_hex, ciphertext_hex

# Example usage
if __name__ == "__main__":
    image_path = "sample_image.jpg"  # Replace with actual image path
    message = "This is a secret message"
    
    key_hex, iv_hex, ciphertext_hex = process_image(image_path, message)
    print(f"Key (hex): {key_hex}")
    print(f"IV (hex): {iv_hex}")
    print(f"Ciphertext (hex): {ciphertext_hex}")
 
Что думаешь?
Да ничего не думаю, мне просто хотелось хоть как нибудь попробовать решить проблему с уникальными ключами на примере уже обученной нейронки. Вроде получилось, но то что она в теории может дать одинаковый ключ для двух принципиально разных изображений, это факт, я только проверил устойчивость к замене 1-5 пикселей на одном и том же изображении со своей кошкой.

Чтобы не пустословить, нужно просто обучить 2 модели сегментации с одинаковыми классами. Но так чтобы в 1 датасет был разнообразным, а во 2 более скудный. И тут речь не про количество изображений, а про их «содержание». Например в 1 модели породы абсолютно всех котов, а во 2 только половина всех пород.

После чего нужно сравнивать пути обобщения, их можно просто визуализировать и наглядно увидеть как та или иная модель воспринимает изображения в зависимости от датасета.

Чтобы это имело еще больше веса, нужно пробовать разные модели yolo-seg, от самой маленькой, до самой большой. А пока что это просто идея, я бы даже сказал, что скорее шутка.

Но проблема в том, чтобы реально все это проверить нужно убить очень много времени на обучение и разметку. У меня после 1 часа разметки вытекают глаза, а тут нужно убить как будто неделю тупо на разметку сегментации и скорее всего в итоге я увижу просто похожие пути обобщения если модели одинаковые а датасеты разные.
 
Да ничего не думаю, мне просто хотелось хоть как нибудь попробовать решить проблему с уникальными ключами на примере уже обученной нейронки. Вроде получилось, но то что она в теории может дать одинаковый ключ для двух принципиально разных изображений, это факт, я только проверил устойчивость к замене 1-5 пикселей на одном и том же изображении со своей кошкой.

Чтобы не пустословить, нужно просто обучить 2 модели сегментации с одинаковыми классами. Но так чтобы в 1 датасет был разнообразным, а во 2 более скудный. И тут речь не про количество изображений, а про их «содержание». Например в 1 модели породы абсолютно всех котов, а во 2 только половина всех пород.

После чего нужно сравнивать пути обобщения, их можно просто визуализировать и наглядно увидеть как та или иная модель воспринимает изображения в зависимости от датасета.

Чтобы это имело еще больше веса, нужно пробовать разные модели yolo-seg, от самой маленькой, до самой большой. А пока что это просто идея, я бы даже сказал, что скорее шутка.

Но проблема в том, чтобы реально все это проверить нужно убить очень много времени на обучение и разметку. У меня после 1 часа разметки вытекают глаза, а тут нужно убить как будто неделю тупо на разметку сегментации и скорее всего в итоге я увижу просто похожие пути обобщения если модели одинаковые а датасеты разные.
Когда ты используешь нейросеть (ну, типа энкодера в модели контрастивного обучения), чтобы для каждого изображения генерить уникальный ключ, ты по сути учишь хеш-функцию в непрерывном пространстве. Эта функция должна иметь два свойства: разные изображения должны давать разные ключи, а мелкие изменения в картинке (типа шума или пары изменённых пикселей) не должны менять ключ. Проблема в том, что эти два свойства друг с другом конфликтуют. Если модель слишком чувствительна к деталям (чтобы различать), она становится чувствительной и к шуму. А если слишком устойчива, может замапить разные изображения в один ключ (коллизия). Обычно в таких сценариях ребята из дип-лёрнинга берут модель типа SimCLR или MoCo, закидывают в неё огромный датасет (вроде ImageNet) и надеются, что пространство признаков будет достаточно богатым, чтобы коллизий было поменьше. Потом ещё используют лосс-функцию, типа triplet loss или contrastive loss, чтобы расстояние между признаками похожих картинок было маленьким, а между разными — большим. Но это всё старьё, заезженное... Я говорю, вместо того чтобы сидеть и тестировать модели YOLO-seg на разных датасетах (как ты сам сказал, результаты будут плюс-минус одинаковые), давай вообще изменим правила игры. Мы замутим новую архитектуру, которая не просто будет генерить уникальные ключи, а гарантирует, что эти ключи распределены в каком-нибудь геометрическом пространстве (ну, типа нелинейного многообразия) так, чтобы вероятность коллизий стремилась к нулю. Вместо того чтобы закидывать признаки в евклидово пространство, давай замутим риманово многообразие, которое будет динамически менять форму в зависимости от датасета. Используем гомотопическую функцию, которая постепенно трансформирует пространство признаков из простого (типа сферы) в более сложное (типа тор или даже многообразие с высоким родом). Это сделает так, что признаки изображений будут равномерно распределены в пространстве, и вероятность коллизий геометрически уменьшится. Добавим в сеть новый слой, который вместо простого MLP будет определять дифференцируемую функцию, градиент которой берётся из уравнения в частных производных (PDE). Это PDE может быть, например, волновым уравнением или уравнением диффузии, которое оптимизирует форму многообразия.


Теперь, когда ключи сгенерированы, есть ещё одна проблема: хранение и масштабируемость. Если ты хочешь хранить 512-мерный ключ для каждого изображения, для большого датасета (типа миллиарда картинок) понадобится куча места. Я предлагаю использовать метод квантовой компрессии. Нет, я не про квантовые вычисления (хотя это тоже было бы круто). Я про метод, который мапит признаки в дискретное низкоразмерное пространство так, чтобы основные данные сохранялись. Это можно сделать с помощью автоэнкодера, который вместо реконструкции картинки будет восстанавливать только ключи. Но вот главная фишка: вместо линейного пространства используем гиперболическое. Почему? Потому что гиперболические пространства имеют большую информационную ёмкость и лучше работают с иерархическими данными (типа пород кошек).


Теперь, когда модель готова, надо её протестировать. Но не по-старинке, добавляя гауссов шум. Я говорю, давай хакнем модель! Используем атаку адверсариального типа, которая целенаправленно меняет пиксели, чтобы создать коллизию. Например, замутим градиентный спуск, который заставит два совсем разных изображения мапиться в один ключ. Если твоя модель отобьёт эту атаку, значит, она реально устойчива. Этот метод можно даже толкнуть в дарквебе как инструмент для тестирования безопасности моделей дип-лёрнинга!
 
9.jpg
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх