Résumé

  • CertificateVerify signe le transcript avec la clé privée du certificat ; Finished authentifie ensuite un transcript plus avancé avec une clé issue du secret de trafic de poignée de main de l’émetteur.
  • La fin est directionnelle. Le Finished du serveur ne prouve ni celui du client, ni l’acceptation du 0-RTT, ni la jambe amont d’un proxy, ni l’autorisation applicative.

Le succès avait été déclaré un message trop tôt

La chaîne était acceptable et la signature CertificateVerify correcte. Pourtant, le verify_data reçu ensuite ne correspondait ni au transcript ni à la clé calculés par le client. RFC 9846 impose alors une erreur fatale. La conversation protégée ordinaire n’a jamais commencé.

CertificateVerify démontre la possession de la clé privée liée au credential présenté. Il signe une construction séparée par rôle, fondée sur le transcript jusqu’au Certificate. La validation du chemin et du nom de service reste une décision distincte.

Finished n’est pas une seconde signature du certificat. TLS 1.3 dérive finished_key du secret de trafic de poignée de main propre à l’émetteur, puis calcule un HMAC du transcript incluant CertificateVerify lorsqu’il existe. Il confirme les clés calculées et l’histoire exacte de cette connexion.

Le mode PSK rend la séparation manifeste : Certificate et CertificateVerify peuvent disparaître, jamais Finished. À l’inverse, une bonne signature suivie d’un mauvais Finished reste un échec complet.

Le transcript n’est pas la capture

Le hachage inclut les messages de poignée de main, leur type et leur longueur, dans l’ordre. Il exclut les en-têtes de record, les alertes et les données applicatives. Fragmenter un message sur plusieurs records ne modifie pas l’objet logique.

Après HelloRetryRequest, le premier ClientHello entre dans l’histoire sous forme de message_hash synthétique. Une sonde ne peut donc pas concaténer des fragments visibles et prétendre connaître l’entrée de Finished. Comme la suite de TLS 1.3 est chiffrée, une reconstruction exacte demande des secrets de diagnostic issus de l’extrémité ou une preuve équivalente dans l’implémentation.

La suite cryptographique sélectionne le hachage utilisé par le transcript et HKDF. La longueur de verify_data est celle de ce hachage, non les douze octets fixes de TLS 1.2.

Deux sens, deux instants de fin

Client et serveur possèdent des secrets de trafic de poignée de main distincts. Chacun émet son Finished et vérifie celui de l’autre à un moment différent.

Après son Finished, le serveur peut déjà employer sa clé applicative d’émission. Il peut même envoyer des données avant de recevoir le Finished client. À cet instant, la norme rappelle qu’il n’a encore aucune assurance sur l’identité ou la présence du client : le ClientHello peut avoir été rejoué.

Le client vérifie le Finished serveur, envoie éventuellement son propre Certificate et CertificateVerify, puis son Finished. Un unique tls_complete détruit l’identité du sens et masque une authentification client encore inachevée.

Il faut conserver, pour chaque extrémité, finished_sent, peer_finished_verified et l’état réel de la machine TLS. L’application définit ensuite la combinaison exigée pour son action.

Les exceptions ne changent pas la règle

Le 0-RTT peut partir avant le Finished serveur. Il dépend d’un PSK antérieur, peut être rejoué et ne prouve ni acceptation ni réussite ultérieure. Le registre doit distinguer offre, acceptation ou refus du early data, vérification de Finished et trafic ordinaire.

Un terminateur TLS crée aussi deux histoires. Client–proxy et proxy–origine n’ont ni secrets, ni transcripts, ni Finished communs. Une corrélation applicative peut relier les jambes ; elle ne peut pas transférer la preuve cryptographique de l’une vers l’autre.

Les API témoignent seulement dans leur périmètre

OpenSSL expose SSL_is_init_finished() pour l’état où des données pleinement protégées peuvent circuler, tout en signalant les particularités du early data. SSL_get_verify_result() ne décrit que la vérification du certificat.

Le key log permet un diagnostic précis, mais divulgue du matériel secret. Il doit rester isolé, limité dans le temps et supprimé après le test. BoringSSL documente que ses accesseurs Finished renvoient zéro en TLS 1.3 ; GnuTLS propose des hooks et une erreur Finished dédiée. Un même nom d’API ne constitue donc pas un contrat portable.

Le test décisif garde Certificate et CertificateVerify valides, altère un octet de Finished et exige un échec terminal. Il recommence sans Finished client, en PSK sans certificat, après HelloRetryRequest et sur deux jambes de proxy différentes. C’est le code en fonctionnement, non la présence d’un symbole, qui établit la frontière.