Геометрический forensic-watermark в моделях конструктора — спайк (AD-1136)
Статус: спайк закрыт, решение принято — НЕ ВНЕДРЯЕМ
(основатель, 2026-07-31;docs/adr/ad-1136-geometric-watermark-not-implemented.md).
Причина — § 4.3: перенос детали на столе слайсера, штатное действие перед
печатью, сбивает чтение метки до случайного угадывания, поэтому схема даёт
ложное чувство защищённости. Документ остаётся как результат замеров, а не как
открытый вопрос: подключать описанное здесь к экспорту нельзя, и повторный
спайк на ту же тему не нужен — условия пересмотра названы в § 5 ADR.
Родитель — эпик AD-1133 (защита моделей: provenance + юридическая,
не DRM); соседи по эпику —LICENSE.txtв архиве (AD-1134) и метка
происхождения в заголовке STEP / имени тела STL (AD-1265) — они остаются
в силе.
1. Вопрос, на который отвечает спайк
Можно ли спрятать идентификатор пользователя в самой геометрии детали так,
чтобы одновременно выполнялось всё:
- не ломает печать — деталь печатается как прежде;
- не меняет аэродинамику — обводы остаются теми же;
- не виден глазом на напечатанной детали;
- извлекается из файла — по файлу можно назвать владельца.
Тикет прямо допускает отрицательный ответ: если незаметность и извлекаемость на
наших размерах несовместимы — это честный результат. Они совместимы.
Несовместимо другое, и об этом § 5.
2. Что померили и на чём
Стенд — обычный экспорт v2-редактора (борт размахом 1 м, фюзеляж 0.8 м):
ASCII-STL 32.5 МБ, 385 812 вершин; STEP 623 КБ, 6 995 контрольных точек.
Разрешающая способность самих форматов (замер, не оценка):
| Формат | Как пишутся координаты | Наименьший различимый шаг |
|---|---|---|
| ASCII-STL (OCCT) | %.6e — 7 значащих цифр (4.259878e+02) |
~10⁻⁴ мм на габарите 0.4 м, ~10⁻³ мм на 1.5 м |
| STEP (OCCT) | полная двойная точность (425.98775446637) |
~10⁻¹¹ мм |
То есть формат не является ограничением: в него влезает шаг модуляции на
порядки мельче того, что различает печать.
3. Схема PoC
Слепая (blind) QIM-модуляция по значению координаты. Для каждой вершины:
индекс бита = hash(round(X/Δ), round(Y/Δ)) mod N
сам бит = чётность round(Z/Δ) → Z «дотягивается» до нужной ячейки
Три свойства, ради которых схема выбрана именно такой:
- ключ по X/Y, а меняется Z — одинаковые вершины соседних фасетов сдвигаются
одинаково, и меш остаётся водонепроницаемым без разбора смежности (в ASCII-STL
вершина дублируется в каждом фасете). Замер: вырожденных треугольников после
встраивания — 0 (до встраивания тоже 0); - извлечение слепое — исходный файл не нужен;
- адресация по позиции, а не по порядку вершин — отрезанный кусок детали
несёт метку целиком. Для нашего экспорта по печатным деталям (AD-1128, борт
режется на сегменты) это не бонус, а необходимое условие.
4. Результаты замеров
Прогон: node scripts/ad1136_watermark_poc.mjs <файл.stl> [--step=мм] [--bits=N]
4.1 Ёмкость и стоимость (Δ = 0.01 мм, 128 бит)
| Величина | Значение |
|---|---|
| вершин обработано | 385 812 |
| покрыто бит | 128 / 128 |
| голосов на самый «бедный» бит | 2 744 |
| прочитано из нетронутого файла | 100 % |
| сдвиг поверхности | max 10 мкм, средний 5 мкм |
| вырожденных треугольников | 0 |
| прирост размера файла | −1.2 % (перезапись чисел, не рост) |
| время | встраивание 0.54 с, извлечение 0.16–0.29 с |
Ёмкость не является ограничением: 512 бит читаются так же на 100 %, самый
бедный бит собирает 545 голосов.
4.2 Незаметность — сравнение с физикой печати
Сдвиг 10 мкм (Δ = 0.01 мм) против того, что умеет FDM:
| Величина | Порядок | Отношение к метке |
|---|---|---|
| высота слоя | 100–200 мкм | в 10–20 раз крупнее |
| ширина экструзии (сопло 0.4) | 400–450 мкм | в 40 раз крупнее |
| шероховатость поверхности Ra | 10–20 мкм | того же порядка или крупнее |
| шаг позиционирования XY | ~50 мкм | в 5 раз крупнее |
| цена деления штангенциркуля | 10–20 мкм | того же порядка |
Ключевая строка — шероховатость: метка мельче собственного микрорельефа
печати, то есть на напечатанной детали её физически нечего увидеть и нечем
измерить. Аэродинамика: 10 мкм на хорде 200 мм — это 5·10⁻⁵ относительной
деформации обвода, на два порядка ниже шероховатости той же поверхности; вклад в
поляру неотличим от нуля любым доступным нам методом (ни XFOIL, ни VLM таких
величин не различают в принципе).
Пункты 1–4 из § 1 выполняются одновременно. Ответ на вопрос спайка —
положительный.
4.3 Устойчивость — где схема ломается (главный результат)
Доля верно прочитанных бит после операции над файлом. Идентификатор читается
только при 100 %; ~50 % — это случайное угадывание, то есть метки нет.
| Операция над файлом | Δ = 0.002 мм | Δ = 0.01 мм | Δ = 0.05 мм |
|---|---|---|---|
| копия файла | 100 % | 100 % | 100 % |
| сдвиг по Z на целое число ячеек | 100 % | 100 % | 100 % |
| перенос детали на (0.137, 0.041, 0.213) мм | 44 % | 48 % | 53 % |
| шум ±2 мкм (пересчёт координат) | 48 % | 100 % | 100 % |
| шум ±20 мкм (грубый пересчёт/ремеш) | 53 % | 50 % | 100 % |
| масштаб ×1.001 (подгонка размера) | 99 % | 95 % | 74 % |
Читается это так:
- Δ задаёт компромисс, а не качество. Мельче — незаметнее, но ломается от
любого пересчёта координат; крупнее — переживает шум, но подбирается к
разрешению печати и всё равно не переживает масштабирование. - Ни одно значение Δ не спасает от переноса и масштабирования. Оба — один
клик в любом mesh-редакторе или слайсере (и, что важнее, штатная операция:
человек двигает деталь на столе принтера). - Настоящий ремеш/децимация (не эмулированные здесь шумом, а перестроение сетки)
убивает метку тем более: она живёт в координатах ИСХОДНЫХ вершин.
⚠ Методическая заметка: первый прогон PoC «доказал» устойчивость к переносу на
7.3 мм — потому что 7.3 мм это ровно 730 ячеек сетки, и чётность сохранилась.
Ложно-успешный замер, атака переписана на некратное смещение. Строка «сдвиг на
целое число ячеек» оставлена в таблице как контроль: она показывает, что схема
инвариантна к сдвигу ПО СЕТКЕ и ни к какому другому.
5. Выводы
- Незаметность и извлекаемость на наших размерах совместимы — с запасом в
один-два порядка по каждому из четырёх требований § 1. Это НЕ то место, где
идея упирается. - Упирается она в устойчивость. Метка переживает то, что делает
недобросовестный пользователь ЛЕНИВО (скачал → выложил как своё, сконвертировал
формат, разрезал на печатные детали), и не переживает того, что делает
пользователь ОБЫЧНО (подвинул деталь на столе, подогнал масштаб) — не говоря
о том, что сделал бы намеренно (ремеш в 3 клика). То есть это forensic-метка
против ленивой перепубликации, а не защита. - Секретность схемы — не защита. Универсальная атака у этого класса схем
одна и тривиальна: пересчитать координаты (шум/переквантование/ремеш).
Стойкость не в том, что метку нельзя снять, а в том, что снимать её никто не
станет, пока не знает, что она есть. - Это НЕ замена AD-1265, а другой слой. Заголовок STEP / имя тела STL —
ЯВНАЯ метка: её видно, её легко удалить, но она честная и работает как
provenance. Геометрическая метка — наоборот: снять её случайно нельзя, а
найти без исходного ключа тоже нельзя. Слои дополняют друг друга и не
конкурируют.
6. Чего внедрение будет стоить (если решат внедрять)
Это оценка, а не план: тикет — спайк, продовой части в нём нет.
- Только STL. PoC работает по вершинам сетки. Для STEP та же схема ложится на
контрольные точки (точности формата хватает с запасом), но любой пересчёт
B-rep в CAD её снимает; отдельный замер по STEP не делался. - Память. Метку приходится ставить ПОСТ-ФАКТУМ, текстом поверх готового
блоба: сетку пишет OCCT, до неё не дотянуться. Для 32 МБ STL это ~65 МБ
JS-строки в воркере, который и без того упирается в порог перезапуска по
WASM-heap (AD-1140, 512 МБ). Это прямо противоречит конструкции AD-1265, где
метка происхождения читает только КРАЯ блоба именно ради независимости цены от
размера модели. Потоковая (по-чанковая) реализация — отдельная работа. - Секрет. В проде хэш обязан солиться секретом из
web/config.py, иначе
сопоставить метку с пользователем сможет любой, кто видел код. Соль — новый
секрет в контуре эксплуатации. - Юридически — решение основателя, не исполнителя. Скрытая метка в файле
пользователя без раскрытия в «Условиях использования» (раздел 19, AD-1132) —
это скрытое отслеживание. Раскрытие метки её forensic-ценность НЕ снижает
(стойкость не зависит от секретности факта, § 5.3), но снижает
репутационный риск до нуля. Внедрять без явного решения по этому пункту
нельзя.
7. Решение основателя (2026-07-31): не внедряем
Спайк спрашивал три вещи — внедряем ли вообще, раскрывать ли метку в «Условиях
использования» и как резать работу на тикеты. Ответ на первый вопрос
отрицательный, поэтому два остальных сняты: ни правки раздела 19, ни
потоковой реализации, ни соли в конфиге не будет.
Обоснование — § 4.3: перенос детали на некратное смещение оставляет 48 %
верно прочитанных бит, то есть метки не остаётся. Двигают деталь на столе
слайсера все и всегда, это первое действие перед печатью, а не атака. Схема,
которая исчезает у того, кто файл просто напечатал, и работает только против
того, кто переслал его байт-в-байт, даёт ложное чувство защищённости при
настоящей цене внедрения (§ 6).
Полная формулировка решения, что остаётся в силе из эпика AD-1133 и при каких
условиях к теме уместно вернуться —
docs/adr/ad-1136-geometric-watermark-not-implemented.md. Отдельного тикета
реализации не заводить.