Résumé

  • Le README de Barry donne ::/0 comme ressource IPv6 par défaut d’une ancre de confiance, alors que l’issue 7 signale le rejet de cette même écriture dans des champs de certificat et de ROA.
  • Dans le code figé, une chaîne non citée ne peut commencer que par un caractère alphanumérique, $ ou . Les deux-points et la barre oblique sont admis ensuite : le zéro ajouté dans 0::/0 ouvre donc le jeton.
  • RFC 4291 reconnaît ::/0 comme préfixe légitime et RFC 5952 en fait la forme canonique. 0::/0 représente la même valeur, mais n’est pas la sortie à compression maximale.
  • Rien ne prouve qu’un objet incorrect ait été produit, qu’un relying party l’ait reçu ou que la production de LACNIC ait été touchée. Il faut un tableau de conformité allant du lexer au DER, avec un stade terminal explicite.

Un zéro qui n’ajoute aucune adresse

Deux écritures suffisent pour localiser la question :

::/0
0::/0

Le zéro visible dans la seconde n’élargit ni ne réduit le préfixe. Les deux chaînes représentent l’adresse IPv6 entièrement nulle avec une longueur de préfixe égale à zéro. D’après l’issue 7 du dépôt LACNIC/barry, il change néanmoins le trajet dans le programme : la forme canonique échoue, la forme commençant par un chiffre sert de contournement.

La différence est importante pour un générateur de matériel RPKI, surtout lorsqu’il doit fabriquer des objets volontairement invalides afin de tester des logiciels de validation. Avant d’attribuer un échec à un certificat, à un ROA ou à un relying party, il faut savoir qu’un texte légitime est devenu une valeur IP puis des octets précis. Ici, la chaîne s’arrête avant cette chaîne de transformation.

Le contexte institutionnel invite à la mesure. Les métadonnées du dépôt présentent Barry comme un outil de génération, pas comme un service de production. Le README figé au commit examiné qualifie le projet et la spécification du Repository Descriptor de travaux en cours et prévient que des ruptures restent possibles avant la version 1.0. Les listes capturées de releases et de tags sont vides. Une issue ouverte et reproductible est donc le fonctionnement normal d’un prototype exposé au public, non l’indice d’un incident.

La trace demande une lecture exacte

L’issue a été ouverte le 28 août 2026. Au 6 septembre, elle n’avait ni étiquette, ni commentaire, ni mise à jour ultérieure. GitHub attribue au rapporteur l’association NONE. Aucun mainteneur n’y confirme encore le comportement, sa cause ou une correction.

Le texte place ::/0 dans deux endroits du descripteur : l’extension de ressources IP d’un certificat d’autorité et ipAddrBlocks dans un ROA. Il propose 0::/0 comme solution provisoire. Toutefois, la reproduction affichée et la trace collée ne sont pas identiques octet par octet. La première valeur imprimée dans la trace porte déjà le zéro du contournement; plus loin, l’analyse s’interrompt au premier : d’un autre champ avec Unexpected character: : (0x3a).

Il ne faut ni ignorer cette différence ni lui faire dire trop. La trace montre bien un échec sur un deux-points initial. Le rapport affirme que les deux champs refusent la forme canonique. Le dossier public ne démontre pas séparément deux échecs identiques au cours d’une seule exécution. C’est précisément pourquoi une preuve de test devrait conserver les octets d’entrée et leur empreinte plutôt que seulement une copie d’écran ou un extrait remanié.

Surtout, cette trace ne contient ni certificat mal formé, ni ROA émis, ni publication de dépôt, ni résultat de validation. Elle ne prouve aucune modification de route et ne concerne pas, dans les éléments disponibles, le service RPKI de production de LACNIC.

La règle initiale du lexer suffit à expliquer l’écart

Le code a été figé au commit 994a598321336baf1767f0fbfb460ed96c29fe4f, daté du 1er septembre. Son objet porte sur authorityCertIssuer dans l’AKI; il ne se présente pas comme la correction de l’issue 7. L’historique récent ne doit pas être transformé en généalogie supposée du défaut.

Dans src/rpki_tree.c, next_token commence une chaîne non citée uniquement si son premier caractère est alphanumérique, s’il s’agit de $ ou de . Un deux-points ne satisfait aucun de ces cas. Il passe vers try_emoji, puis un : ordinaire déclenche l’erreur de caractère inattendu.

La règle de continuation est plus permissive. Après l’ouverture d’un jeton, elle accepte les deux-points et la barre oblique, en excluant surtout les espaces et séparateurs structurels du langage. 0::/0 ne convainc donc pas un parseur IPv6 auparavant hostile : son premier 0 donne simplement au lexer le droit de créer une chaîne, dans laquelle ::/0 peut ensuite rester.

Le README rend cette dissymétrie directement vérifiable. Il montre aussi 2001:db8::/64, qui commence par un chiffre et franchit la première règle. Puis il donne ::/0 comme ressource IPv6 par défaut de l’ancre de confiance. Dire que « l’IPv6 compressé est pris en charge » masquerait le point décisif : la compression après un chiffre et la compression dès le premier caractère ne suivent pas le même chemin lexical.

Le fichier src/field.c contient un parse_ip_node situé en aval. Il sépare la chaîne au /, reconnaît IPv6 grâce aux deux-points, appelle inet_pton, puis lit la longueur de préfixe. La forme refusée n’arrive jamais jusque-là. L’appeler erreur d’inet_pton, de longueur ou de liaison de champ déplacerait le diagnostic vers un composant qui n’a rien décidé.

L’entrée canonique ne devrait pas être le cas exceptionnel

RFC 4291 autorise :: pour compresser une suite de groupes nuls et désigne ainsi l’adresse entièrement nulle. Pour un préfixe, elle associe une adresse IPv6 écrite sous une forme légitime à une longueur après /. ::/0 est donc une entrée valide.

RFC 5952 distingue l’acceptation et l’émission. Une implémentation doit accepter les formes légitimes de RFC 4291; lorsqu’elle produit du texte, elle doit préférer la représentation canonique et la compression maximale. Ainsi, 0::/0 porte la même valeur que ::/0, mais ne devrait pas être la forme normalisée en sortie.

Un contrôle local borné avec la bibliothèque Ruby IPAddr a réduit ::/0, 0::/0, 0000::/0 et l’adresse nulle entièrement développée à la même valeur et à la même plage. Ce contrôle n’est pas une exécution de Barry et n’a pas l’autorité normative des RFC. Il confirme seulement que l’hypothèse d’équivalence mérite un test direct à la frontière du descripteur.

RFC 3779 montre enfin pourquoi il faut séparer le texte de l’objet. Les ressources IP sont encodées en DER sous forme de BIT STRING. Le bloc couvrant toutes les adresses, de longueur zéro, devient 03 01 00; la graphie du descripteur n’est pas une propriété conservée dans le certificat ou le ROA. Si le lexer refuse l’entrée, aucun octet DER ne peut être qualifié de bon ou de mauvais.

Une table de conformité, pas une promesse générale

Le contrôle utile tient dans un tableau versionné. Chaque ligne devrait contenir les octets exacts du descripteur et leur empreinte, le champ visé, le résultat de tokenisation, la famille d’adresses, la valeur numérique et la longueur normalisées, la graphie canonique, le commit du parseur et du générateur, puis l’empreinte de l’objet si sa construction a été atteinte.

Un champ terminal contrôlé—descriptor-tokenize, prefix-parse, field-bind, object-build, DER-encode ou RP-validate—empêcherait d’attribuer au validateur un cas que le générateur n’a jamais produit. L’invariant central serait simple : parse(format(parse(input))) conserve le triplet famille, valeur, longueur. Les écritures légitimes équivalentes convergent vers la même valeur DER lorsque la génération réussit.

Ce contrat ne prétend pas certifier Barry ni couvrir toute l’IPv6. Il rend un résultat localement vérifiable. Pour un outil de tests négatifs, cette modestie est essentielle : on ne peut savoir ce qui est délibérément invalide qu’après avoir prouvé ce que la chaîne positive a réellement construit.

Sources