iam

Review návrhů: conformance-profile, graph-validation, revision-plan

Čteno 14. 9. 2026 proti IAM.md na commitu 9e95c989. Píše autor prvního auditu, takže opět ne nezávislý hlas.

Souhrn: tohle je dobrá práce a dva nálezy v ní jsou lepší než cokoli v mém auditu. Doporučuju přijmout, s devíti doplňky. Jediné rozhodnutí, které se nedá odložit, je identifikace profilu, a tam bych se přiklonil jinam, než k čemu se návrh kloní.


Dvě věci, které návrhy našly a já ne

Důkaz o forku musí být autentizovaný (graph-validation §2). To je nejlepší nález celé téhle výměny. V algoritmu 1.0 krok 5 prochází sourozence takto: s != r AND (s.tree, s.context, s.actor) == chain_key AND s.prev == r.prev. Žádná podmínka na platnost s. Kdokoli tedy může vyrobit záznam s cizím actor, správným prev a naprosto vymyšleným podpisem, a tím trvale zlomit řetězec oběti. Žádný klíč k tomu není potřeba.

Nejde o teorii, protože spec sama tu cestu otevírá na dvou místech. Za prvé v sekci o ukotvení: nový účastník smí převzít ukotvenou množinu od důvěryhodného protějšku. Za druhé v poznámkách k algoritmu, kde se výslovně tvrdí, že krok 2 znovu ověřuje invariant admission, „takže vyhodnocení nezávisí na tom, že admission proběhla správně“. Sourozenecká kontrola je přesně to místo, kde tohle tvrzení neplatí. Návrh to opravuje správně a navíc správně dodává, že zpětné datování nesmí důkaz o dvojím podpisu smazat.

Rezervace genesis klíče (graph-validation §4) opravuje mě. V auditu jsem napsal, že únik genesis klíče je inertní, protože genesis není členem grafu. To platí jen do chvíle, kdy ho někdo přijme. Genesis veřejný klíč je veřejně čitelný z bootstrapu, je to platný Ed25519 klíč a nic v 1.0 nebrání kterémukoli platnému členovi vydat na něj obyčejný ACCEPT. Pak držitel neuklizeného klíče podepisuje jako plnoprávný člen. Erasure je tedy nosná, ne pás k šlím, a moje věta o tom byla špatně.

K tomu dvě menší, kde je verze v návrhu lepší než moje:

Zdůvodnění p=1 je celé chybné, nejen jeho bezpečnostní věta. Argon2 parametr m je celková paměť napříč drahami, takže „čtyři paralelní 64MiB buffery“ nikdy neexistovaly; p=4 by spotřebovalo stejných 64 MiB ve čtyřech drahách po 16. Můj nález 13 mířil na poznámku o odolnosti, návrh odstraňuje i ten důvod. Souhlas i s tím, že p se měnit nemá, protože by to změnilo odvozené klíče.

Nejednoznačnost vstupu do pečeti. Spec píše pub_hash = SHA-512(public_key) a o kus výš definuje veřejný klíč jako 64 hexadecimálních znaků. Přehlédl jsem to. Rozdíl není kosmetický, tady je tentýž klíč přečtený třemi způsoby:

pubkey              d48a52a5…88ef
SHA-512(32 bajtů)   horizon arch heath fleet
SHA-512(hex textu)  onyx maple heron moss
SHA-512(HEX textu)  wake grain frost cloud

Tři implementace, tři pečeti, jedna identita. Rozhodnutí pro syrové bajty je správné.


Co je v profilu udělané dobře

Kryptografický profil v §1 má vlastnost, kterou bych zvlášť pochválil, protože nevypadá jako náhoda: tím, že se A i R zamknou do podgrupy prvořádu ([L]A = O, A != O, [L]R = O) a S do 0..L-1, přestává být volba mezi kofaktorovou a nekofaktorovou rovnicí pozorovatelná. Ověřovač postavený na libsodium a ověřovač s kofaktorovou rovnicí dojdou ke stejnému výsledku. Přesně tohle se od profilu chce: ne vybrat jednu z možností a doufat, ale zúžit vstupy tak, aby na té volbě nezáleželo. Povolení R = O s uvedeným odůvodněním je v pořádku.

