Nezávislé čtení publikované specifikace z openiam.org (hlavička m=670625, tedy 11. 4. 2026). Auditován je text specifikace, ne implementace. Mechanicky ověřeno: wordlist, epocha, kolizní mez pečeti.
Doménová separace prefixů („IAM1:id\0“, „IAM1:record\0“, „IAM1:tree\0“) je udělaná správně a NUL je v JSONu neproniknutelný, takže hranice prefixu není dvojznačná. Tree id jako hash redukovaných deskriptorů elegantně obchází kruhovou závislost a zároveň váže strom na obsah bootstrapu. Rozdělení na pozorování, přijetí a ukotvení je nejlepší část dokumentu, protože odděluje transport od pravdy. A jedna věc, kterou spec podceňuje ve svůj neprospěch: únik genesis klíče je ve skutečnosti inertní, protože genesis není členem grafu a jeho ACCEPT by neprošel pravidlem platnosti aktéra. Erasure je pás k šlím, ne nosný prvek.
Ověřeno počítačem: wordlist má přesně 256 unikátních položek, samé ASCII malé písmo, délka 3 až 7, seřazený, a všechny čtyřznakové prefixy jsou unikátní. Epocha 28928160 skutečně odpovídá 2025-01-01 00:00 UTC. Kolizní mez 77000 sedí (přesně 77162).
Celá hodnota protokolu stojí na větě, že dvě implementace nad stejným vstupem spočítají stejný stav. Tři věci tomu dnes brání.
1. Není zafixovaný profil ověřování Ed25519. Tohle je nejvážnější nález celého auditu. Ed25519 je proslulý tím, že se implementace neshodnou na hraničních podpisech: kofaktorové versus nekofaktorové ověření, body malého řádu, nekanonické kódování bodu (y >= p), nekanonické S (bez kontroly S < L). Knihovna A podpis přijme, knihovna B ho odmítne, a tím se rozejde stav grafu. Ne teoreticky, tohle je v praxi nejčastější zdroj neshody mezi Ed25519 knihovnami. Spec musí normativně určit jeden profil: buď plně specifikovaná pravidla ZIP-215, nebo přesně popsaná striktní varianta (odmítnout klíče malého řádu, odmítnout nekanonická kódování, vyžadovat S < L, určit rovnici ověření). Věta „použijte RFC 8032“ nestačí, protože RFC 8032 samo nechává tuto volnost.
2. Není zafixovaná verze Argon2. Existují Argon2 verze 0x10 a 0x13 a dávají různý výstup ze stejných vstupů. Starší knihovny umí obojí. Bez zafixování version = 0x13 může vzniknout dvojí prostor identit, což je přesně to, čemu se sekce o parametrech snaží zabránit. Doplnit také, že secret a associated data jsou prázdné a že hesplem jsou syrové bajty zaklínadla.
3. Neexistují normativní testovací vektory. Pro specifikaci, jejíž hlavní slib je bajtová shoda, je to největší chybějící část. Potřeba je verzovaná příloha s vektory pro: odvození klíče (jméno, zaklínadlo, master_seed, root_seed, veřejný klíč), pečeť, kanonický tvar záznamu a jeho id, podpis, a kompletní bootstrap včetně tree id. Bez toho nelze mluvit o shodě, jen o dobré vůli.
4. Pečeť má 32 bitů a spec počítá jen s náhodnou kolizí. Sekce mluví o narozeninovém paradoxu při zhruba 77000 identitách, což je správně, ale je to špatná otázka. Útočník nechce náhodnou kolizi, chce cílenou: pečeť shodnou s konkrétní obětí. To stojí řádově 2^32 generování klíčů, což jsou hodiny na jedné GPU, a nic nenutí, aby výsledný klíč vůbec pocházel z odvození ze jména a zaklínadla. Přitom pečeť je jediná věc, kterou porovnává člověk očima. Dobrá zpráva: rozšíření na šest slov je zpětně kompatibilní, protože seal[i] bere i-tý bajt SHA-512 a první čtyři slova zůstanou stejná. Cena za 48 bitů jsou dvě slova navíc. Rozhodnout se dá teď, později už budou pečeti vytištěné.
5. m se dá libovolně datovat dozadu a rozhoduje o výsledku. Admission má horní mez (now + 5), dolní žádnou. Uvnitř řetězce hlídá monotónnost prev, mezi aktéry ne. Přitom pořadí (m, id) určuje, zda byl aktér v okamžiku zpracování platným členem, tedy určuje výsledek souboje ACCEPT proti REVOKE. Nový aktér může svůj první záznam datovat na m=0 a předběhnout kohokoli. Sekce Time přitom tvrdí, že m se nepoužívá k určování vítězů. To je vnitřní rozpor: prohru sice neurčuje ve forku, ale určuje ji v semantické platnosti. Buď doplnit dolní mez při admission (nejméně m bootstrapu daného stromu), nebo tu větu přeformulovat, aby neuspávala implementátory.
6. Rozštěpení řetězce jde použít jako útok, nejen se stát nehodou. Fork zlomí řetězec (tree, context, actor) natrvalo a od té chvíle neprojde žádný další záznam toho aktéra, včetně LEAVE. Kdo získá cizí kořenový klíč, může oběti řetězec záměrně rozštěpit a tím jí odebrat schopnost odejít nebo cokoli odvolat. U běžného člena to spraví rodič svým REVOKE. U zakládajícího člena, kterého nelze odvolat zvenčí a jehož jediným východiskem je vlastní LEAVE, tím vzniká trvale nesmazatelný člen. Spec připouští, že kompromitovaný zakládající člen může odmítnout odejít; nepřipouští, že mu útočník může možnost odejít vzít i tehdy, když spolupracuje.
A druhá, pravděpodobnější polovina téhož: nejčastější reálná porucha bude sebe-rozštěpení. Protokol povzbuzuje uživatele, aby si klíč odvodil z hlavy na jakémkoli zařízení, a zároveň nedefinuje žádný způsob, jak zjistit hlavu vlastního řetězce, protože distribuce je mimo rozsah. Dva stroje, jedna identita, dva podpisy nad stejným prev, konec. Doporučení: buď povinnost runtime dodat hlavu řetězce před podpisem, nebo změkčit fork tak, aby řetězec nelámaly záznamy shodného obsahu.
7. Karanténa bez horní meze. Záznamy s neznámým prev nebo budoucím m se MAY držet v karanténě. Útočník tím dostane zdarma paměťový kanál: proud syntakticky platných záznamů s odkazem na neexistující předchůdce. Doplnit povinnost karanténu shora omezit.
8. Jeden klíč ve všech grafech. Kořenový klíč je stejný v každém komunitním grafu, do kterého člověk vstoupí. Účast v jednom veřejném grafu tak deanonymizuje účast ve všech ostatních. Odvozování podklíčů přes HKDF v protokolu existuje, ale pro identitu se nepoužívá. Pro redakci a pro přispěvatele, který nechce, aby jeho novinářská identita byla propojitelná s jinými komunitami, je to zásadní vlastnost, ne detail. Buď to napsat nahlas do sekce o bezpečnostní hranici, nebo přidat volitelnou identitu odvozenou per strom (za cenu toho, že linie napříč grafy zmizí).
9. Cena KDF je zmrazená napořád, bez cesty ven. Determinismus zakazuje per uživatele volitelný parametr, takže 64 MiB a tři iterace platí po celou životnost prostoru identit. Za patnáct let to bude vypadat jinak. Jedinou cestou je v2 s novým prefixem soli, což znamená všem novou identitu a znovupřijetí, tedy totéž co obnova po kompromitaci. Doporučuji to napsat rovnou jako plánovaný scénář migrace, protože nevyslovený plán je horší než přiznaně drahý.
10. Zákaz cyklů zakazuje vzájemné přijetí. Alice přijme Boba, Bob už nikdy nemůže přijmout Alici. Přitom vzájemné ručení je v lidských sítích ta nejběžnější věc. Pravidlo je navíc pravděpodobně zbytečné: kdyby se členství definovalo jako dopředná dosažitelnost z axiomatické množiny (nejmenší pevný bod), plovoucí cyklus prostě není dosažitelný a tedy nejsou členy, aniž by se cokoli muselo zakazovat. Ušetří to i O(V+E) kontrolu u každého ACCEPT.
11. LEAVE je v rámci vyhodnocení nevratný. Kdo odešel, nemůže být znovu přijat. Návrat se dá vyjádřit jedině tak, že komunita ukotví množinu bez toho záznamu, tedy že se bude tvářit, že se odchod nestal. U append-only návrhu je to zvláštní: jediný způsob, jak říct „vrátil se“, je přepsat minulost.
12. name se nikdy neobjeví v žádném záznamu. Jméno vstupuje jen do KDF. Pasáž, která říká, že runtime MUSÍ rozlišovat členy se stejným jménem, je proto na této vrstvě neproveditelná, protože jádro o jménech nic neví a nikdy je nepřenáší. Zároveň body musí být prázdné, takže neexistuje žádný podepsaný kanál pro sebe-popis. A abstrakt na titulní straně slibuje „entities, attributes, and trust relationships“, přičemž atributy v ústavě nejsou vůbec. Buď zúžit abstrakt, nebo připustit jeden podepsaný atributový záznam.
13. Poznámka o p=1 je nejspíš obrácená. Text říká, že snížení paralelismu mírně oslabuje odolnost proti offline útoku. Celková paměť i počet iterací zůstávají stejné, mění se hlavně latence pro poctivého uživatele, a jedna dráha znamená delší sekvenční závislost, což útočníkovi spíš nepomáhá. Buď to doložit, nebo přeformulovat, protože sebekritika na špatném místě podkopává důvěru ve zbytek.
14. Uzavřenost ukotvené množiny pod prev je MUST, ale algoritmus místo abortu tiše degraduje. Krok 2 záznam jen odmítne. Buď to povýšit na kontrolu s abortem jako u neznámého stromu, nebo z toho udělat SHOULD.
15. JAM není v dokumentu nikde definován, přesto hned druhá věta specifikace říká, že jde o jeho ústavu. Pro cizího čtenáře je to odkaz do prázdna.
16. Wordlist a mluvené slovo. Seznam je mechanicky v pořádku, ale obsahuje homofona, která rozbijí diktování pečeti po telefonu: yew a you, isle a aisle, peak a peek, vale a veil, wood a would, sun a son, root a route, mist a missed, berry a bury, coral a choral. Navíc „tor“ je vlastní prefix slova „torch“, což vadí jen při zápisu bez oddělovačů. Pokud se s hlasovým ověřováním nepočítá, stačí to napsat.
17. Právo na výmaz. Komunitní graf je trvalý, replikovaný a veřejný sociální graf. Veřejný klíč plus pozice v grafu je pseudonymní osobní údaj a LEAVE nemaže nic, jen deaktivuje hrany. Pro evropského provozovatele to není maličkost a v materiálech se to nemá zamlčovat. (Zároveň si tím opravuji větu z jednostránky: chybí databáze hesel a e-mailů, ale „není co uniknout“ je moc silné.)
Dokument je na svůj rozsah nadprůměrně poctivý. Přiznává hranici bezpečnosti, přiznává smrtelnost stromů, přiznává, že rotace klíče neexistuje. Slabina není v tom, co zamlčuje, ale v tom, kam všechno těžké odsouvá. Oprava forku, návrat po odchodu, zbavení se kompromitovaného zakládajícího člena: všechny tři končí větou, že to vyřeší ukotvení, a ukotvení nemá specifikaci žádnou. Dvě implementace tak mohou být obě konformní a přesto se v provozu rozejít, protože se rozejdou v politice, kterou jádro vědomě nepopisuje. Sesterský dokument s aspoň jedním vzorovým, deterministicky popsaným profilem ukotvení by tuhle díru zavřel, aniž by se musela měnit ústava.
První tři kroky, v tomto pořadí: