CBOR, et pourquoi WebAuthn y a tenu

Ouvrez un objet d’attestation WebAuthn et vous n’y trouverez pas de JSON. Vous y trouverez du CBOR, défini par la RFC 8949 : le même modèle de données que JSON, encodé en binaire.

Pourquoi pas JSON

Le code qui produit une attestation tourne sur une clé de sécurité : un microcontrôleur qui a très peu de mémoire et aucun goût pour l’analyse de chaînes. Le CBOR s’écrit et se lit avec quelques centaines d’octets d’état, ce que JSON ne permet pas.

Le CBOR porte aussi les chaînes binaires nativement. Une clé publique en JSON doit être encodée en base64, ce qui coûte un tiers d’octets en plus et une étape d’encodage de chaque côté. En CBOR, ce ne sont que des octets.

La partie qui fait trébucher

Les clés de dictionnaire CBOR ne sont pas toujours des chaînes. En COSE, le format de clé qu’emploie WebAuthn, ce sont de petits entiers : 1 pour le type de clé, 3 pour l’algorithme, -1 et -2 pour les paramètres de courbe. Décodez une clé COSE en tableau PHP et vous obtenez des clés numériques sans noms, ce qui déconcerte la première fois.

Pire pour qui vérifie une signature : l’encodage doit être canonique. Les mêmes données peuvent être encodées de plusieurs façons valables, et une signature couvre les octets, pas le sens. Réencodez une structure différemment et la signature échoue, sans rien pour vous dire pourquoi.

La règle pratique

Ne réencodez jamais ce que vous vous apprêtez à vérifier. Gardez les octets d’origine, vérifiez contre eux, et n’employez la forme décodée que pour lire des valeurs. C’est la cause de loin la plus fréquente d’une signature WebAuthn qui refuse de se valider sans raison apparente.

cbor-php encode et décode le CBOR, et garde les octets d’origine disponibles exactement pour cette raison.