Технічний огляд верифікації посвідчень з нульовим розголошенням — від проблеми приватності до конвеєра доведення, предикатів та відповідності стандартам.
Кожен раз, коли ви підтверджуєте вік у барі, купуєте товари з обмеженням за віком або підтверджуєте категорію прав — верифікатор отримує ваше повне посвідчення. Ваше ім'я, дату народження, адресу, номер документа. Все. Заради однієї відповіді так/ні.
Охоронець, вебсайт, роботодавець — всі бачать все. Ваше посвідчення повністю відкрите при кожній перевірці. Ці дані можуть зберігатися, витікати або продаватися.
Верифікатор дізнається рівно одне: предикат виконується. Доказ криптографічно прив'язаний до підпису видавця — його неможливо підробити. Але справжні дані ніколи не залишають пристрій власника.
Криптографічний примітив, який робить можливою верифікацію зі збереженням приватності.
Уявіть, що у вас є дальтонік-друг і дві кулі — червона і зелена. Ви хочете довести, що вони різного кольору, не кажучи другу, яка яка. Ви даєте обидві кулі, друг ховає їх за спиною і або міняє місцями, або ні. Потім показує вам кулі і питає: «Я їх поміняв?» Ви завжди можете відповісти — бо бачите кольори. Після достатньої кількості раундів друг переконаний, що кулі різні, але все одно не знає, яка червона, а яка зелена.
// Застосовано до посвідчень:
Замініть «кулі різного кольору» на «моя дата народження була більше 18 років тому». Довідник (громадянин) переконує верифікатора (сервіс), що твердження істинне, не розкриваючи фактичну дату народження.
У zk-eidas «твердження» — це предикат над підписаним полем посвідчення, а «доказ» — це Groth16 доказ, згенерований з Circom схеми. Двоетапна архітектура забезпечує три речі: логіку предиката, включення поля у посвідчення та дійсність ECDSA підпису видавця (з'єднані через Poseidon комітмент).
Якщо твердження істинне і обидві сторони дотримуються протоколу, верифікатор завжди прийме.
Якщо твердження хибне, жоден нечесний довідник не зможе переконати верифікатора в його істинності (за виключенням нехтовної ймовірності).
Верифікатор не дізнається нічого, крім істинності твердження. Жодної побічної інформації, жодного витоку метаданих.
Від сирого посвідчення до непрозорих байтів доказу — ось що саме відбувається, коли громадянин доводить предикат.
SD-JWT VC (або mdoc) розбирається на окремі поля. Кожне поле — це пара ключ-значення з сіллю та хешем розкриття. Парсер також витягує публічний ключ ECDSA P-256 видавця з заголовка JWT.
Формат SD-JWT: header.payload~disclosure1~disclosure2~... Кожне розкриття — base64url(json([salt, key, value])). SHA-256 хеш кожного розкриття вбудований у підписаний payload.
Значення поля конвертується в елементи поля — нативний числовий тип ZK схеми. Дати стають цілими числами epoch-day, рядки — SHA-256 хешами, булеві — 0/1. Свідок включає значення поля, сіль розкриття, компоненти підпису (r, s) та координати публічного ключа.
Всі входи свідка приватні — схема ніколи їх не розкриває. Єдині публічні входи — параметри предиката (напр., поріг=18) та результат верифікації.
Схема робить три речі за одне виконання: (1) перевіряє ECDSA P-256 підпис видавця над JWT payload, (2) перевіряє, що SHA-256 хеш розкриття збігається з тим, що в підписаному payload, та (3) обчислює предикат (напр., вік >= 18). Якщо будь-яка перевірка не пройде, генерація доказу провалюється.
Groth16 доводчик (ark-circom на сервері, snarkjs у браузері) бере задоволену схему і створює компактний доказ. Groth16 використовує довірену ініціалізацію для кожної схеми (.zkey файли). Доказ — непрозора послідовність байтів, яка кодує трасу виконання без розкриття значень свідка.
Доведення займає ~1-3 секунди на серверному обладнанні. Розмір доказу постійний незалежно від складності посвідчення.
Верифікатор виводить ключ верифікації з байткоду схеми — ніколи не довіряючи ключам від довідника. Верифікація перевіряє доказ проти публічних входів (параметрів предиката). Вона виконується за ~5мс на сервері або <100мс у браузері через WASM.
Верифікація в браузері використовує snarkjs — той самий Groth16 верифікатор, що працює на JavaScript. Без серверних запитів.
// Holder: prove age >= 18 with ECDSA verified in-circuit
let proof = ZkCredential::from_sdjwt(&credential, "circuits/predicates")?
.predicate("birth_date", Predicate::gte(18))
.prove()?;
// Verifier: learns nothing except that the predicate holds
let valid = ZkVerifier::new("circuits/predicates").verify(&proof)?;
// valid == true — the holder is 18+, signature is authentic
// The verifier never sees the birth date, name, or any other claimПредикат — це булева функція над полем посвідчення. Кожен тип предиката має окрему Circom схему з вбудованою перевіркою підпису ECDSA.
| Тип | Опис | Приклад |
|---|---|---|
| gte | Більше або дорівнює — числове порівняння | age >= 18 |
| lte | Менше або дорівнює — числове порівняння | age <= 65 |
| eq | Рівність — порівняння на основі хешу для рядків | status == "active" |
| neq | Не дорівнює — доводить, що значення відрізняється від цільового | status != "revoked" |
| range | Перевірка діапазону — значення в межах | 18 <= age <= 25 |
| set_member | Членство у множині — значення є одним з до 16 допустимих | nationality ∈ {"DE","FR","NL"} |
| nullifier | Скопована протидія повторному використанню — детерміновано, без зв'язку між скопами | hash(secret, scope) |
Кожна схема предиката включає повну перевірку підпису ECDSA P-256. Непідписаних варіантів схем не існує — всі докази криптографічно прив'язані до автентичних посвідчень.
За межами окремих предикатів — складена логіка, протидія повторному використанню, відкликання та міждокументна прив'язка.
Поєднуйте кілька предикатів за допомогою булевої логіки. AND вимагає верифікації всіх під-доказів. OR — принаймні одного. Кожен під-доказ незалежно перевіряє свій ECDSA підпис.
.predicate("birth_date", Predicate::gte(18))
.predicate("nationality", Predicate::set_member(vec!["DE","FR","NL"]))
.prove_all()? // AND: both must holdНуліфікатор — це детермінований, специфічний для скопу токен, отриманий з секрету власника та рядка скопу верифікатора. Одне посвідчення створює різні нуліфікатори для різних сервісів, що унеможливлює міжсервісне зв'язування.
// Same credential, different scope = different nullifier // bar.example.com can't link to shop.example.com nullifier = hash(holder_secret, "bar.example.com")
Використовує розріджене дерево Меркла (SMT) для доведення того, що посвідчення НЕ було відкликано. Видавець публікує корінь дерева; власник генерує доказ невключення всередині схеми.
// Sparse Merkle Tree: prove credential NOT in revocation set // Issuer publishes tree root, holder proves non-membership SMT::prove_non_membership(credential_id, tree_root)
Доводить, що два різних посвідчення (напр., національний ID та водійські права) належать одній особі, не розкриваючи спільний ідентифікатор. Використовує SHA-256 commitments, порівнювані всередині схеми.
// Prove PID and driver's license belong to same person // Without revealing the shared identifier hash(pid.personal_id) == hash(license.personal_id)
Розроблено нативно для eIDAS 2.0 — не адаптовано. Кожен підтримуваний стандарт перевірений проти специфікації.
Рамка цифрової ідентичності ЄС, що зобов'язує до цифрових гаманців для всіх громадян ЄС до 2026. zk-eidas підтримує профіль PID, визначений у Architecture Reference Framework.
Selective Disclosure JSON Web Tokens (RFC 9901) — основний формат посвідчень для EUDI Wallets. Кожне поле може розкриватися окремо через SHA-256 хеші з сіллю.
Формат мобільних документів ISO 18013-5 з підписами COSE_Sign1. Використовується для мобільних водійських посвідчень (mDL). zk-eidas перевіряє підписи COSE_Sign1 всередині тих самих ZK схем.
Алгоритм підпису, визначений як для SD-JWT VC (ES256), так і для mdoc (COSE_Sign1). Крива P-256, перевіряється всередині Circom схеми для кожного доказу.
OpenID for Verifiable Presentations — транспортний протокол для запиту та отримання ZK доказів від EUDI Wallets. zk-eidas генерує PresentationDefinition та InputDescriptor об'єкти.
EUDI Wallet Architecture Reference Framework визначає схему PID посвідчення (birth_date, age_over_18, issuing_country тощо). Тести відповідності zk-eidas валідують саме цю схему.
zk-eidas реалізує принцип мінімізації даних GDPR на криптографічному рівні. Докази з нульовим розголошенням розкривають лише булеві результати предикатів — ніколи сирі персональні дані. Скоповані нуліфікатори запобігають міжконтекстному відстеженню. Бібліотека не зберігає, не обробляє та не передає жодних персональних даних.
Докази розкривають лише булеві результати. Жодне сире значення не залишає власника.
Кожен доказ прив'язаний до конкретного предиката. Верифікатор не може використати його для інших перевірок.
Бібліотека не зберігає персональних даних. Докази тимчасові. Нічого для витоку.