Uzavřené schéma obálky i záznamu, omezení číselných tokenů na 0|[1-9][0-9]*, kanonická base64 s kontrolou přes zpětné zakódování, odmítnutí duplicitních klíčů až po dekódování escape sekvencí a zákaz zahazovat neznámá pole před ověřením: každý z těch bodů zavírá jedno místo, kde se dvě implementace mohly rozejít. HKDF s povinným Extract i Expand je další reálná nejednoznačnost, kterou jsem minul.

Procesní disciplína je nadprůměrná. Status „PROPOSED, not adopted“, oddělení erraty od změny pravidel, věta, že širší změna se nesmí vydávat za redakční, a připnutí auditních artefaktů k původnímu commitu. Tohle je lepší hygiena než u většiny vydaných specifikací.


Kde bych rozhodl jinak: identifikace profilu

Návrh správně pojmenovává jádro problému: štítek 1.1 na dokumentu nevybírá pravidla na drátě, v=1 a prefixy IAM1:* nový profil nekódují, a dva hostitelé s různými profily mohou pod stejným tree id uznávat různé záznamy. Souhlas, a je to hlubší postřeh než cokoli v mém auditu.

Kloní se ale k vnějšímu výběru profilu z důvěryhodné konfigurace. Proti tomu mám tři argumenty.

Za prvé, tree id tím přestává identifikovat graf a začíná identifikovat jen jeho bootstrap. Dvě komunity mohou sdílet tree id a neshodnout se na členství. Spec přitom tree id staví jako obsahově vázanou identitu grafu.

Za druhé, „důvěryhodná konfigurace nebo autentizovaná dohoda“ je v jinak offline protokolu nejslabší článek. Celý smysl je, že k ověření není potřeba živá instituce. Profil z konfigurace ji tiše vrací zpátky, jen se jí neříká instituce.

Za třetí, cena. Migrační náklad je úměrný instalované základně. Pokud je jediný živý strom pořád ten s jediným bootstrap kořenem a bez následných záznamů, je ten náklad dnes prakticky nulový. Za rok už nebude. Tohle si ověř, než se rozhodne, protože na tom rozhodnutí to stojí.

Minimální konkrétní podoba: nechat tvar záznamu i v=1 beze změny a lišit jen doménový prefix pro tree id (IAM1:tree\0 pro 1.0, jiný řetězec pro 1.1). Staré stromy si ponechají svoje id pod starými pravidly, nový strom má id obsahově vázané na pravidla, kterými se řídí. Žádné nové pole, žádná nová verze záznamu, nic ke konfiguraci. Druhá varianta je vložit identifikátor profilu do bootstrap deskriptoru, což je čistší, ale mění tvar deskriptoru.

Pokud vnější výběr přesto vyhraje, patří do textu jedna věta: samotné tree id není tvrzením o interoperabilitě, jednotkou je dvojice profil a tree id.


K §3 graph-validation, kde návrh mění výsledek

Podporuju to, a to argumentem, který v dokumentu není: §3 zavírá díru se zpětným datováním. Můj nález 5 předpokládal, že monotónnost řetězce omezuje, jak hluboko může aktér datovat dozadu. V 1.0 to neplatilo: nemonotónní záznam se odmítl, ale jeho potomek se přes něj aplikoval, takže si aktér mohl resetovat čas pod vlastní hlavu a pokračovat. S §3 to nejde. Monotónnost m je tedy bezpečnostní vlastnost, ne hygiena, a pravidlo je správné.

Cena je reálná a stojí za to ji napsat nahlas: chybný klíček na hodinách teď zabije řetězec stejně spolehlivě jako dvojí podpis. Způsoby, jak si nevratně zlomit řetězec, jsou nově dva místo jednoho. Obrana je na straně podepisujícího a je to táž obrana jako proti forku: runtime musí znát hlavu vlastního řetězce a odmítnout podepsat pod ni. Dal bych obě povinnosti na jedno místo jako závazky podepisujícího, ne je nechal roztroušené mezi fork a monotónnost.


Devět věcí, které v návrzích chybí

  1. Omezená karanténa. Není ani v jednom ze tří dokumentů. Admission specifikuje IAM, takže požadavek na omezené prostředky a definovanou politiku vyřazování patří sem, ne do runtime. Zůstává otevřeným DoS kanálem.
  2. Výjimka pro bootstrap deskriptor. Uzavřené schéma říká, že záznam má přesně devět polí. Deskriptor pro výpočet tree id jich má šest. Buď explicitní druhé schéma, nebo se ta dvě pravidla perou.
  3. Cena kontroly podgrupy. [L]A = O je plnohodnotné násobení skalárem na každý klíč, tedy zhruba zdvojnásobení ceny ověření. Na PIS a PIZE nad velkou ukotvenou množinou to není zanedbatelné. Buď to přiznat a změřit, nebo připustit ekvivalentní rychlejší test, nebo zavést cache ověřených klíčů vázanou na profil.
  4. Jedno id, více platných podpisů. Podpis Ed25519 není jednoznačný pro daný klíč a zprávu, pokud podepisující chce alternativu, a id podpis nekryje. Deduplikace podle id v §1 řeší stav grafu, ale profil by měl říct, která obálka se uchová a že bajty podpisu nejsou součástí identity záznamu.
  5. Rozsah požadavku na known_trees. Vyhodnocení je per (tree, context). Abortovat kvůli tomu, že v témže souboru je záznam z neznámého cizího stromu, je zbytečná vazba. Buď požadavek omezit na scope, nebo říct, že ukotvené množiny jsou vždy per strom.
  6. Šířka pečeti. Odložit produktové rozhodnutí je legitimní. Text se ale opravit dá hned: sekce dnes uvádí jediné číslo, narozeninovou mez, a to je špatná hrozba. Jedna věta o ceně cílené kolize nestojí nic a zabrání tomu, aby čtyři slova někdo četl jako autentizátor.
  7. Drobty z auditu, které nikdo nezmiňuje: homofona v seznamu slov, nikde nedefinovaný JAM, a právo na výmaz. Linkabilita napříč grafy v redakčním výčtu je, výmaz ne.
  8. Ukotvení. V kritériích pro vydání 1.1 je pět bodů a ani jeden se ukotvení netýká. Jenže tam končí tři nezávislé poruchy: oprava forku, návrat po odchodu a zbavení se kompromitovaného zakládajícího člena. Verze, která utáhne validaci a ukotvení nechá nepopsané, konvergenci pořád nedoručí. Stačil by sesterský dokument s jedním vzorovým, deterministicky popsaným profilem.
  9. Potvrdit, že rozpor v sekci LEAVE je opravdu zavřený. Redakční tabulka u axiomatických členů je v tomhle nejednoznačná. Chce to výslovně: zmizela věta, podle níž může bývalého axiomatického člena znovu přijmout jiný platný člen, nebo je jen omezená na jiné ukotvení?

Verdikt

Přijmout. Jedno vydání, ne dvě, souhlasím s návrhem. Jediná výhrada k tomu: současný text obsahuje věcně chybné zdůvodnění u p=1 a zavádějící větu o m, a obojí dnes aktivně mate implementátory. Pokud se 1.1 protáhne, vydal bych erratu dřív, ale s viditelnou poznámkou, že revize validace probíhá a konformitu 1.0 zatím nelze testovat.

Rozhodnutí, které se nedá odložit, je identifikace profilu. Všechno ostatní v těch třech dokumentech snese měsíc. Tohle ne, protože jeho cena roste s každým stromem, který mezitím vznikne.