24 słowa: czym jest tożsamość kryptograficzna
Tożsamość kryptograficzna to nie hasło: żaden serwer jej nie przechowuje i nie można jej odzyskać. Dydaktyczne wyjaśnienie mechanizmu BIP39, dlaczego dokładnie dwadzieścia cztery słowa i jaki realny ciężar spoczywa na tym, kto je posiada.
Różnica między hasłem a tożsamością
Hasło w klasycznym modelu internetu to dowód tożsamości. Użytkownik posiada tożsamość — imię i nazwisko, adres e-mail, numer klienta — a aby udowodnić serwerowi, że jest tym, za kogo się podaje, przedstawia hasło, które serwer porównuje z zapisanym śladem. Jeśli ślady się zgadzają, serwer zezwala na sesję. Jeśli hasło zostanie zgubione, użytkownik nadal jest tym samym użytkownikiem; to co traci, to dowód, i istnieje procedura odzyskiwania — e-mail na zarejestrowany adres, pytanie pomocnicze — aby go przywrócić.
Tożsamość kryptograficzna działa inaczej. Jest to przede wszystkim w pełni kompletny w sobie sekret matematyczny, a nie poświadczenie, które ktoś porównuje z zapisanym śladem. Nie ma znaczenia, gdzie się znajduje — na papierze, na urządzeniu, a nawet na obcym serwerze —: tożsamość istnieje dzięki swojej matematyce, a nie dzięki temu, kto ją weryfikuje. Pojawia się tu właściwość podobna do tej, którą widzieliśmy w «Czym naprawdę jest SHA-256»: posiadania nie udowadnia się pokazując sekret, lecz używając go do podpisania. Wytworzony w ten sposób podpis może sprawdzić każdy za pomocą publicznej wartości, która matematycznie wywodzi się z samego sekretu, bez konieczności znajomości samego sekretu i bez pośrednictwa osoby trzeciej w sprawdzaniu. Kto ma sekret, ten jest tożsamością; kto go gubi, przestaje nią być. Wyrok jest kategoryczny: nie ma nikogo, kogo można by poprosić o zwrot tożsamości. Ktoś taki nie istnieje, ponieważ od początku jej nie miał.
Co reprezentuje dwadzieścia cztery słowa
Tożsamość kryptograficzna jest zazwyczaj reprezentowana przez matematyczny sekret o długości trzydziestu dwóch bajtów – dwustu pięćdziesięciu sześciu bitów. Liczba, którą trudno zapamiętać i jeszcze trudniej bezbłędnie przepisać. Branża kryptograficzna rozwiązała ten problem w 2013 roku za pomocą małego i eleganckiego standardu o nazwie BIP39: sposobu reprezentacji tych dwustu pięćdziesięciu sześciu bitów jako sekwencji dwudziestu czterech słów zaczerpniętych z oficjalnej listy dwóch tysięcy czterdziestu ośmiu słów. Kryjąca się za tym arytmetyka pasuje idealnie; ci, którzy chcą ją zobaczyć szczegółowo, znajdą ją na marginesie.
Liczenie nie jest dekoracyjne. Jeśli ktoś przepisze dwadzieścia trzy słowa poprawnie i pomyli się przy dwudziestym czwartym, suma kontrolna to wykryje: oprogramowanie powie mu „ta sekwencja jest nieprawidłowa”. Jeśli ktoś przepisze wszystkie dwadzieścia cztery słowa poprawnie, oprogramowanie jednoznacznie wyprowadzi tę samą tożsamość. Wybór listy słów jest również celowy: słowa ze słownika BIP39 są krótkie, wyraźnie różniące się od siebie, bez znaków diakrytycznych, wybrane tak, aby zminimalizować pomyłki fonetyczne i ortograficzne. Jest to słownictwo zaprojektowane tak, aby ludzie mogli je zapamiętać, zapisać i podyktować bez strat.
Od frazy do klucza
Te dwadzieścia cztery słowa nie są kluczem kryptograficznym, który podpisuje wiadomości. Są odtwarzalną reprezentacją oryginalnej entropii, która poprzez deterministyczny proces zwany PBKDF2 zostaje przekształcona w ziarno (seed) o długości sześćdziesięciu czterech bajtów. Z tego ziarna, również w sposób deterministyczny, wywodzone są konkretne klucze kryptograficzne, którymi posługuje się użytkownik: klucz prywatny do podpisywania oraz odpowiadający mu klucz publiczny, który jest publikowany w celu weryfikacji podpisów. Ten sam mechanizm w różnych systemach: kryptowaluty używają krzywej secp256k1; protokół Signal i wiele nowoczesnych systemów używa Ed25519 na krzywej Curve25519. Dla konkretnej krzywej, takiej jak Ed25519, standardy BIP32 i SLIP-0010 pobierają to sześćdziesięcioczterobajtowe ziarno i deterministycznie wywodzą trzydzieści dwa bajty, które stanowią efektywny klucz podpisujący — te same trzydzieści dwa bajty, od których zaczyna się przykład kodu w następnej sekcji.
To standardowy sposób, w jaki cała branża prezentuje mechanizm użytkownikowi —portfele kryptowalutowe, menedżerowie tożsamości zdecentralizowanej, Signal w części dotyczącej tożsamości trwałej, a wśród nich Solo2—: użytkownik w praktyce nigdy nie widzi ziarna ani wywodzonych kluczy. Widzi dwadzieścia cztery słowa podczas tworzenia swojej tożsamości i opcjonalnie zapisuje je na kartce papieru. Słowa te podróżują następnie między jego urządzeniami, gdy chce przenieść tożsamość: wpisuje je w nowej aplikacji, aplikacja wywodzi to samo ziarno, te same klucze, tę samą tożsamość. Jest to mechanizm przenośny, solidny pod względem kryptograficznym i, w granicach rozsądku, możliwy do zapamiętania.
Jak podpisywać kluczem (muśnięcie Zig)
W Zig, gdy posiada się już trzydziestodwubajtowe ziarno wywiedzione z dwudziestu czterech słów, podpisanie wiadomości za pomocą Ed25519 mieści się w kilku liniach:
const std = @import("std");
const Ed25519 = std.crypto.sign.Ed25519;
// 'semilla' son los 32 bytes derivados de las 24 palabras.
const par = Ed25519.KeyPair.create(semilla);
// Firmar un mensaje con la clave privada:
const mensaje = "Este mensaje lo escribí yo.";
const firma = try par.sign(mensaje, null);
// Cualquiera con la clave pública del par puede verificar:
try Ed25519.Signature.verify(firma, mensaje, par.public_key);
Operacja podpisywania generuje sześćdziesiąt cztery bajty —zwane podpisem— które mogły zostać utworzone wyłącznie na podstawie odpowiedniego klucza prywatnego. Weryfikacja jest publiczna: każdy, kto posiada klucz publiczny, może sprawdzić, czy podpis odpowiada wiadomości. Bez klucza prywatnego nikt nie może wygenerować poprawnego podpisu dla danej wiadomości; posiadając klucz publiczny, każdy może wykryć, czy podpis jest prawidłowy. Ta asymetria pozwala sygnatariuszowi udowodnić autorstwo bez dzielenia się sekretem.
Poprzedni przykład to minimalna wersja z podręcznika. W prawdziwym kodzie Solo2 łańcuch przechodzi przez dwa pliki: jeden w JavaScript, który żyje w przeglądarce użytkownika i rekonstruuje entropię z dwudziestu czterech słów, drugi w Zig wewnątrz biblioteki zcatcrypto, który przyjmuje tę entropię i wyprowadza konkretne klucze kryptograficzne. Zaczynając od strony przeglądarki:
// solo2/web-app/js/lib/bip39.js
async function mnemonicToEntropy(mnemonic, lang) {
const validation = await validateMnemonic(mnemonic, lang);
if (!validation.valid) {
return { entropy: null, valid: false, error: validation.error };
}
const wordlist = WORDLISTS[lang || 'en'];
const words = mnemonic.trim().split(/\s+/);
// Cada palabra aporta 11 bits (su índice en la lista de 2048).
let bits = '';
for (let i = 0; i < words.length; i++) {
bits += wordlist.indexOf(words[i]).toString(2).padStart(11, '0');
}
// 24 palabras = 264 bits. Los primeros 256 son la entropía.
const entropyBytes = new Uint8Array(32);
for (let j = 0; j < 32; j++) {
entropyBytes[j] = parseInt(bits.slice(j * 8, (j + 1) * 8), 2);
}
return { entropy: entropyBytes, valid: true };
}
Te trzydzieści dwa bajty entropii, wraz z kolejnymi trzydziestoma dwoma wyprowadzonymi w tym samym kroku, trafiają do modułu WebAssembly Zig, który generuje właściwe klucze Ed25519. Pełna funkcja, wraz z końcowym czyszczeniem pamięci, mieści się na jednym ekranie:
// zcatcrypto/wasm/bindings/identity.zig
const Ed25519 = std.crypto.sign.Ed25519;
const X25519 = std.crypto.dh.X25519;
export fn identity_generate() ?*IdentityHandle {
var seed: [64]u8 = undefined;
if (!common.getRandomBytes(&seed)) return null;
const handle = common.wasm_allocator.create(IdentityHandle) catch return null;
// Bytes 0..31: semilla determinista del par Ed25519 (firma).
const sign_kp = Ed25519.KeyPair.generateDeterministic(seed[0..32].*) catch {
common.wasm_allocator.destroy(handle);
return null;
};
handle.sign_secret = sign_kp.secret_key.toBytes();
handle.sign_public = sign_kp.public_key.toBytes();
// Bytes 32..63: secreto X25519 (para acordar claves de cifrado con el otro).
handle.exchange_secret = seed[32..64].*;
handle.exchange_public = X25519.recoverPublicKey(handle.exchange_secret) catch {
common.wasm_allocator.destroy(handle);
return null;
};
@memset(&seed, 0); // Borra la semilla de la memoria.
return handle;
}
Warto odnotować dwa szczegóły. Pierwszy: ten sam seed zawsze produkuje tę samą parę kluczy — to właśnie pozwala na odzyskanie tożsamości poprzez wpisanie dwudziestu czterech słów na nowym urządzeniu. Drugi: seed jest jawnie usuwany z pamięci w ostatniej linii. Po tym punkcie nawet sama funkcja nie mogłaby zrekonstruować kluczy; jedynym źródłem byłyby słowa użytkownika.
Warto tutaj zrobić krótką przerwę, ponieważ cały łańcuch może na pierwszy rzut oka zostać pomylony z inną prymitywą z tej trójki: hashem. Tak nie jest. Hash to unikalna funkcja, która kompresuje — wchodzi wiele bajtów, wychodzi krótki odcisk, tam kończy się droga. Tożsamość kryptograficzna to komplementarna para matematyczna: sekret zostaje i podpisuje; jego publiczny odpowiednik jest publikowany i weryfikuje. Tam, gdzie hash zapada informacje w jednym kierunku, tożsamość ustanawia asymetrię między dwiema połówkami. Hash poświadcza, co zostało powiedziane; tożsamość poświadcza, kto to powiedział.
Czym fraza nie jest
Warto wyjaśnić trzy częste nieporozumienia. Fraza nie jest hasłem w ścisłym tego słowa znaczeniu: nie jest porównywana z odciskiem przechowywanym na serwerze; wpisuje się ją do urządzenia użytkownika, aby matematycznie zrekonstruować tożsamość. Frazy nie da się odzyskać: jeśli zostanie zgubiona, nie ma kogo o nią poprosić; jeśli zostanie powielona, powielona zostanie również tożsamość. Fraza nie jest poświadczeniem oddzielnym od tożsamości: fraza jest tożsamością. Kto ją posiada, może działać jako ta tożsamość, bez dodatkowej zgody, bez procesu autoryzacji, bez możliwości odzyskania.
Właśnie ta trzecia właściwość zmienia ciężar tej kwestii. Utracone hasło to administracyjna niedogodność. Utracona tożsamość kryptograficzna to utrata tożsamości jako takiej. Kawałek papieru z frazą znaleziony przez osoby trzecie nie stwarza ryzyka kradzieży konta: oznacza przekazanie całej tożsamości. Obietnica systemu — że nikt nie może cofnąć ci tożsamości ani zablokować cię z własnego widzimisię — nierozerwalnie wiąże się z odpowiedzialnością bycia jedynym strażnikiem czegoś, czego nikt za ciebie nie odzyska.
Obietnica i ciężar
Model tożsamości kryptograficznej często otrzymuje miano samosuwerennej —self-sovereign w literaturze anglosaskiej—. Wybór słowa jest celowy i dość dokładnie opisuje ten stan. Użytkownik jest suwerenem swojej tożsamości w sensie niemal średniowiecznym: nie nadaje jej żaden król, żaden wydawca, żadna władza centralna; nikt z powyższych nie może jej również odebrać. Ale też, niczym średniowieczny monarcha, użytkownik ponosi pełne konsekwencje swoich błędów: nie ma regenta, który podjąłby decyzje za niego, jeśli zgubi pieczęć.
Wybór między tożsamością zarządzaną przez stronę trzecią a tożsamością samosuwerenną nie ma jednej uniwersalnej poprawnej odpowiedzi. W przypadku konta na nieistotnym forum tożsamość zarządzana jest prawdopodobnie proporcjonalna do ryzyka. W przypadku tożsamości zawodowej, która podpisuje dokumenty wiążące prawnie, tożsamości ekonomicznej, która strzeże własnych oszczędności, czy tożsamości do komunikacji zawodowej z klientami, którzy powierzyli poufne informacje, kwestia ta ulega zmianie. Tam pytanie przestaje brzmieć „czy to wygodne?”, a staje się pytaniem „kto, poza mną, ma moc działania jako ja i w jakich okolicznościach?”.
Gdzie ten mechanizm pojawia se w rzeczywistych systemach
Standard BIP39 narodził się w świecie Bitcoin w 2013 roku i szybko rozprzestrzenił się na cały ekosystem kryptowalut: każdy poważny portfel akceptuje dziś dwunasto- lub dwudziestoczterowyrazową frazę BIP39 jako zabezpieczenie tożsamości ekonomicznej jej posiadacza. Poza kryptowalutami ta sama podstawowa koncepcja — para kryptograficzna potwierdzająca autorstwo bez pośrednika — pojawia się w innych systemach o innej składni. Klucze SSH, których administrator systemów używa do uzyskiwania dostępu do swoich serwerów, są klasycznym przypadkiem: klucz prywatny, który administrator przechowuje na swojej maszynie, oraz klucz publiczny, który jest kopiowany na każdy serwer; nie interweniuje żaden podmiot porównywalny z usługą scentralizowaną. Protokół Signal wykorzystuje Ed25519 z trwałym materiałem klucza na urządzeniu; europejskie rozporządzenia eIDAS, w części dotyczącej podpisu kwalifikowanego, opierają się na tej samej zasadzie kryptograficznej, z tą różnicą, że klucz jest przechowywany przez kwalifikowanego dostawcę usług zaufania zamiast przez użytkownika.
Solo2, platforma wydawnicza niniejszej publikacji, używa dwudziestoczterowyrazowej frazy BIP39 jako tożsamości każdego użytkownika. Użytkownik, podczas zakładania konta, widzi słowa raz. Nie są one przechowywane na żadnym serwerze Solo2 ani nikogo innego: jeśli użytkownik je zapisze i będzie je chronił, zachowa swoją tożsamość na zawsze. Jeśli je zgubi, traci je bezpowrotnie. Jest to logiczna konsekwencja architektury bez operatora pośredniczącego: gdyby Solo2 mogło zwrócić tożsamość użytkownikowi, który ją zgubił, mogłoby ją również przekazać każdemu, kto wywarłby presję na Solo2, aby ją otrzymać.
Pieczęć bez kancelarii
Średniowieczny monarcha rządził poprzez swoją pieczęć, ale nie rządził tylko nią: za nią stała kancelaria, skrybowie prowadzący rejestry, dwór zdolny rozpoznać falsyfikat i zrekonstruować, za pomocą świadków i pamięci instytucjonalnej, to, co zagubiona lub skradziona pieczęć pozostawiała w wątpliwość. Kto dziś strzeże dwudziestu czterech słów, ma pieczęć i nic poza tym: żadnej kancelarii, żadnego skryby, żadnej pamięci instytucjonalnej, która mogłaby rozstrzygnąć spór lub przywrócić to, co utracone. Złożony papier w szufladzie, wygrawerowana metalowa płytka, zapieczętowana koperta w depozycie notarialnym to nic innego jak próba, rzemieślnicza i siłą rzeczy niekompletna, stworzenia własnej kancelarii za pomocą narzędzi osoby prywatnej.
Król bez kancelarii rządzi dokładnie tak samo jak król, który ją posiada —aż do dnia, w którym gubi pieczęć, i tego dnia przestaje rządzić, a nie ma już nikogo, do kogo można by się odwołać.
Ten artykuł zamyka koncepcyjne trio, które otworzyło cykl — hash, szyfrowanie, tożsamość —. Te trzy idee budują się na sobie: hash daje niezmienny odcisk, szyfrowanie daje poufność bez zaufanej strony trzeciej, tożsamość daje autorstwo bez strony trzeciej nadającej uprawnienia. Wszystkie trzy łączy cecha, która również nie jest ideologiczna: przenoszą one z podmiotu zarządzającego usługą na użytkownika możliwości techniczne, które tradycyjnie należały do operatora. Przenoszą wraz z nimi również odpowiedzialność. Uczciwa rozmowa o którejkolwiek z tych trzech idei wymaga rozmowy także o dwóch pozostałych.
Źródła i dodatkowa lektura
- Palatinus, M.; Rusnak, P.; Voisine, A.; Bowe, S. — BIP-0039: Mnemonic code for generating deterministic keys, propozycja ulepszenia Bitcoin z 2013 roku. De facto standard dla fraz odzyskiwania w branży kryptowalut.
- RFC 8032 — Edwards-Curve Digital Signature Algorithm (EdDSA), w tym Ed25519. IETF, styczeń 2017. Normatywna specyfikacja schematu podpisu stosowanego w dużej części współczesnego przemysłu.
- RFC 2898 — PKCS #5: Password-Based Cryptography Specification, wersja 2.0. IETF, wrzesień 2000. Definiuje algorytm PBKDF2 stosowany w wyprowadzaniu BIP39 z frazy do ziarna (seed).
- Rozporządzenie (UE) 910/2014 (eIDAS) oraz jego ewolucja poprzez Rozporządzenie (UE) 2024/1183 (eIDAS 2) — europejskie ramy tożsamości elektronicznej i podpisu kwalifikowanego. Reżim inny niż samostanowiący, ale koncepcyjny oparty na tych samych prymitywach kryptograficznych.
- Allen, C. — The Path to Self-Sovereign Identity (2016). Kanoniczny tekst na temat zasad i zobowiązań modelu samostanowiącego, wcześniejszy, ale istotny dla zrozumienia rodziny współczesnych rozwiązań.