RFC 7929: DNS-Based Authentication of Named Entities (DANE) Bindings for OpenPGP
Stephane Bortzmeyer <[email protected]>
| Newsgroups | gmane.network.dns.french |
|---|---|
| Message-ID | <[email protected]> |
Encore une nouvelle utilisation du DNS : stocker les clés PGP.
http://www.bortzmeyer.org/7929.html
----------------------------
Auteur(s) du RFC: P. Wouters (Red Hat)
----------------------------
Un problème classique du système de cryptographie OpenPGP, normalisé
dans le RFC 4880 est de vérifier les clés publiques des correspondants.
Les trouver, c'est relativement facile : le correspondant pense à vous
les envoyer ou bien on se sert tout simplement d'un serveur de clés.
Mais ceux-ci ne garantissent rien sur la clé. N'importe qui peut créer
une clé marquée [email protected] et la mettre sur les serveurs de
clé, même si cette clé n'a rien à voir avec la Maison-Blanche. Avant la
solution de ce nouveau RFC, il n'existait pas de mécanisme sécurisé
pour récupérer une clé publique PGP. Que propose ce RFC ? De mettre les
clés dans le DNS (nouveau type d'enregistrement OPENPGPKEY), dans le
domaine de la partie droite de l'adresse de courrier, sécurisée par
DNSSEC. En gros, il s'agit de faire pour le courrier et PGP ce que fait
déjà DANE (RFC 6698) pour le Web/TLS.
Les serveurs de clés utilisent le protocole HKP (jamais décrit dans un
RFC). Ils fournissent un service très utile en permettant de chercher
une clé, par son identificateur, ou par l'adresse de courrier associé.
Voici un exemple par identificateur :
% gpg --recv-key 0xF3396311465F8E5D
gpg: requesting key 465F8E5D from hkps server hkps.pool.sks-keyservers.net
gpg: key 465F8E5D: public key "Amaelle G <[email protected]>" imported
...
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
et un par adresse (notez que les adresses sont probablement
toutes fausses) :
% gpg --search-key elysee.fr
gpg: searching for "elysee.fr" from hkps server hkps.pool.sks-keyservers.net
(1) hollande (flamby) <[email protected]>
2048 bit RSA key CF22758A, created: 2014-01-06
(2) jacques chirac (ancienne adresse) <[email protected]>
2048 bit RSA key 2A97F759, created: 2007-11-15, expires: 2007-11-16 (expired)
(3) kaziwan <[email protected]>
1024 bit DSA key AA7FD67C, created: 2005-11-28
(4) Gerard Calestroupat <[email protected]>
1024 bit DSA key 82F02C73, created: 2003-08-05, expires: 2003-08-08 (expired)
(5) Toto Berlingo <[email protected]>
1024 bit DSA key E9E920B7, created: 1999-06-10
Ces serveurs n'offrent aucune garantie : n'importe qui peut y
publier une clé, avec n'importe quelle adresse et certaines clés
sont clairement mensongères. L'usage normal est
de récupérer la clé et ses signatures, puis de vérifier les
signatures. Si elles sont faites par des gens qu'on a validé
(directement, ou bien transitivement, jusqu'à une certaine
profondeur), on estime la clé correcte (c'est ce qu'on nomme le
"web of
trust"). Autrement, la clé ne vaut rien. En
outre, le seul système de révocation est de signer une révocation
avec sa clé privée : si on l'a perdue, on ne pourra jamais retirer
la clé des serveurs de clé. Pour ces deux raisons (fausses clés,
et clés devenues inutilisables), il
est donc difficile d'utiliser automatiquement, depuis un
MUA ou un MTA, ces
serveurs.
La solution proposée dans ce RFC est, comme souvent aujourd'hui,
d'utiliser le DNS, qui a montré sa fiabilité et son ubiquité. Tout le
monde peut faire des requêtes DNS, même coincé derrière un pare-feu, et
tout le monde peut les valider, grâce à DNSSEC (RFC 4035).
On va donc publier dans sa zone DNS des enregistrements de type
OPENPGPKEY, indexés par la partie gauche de l'adresse de courrier
(c'est un peu plus compliqué, car elle peut contenir des caractères qui
sont spéciaux pour le DNS ; voir plus loin). Le correspondant qui veut
envoyer du courrier à quelqu'un cherchera cet enregistrement dans le
DNS, et, s'il en trouve un, le validera avec DNSSEC et récupérera ainsi
une clé PGP relativement sûre. La révocation d'une clé se fait
simplement en retirant l'enregistrement du DNS.
La solution de ce RFC rend envisageable de récupérer et de vérifier
automatiquement une clé avant l'envoi d'un message. Mais le RFC note
bien qu'elle ne remplace pas complètement le "web of trust", qui reste
nécessaire si on veut une vérification sérieuse.
Ce RFC a le statut « Expérimental ». Il s'agit de tester l'idée et de
voir si elle marche bien, et n'a pas trop d'inconvénients (par exemple
de taille des zones DNS pour les domaines gérant beaucoup de comptes de
courrier, surtout vu la taille des enregistrements OPENPGPKEY). Si le
nombre de messages chiffrés avec OpenPGP augmente significativement
suite à ce RFC, ce sera bon signe.
Notez qu'une expérience ressemblant à celle-ci avait déjà été faite
avec le type d'enregistrement DNS CERT du RFC 4398. Ce fut un échec
(peu de déploiement, peut-être en raison de la complexité du type
CERT).
La section 2 de notre RFC décrit le format du nouveau type
d'enregistrement DNS. Chaque enregistrement contient une et une seule
clé. Si un utilisateur a plusieurs clés, il doit créer plusieurs
enregistrements. Le type est 61 (et enregistré à l'IANA
<http://www.iana.org/assignments/dns-parameters/dns-parameters.xml#dns-p
arameters-4> depuis août 2014). La partie droite de l'enregistrement
(les données) contient la clé et au moins un ID et une auto-signature.
Les clés PGP complètes, avec des tas de signatures, peuvent être
grosses, trop pour le DNS ; le RFC recommande de ne publier que des
clés minimales (pas trop de signatures, par exemple, et évidemment pas
les photos qu'on peut inclure dans un attribut de la clé, cf. RFC 4880,
section 5.12.1). Avec GnuPG, regardez l'exportation de ma clé avec
toutes ses méta-données, et en exportation minimale (l'annexe A du RFC
décrit les commandes GnuPG à utiliser) :
% gpg --export CCC66677 > key.pgp
% ls -lh key.pgp
-rw-r--r-- 1 bortzmeyer bortzmeyer 86K Aug 25 17:17 key.pgp
% gpg --export --export-options export-minimal,no-export-attributes CCC66677 > key.pgp
% ls -lh key.pgp
-rw-r--r-- 1 bortzmeyer bortzmeyer 5.8K Aug 25 17:18 key.pgp
Le format utilisé est celui du RFC 4880, section 11.1. C'est donc du
binaire qui circule sur le réseau (rappelez-vous bien que, dans le DNS,
le format de présentation, celui des fichiers de zone, et de la sortie
de dig, n'a rien à voir avec le format binaire utilisé sur le réseau.)
Les formats « texte » d'OpenPGP (« "ASCII armor" ») ne sont pas
utilisés sur le réseau. (Donc, avec GnuPG, pas d'option --armor.)
Le format de présentation (celui des fichiers de zone et de la sortie
de dig) encode la clé en Base64.
Et la partie gauche de l'enregistrement DNS ? Quel est le nom de
domaine utilisé ? La section 3 du RFC fixe les règles :
* Le domaine de l'adresse de courrier (partie droite de l'adresse de
courrier) est celui où on met les enregistrements DNS OPENPGPKEY. La
clé pour l'adresse [email protected] sera donc dans la
zone trucmachin.example.
* Le nom de domaine sera la concaténation d'un condensat de la partie
gauche de l'adresse de courrier (stephane+chose, dans l'exemple
ci-dessus), et du composant _openpgpkey, avec le domaine de l'adresse
de courrier (trucmachin.example dans l'exemple ci-dessus). Le condensat
est tronqué à 28 octets. (Le nom de domaine n'est pas utilisé dans le
condensat, pour faciliter la vie des opérateurs qui proposent la même
adresse dans différents domaines.)
* En fait, la règle est plus compliquée en raison des équivalences
entre certains caractères (voir les exemples plus loin). Une
correspondance est donc faite pour certains caractères. (Ce fut l'un
des points les plus discutés dans le groupe de travail à l'IETF.)
* Par exemple, les guillemets (oui, "jipoune le meilleur"@example.com
est une adresse de courrier légale) sont retirés.
* La condensation de la partie gauche de l'adresse de courrier est
faite en SHA-256 (RFC 5754). Cela permet une protection limitée (cf.
section 7.4) de la vie privée : même si un méchant met la main sur tout
le fichier de zone, il ne trouvera pas facilement toutes les adresses
(qui sont des données personnelles). Mais le but principal de cette
condensation est de résoudre le problème de certains caractères qui
sont permis dans la partie locale d'une adresse de courrier, mais qui
posent des problèmes dans le DNS.
Ainsi, si l'adresse de l'utilisateur est [email protected], la requête
OPENPGPKEY devra chercher
c93f1e400f26708f98cb19d936620da35eec8f72e57f9eec01c1afd6._openpgpkey.exa
mple.com. Voici comment calculer cela avec les outils du shell Unix (28
octets = 56 caractères dans la représentation en hexadécimal) :
% echo -n hugh | sha256sum | cut -c -56
c93f1e400f26708f98cb19d936620da35eec8f72e57f9eec01c1afd6
Une des difficultés pour trouver le bon nom de domaine est que les
applications doivent traiter la partie gauche des adresses de courrier
comme opaque (pas le droit d'analyser sa structure) et qu'elles ne
connaissent pas les règles de canonicalisation qu'appliquera le domaine
de destination, comme d'ignorer la casse de la partie locale (ce qui
est souvent fait, mais pas toujours). Par exemple, Gmail ignore les
points dans les adresses (donc [email protected] et [email protected]
arrivent dans la même boîte aux lettres). L'émetteur qui ne connait pas
cette règle va chercher la clé dans un domaine qui ne sera pas le bon.
Idem avec les sous-adresses utilisées par certains domaines (en général
avec le séparateur plus, comme stephane+blog, stephane+ietf, etc). Le
RFC rappelle que l'émetteur ne peut pas se permettre de deviner ces
règles locales, et qu'elles peuvent changer à tout moment. C'est au
destinataire de se débrouiller, en publiant la clé à plusieurs noms, et
en faisant attention aux variantes qu'il publie.
L'internationalisation des adresses de courrier complique évidemment
encore un peu les choses (voir par exemple la section 10.1 du RFC
6530).
La section 6 du RFC se penche sur un problème pratique qu'on rencontre
parfois avec le DNS : la difficulté à recevoir des réponses au delà
d'une certaine taille (il y a trois limites fréquemment rencontrées, la
très ancienne limite de 512 octets du DNS, largement dépassée de nos
jours, la limite de la MTU à 1 500 octets, au delà de laquelle peut
commencer la fragmentation, et la limite par défaut de la plupart des
clients DNS à 4 096 octets). Les clés PGP peuvent être grosses, et le
RFC recommende donc si possible de les récupérer sur TCP, pas UDP.
La section 7 de notre RFC analyse les questions de sécurité liées à
cette technique. Elle rappelle que DNSSEC *doit* être utilisé : les
enregistrements OPENPGPKEY récupérés ne doivent être utilisés que s'ils
sont signés, et que la signature est valide. (Autrement, il serait trop
facile à un ataquant de répondre avec une fausse clé.) Mais si DNSSEC
est nécessaire, il n'est pas suffisant et la validation habituelle des
clés PGP reste nécessaire si on veut un haut niveau de confidentialité.
Ceci dit, comme souvent en sécurité, le mieux est l'ennemi du bien, et
il vaut mieux une clé pas très vérifiée plutôt que d'envoyer le message
en clair, comme le fait presque tout le monde aujourd'hui.
Et, évidemment, la sécurité DNSSEC doit être équivalente à la sécurité
PGP puisqu'un attaquant qui aurait cassé la clé DNSSEC pourrait
remplacer toutes les clés PGP du domaine. Il faut donc une cohérence
dans les politiques de sécurité entre PGP et DNSSEC (section 7.6).
Autre problème de sécurité, cette fois lié à la vie privée : les
requêtes DNS révèlent avec qui on veut communiquer de manière sécurisée
par courrier (RFC 7626). Le fait que le nom de domaine utilisé soit un
condensat de la partie locale de l'adresse de courrier limite un peu
les risques, mais pas suffisamment (si on soupçonne qu'Alice écrit à
[email protected] mais qu'on n'en est pas sûr, il suffit de construire le
nom où se trouve l'enregistrement OPENPGPKEY et de vérifier que ce nom
est demandé, cf. section 7.4). C'est d'autant plus grave que les
clients DNS actuels envoient en général le nom de domaine complet à
*tous* les serveurs, même ceux qui n'en ont pas besoin. La
minimalisation de la requête (RFC 7816) limite ce problème. Le
chiffrement des requêtes DNS (RFC 7858) peut faire le reste. Le cache
du DNS limite un peu les risques et il est donc essentiel de ne pas
faire une requête DNS externe à chaque fois qu'on envoie un message PGP
à quelqu'un, cela ferait fuiter bien trop d'informations (section 7.5).
Pour limiter les risques qu'un attaquant récolte toutes les adresses de
courrier du domaine, le RFC recommande de signer la zone en utilisant
NSEC3 (RFC 5155).
À l'inverse de ce souci de protection de la vie privée, si une
organisation veut lire le courrier de ses employés, la solution est
qu'elle publie une clé d'organisation dans le DNS, pour pouvoir
déchiffrer les messages entrants.
Un autre problème de sécurité est le risque d'utilisation dans des
attaques par amplification
<http://www.bortzmeyer.org/amplification-dns-combien.html>. La taille
importante des enregistrements OPENPGPKEY (surtout avec les clés RSA)
aggrave ce risque. Le RFC suggère de n'envoyer ces enregistrements via
UDP que si l'adresse IP source de la requête a été vérifiée, par
exemple avec les petits gâteaux du RFC 7873.
Où en sont les mises en œuvre de ce RFC ? GnuPG contient le code pour
gèrer ces clés dans le DNS depuis la version 2.1.9. Même chose pour
openpgp-milter <http://github.com/letoams/openpgpkey-milter/>.
L'outil hash-slinger <http://people.redhat.com/pwouters/hash-slinger/>
permet quant à lui de génerer et de vérifier des enregistrements
OPENPGPKEY :
% openpgpkey --fetch --uid [email protected] [email protected]
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: [email protected] key obtained from DNS
Comment: key transfer was protected by DNSSEC
Version: GnuPG v1
mQENBFaJkKsBCADDSwQawRsKYqY/DuxWZjNNn39f14tDaswbpuF+PorNnt0MrepI
0yVY28NQ+5P09j75Os1jlqksK06aAVBtkJvr+T1ip85AxPUdTjD3U3zhM5/YATMi
...
On peut alors enregistrer la clé dans le trousseau PGP :
% openpgpkey --fetch --uid [email protected] [email protected] | gpg --import
gpg: key BBAE5D31: public key "Paul Wouters (online key) <[email protected]>" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
Voici un exemple de récupération de ma clé :
% openpgpkey --fetch --uid '[email protected]' [email protected] |gpg
pub 4096R/CCC66677 2014-02-08 Stéphane Bortzmeyer (Main key) <[email protected]>
uid Stéphane Bortzmeyer <[email protected]>
uid Stéphane Bortzmeyer (Work address) <[email protected]>
uid TextSecure fingerprint (05 d6 3b dc b7 e4 d7 69 2f f6 24 d5 51 31 88 2f a5 59 ae 96 e0 fb a5 75 ab e6 6c 64 ca e9 bb 6a 77) <BdY73Lfk12kv9iTVUTGIL6VZrpbg+6V1q+ZsZMrpu2p3@base64>
sub 4096R/96A4A254 2014-02-09 [expires: 2018-01-10]
sub 4096R/57F02AA1 2014-02-09 [expires: 2017-01-10]
Mais comment ai-je fait pour que ça marche ? hash-slinger permet de
créer la clé directement au bon format :
% openpgpkey --create [email protected]
; keyid: 555F5B15CCC66677
28182f0a278161989f90f090dabd6cab331663d8509ddbae617bb1e7._openpgpkey.bortzmeyer.org. IN OPENPGPKEY mQINBFL2VNABE...
Il n'y a plus qu'à la mettre dans le fichier de zone, et à re-signer.
Mais, car il y a un mais, cela ne marche que si on a des logiciels
récents, qui connaissent le type 61
(OPENPGPKEY). Si ce n'est pas le cas, le signeur
refusera de signer, ou le serveur de recharger la zone. C'était mon
cas, en raison d'une trop vieille version
d'OpenDNSSEC. Trois solutions, commençons par la
plus simple, demander à hash-slinger de générer un enregistrement DNS
à la syntaxe génériques (« types inconnus », du RFC 3597) :
% openpgpkey --create [email protected] --output generic
; keyid: 555F5B15CCC66677
28182f0a278161989f90f090dabd6cab331663d8509ddbae617bb1e7._openpgpkey.bortzmeyer.org. IN TYPE61 \# 5874 99020d0452f654d001100096b30513d96c42e697fd06674b1...
Et c'est cet enregistrement à la syntaxe générique qu'on met dans le
fichier de zone. Sinon, si on aime bien faire l'encodage soi-même,
utilisons xxd :
% openpgpkey --create [email protected] > key.zone
[Edit to keep the zone data]
% base64 -d key.zone > key.bin
[wc -c key.bin to know what number to put in the zone file]
% xxd -p key.bin > key.hex
Et on met le contenu de key.hex dans le fichier
de zone.
Sinon, l'annexe A du RFC fournit une variante de cette solution,
utilisant hexdump.
Voici la récupération de cette clé dans le DNS, avec un dig récent, qui
connait ce type OPENPGPKEY et sait formater le résultat :
% dig OPENPGPKEY 28182f0a278161989f90f090dabd6cab331663d8509ddbae617bb1e7._openpgpkey.bortzmeyer.org
;; Truncated, retrying in TCP mode.
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36368
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 9, ADDITIONAL: 13
...
;; ANSWER SECTION:
28182f0a278161989f90f090dabd6cab331663d8509ddbae617bb1e7._openpgpkey.bortzmeyer.org. 85841 IN OPENPGPKEY ( mQINBFL2VNABEACWswUT2WxC5pf9BmdLHzPeXzHZfvik
ExJHaQ7LHPRVjAQtBiBN0vI3Uh0VgFzjA+0H2sqTduJY
tqd8mrTh9clDnCbrMU8svc7MeWxkW21ogjqBYL8puA3d
...
Notez le « "Truncated, retrying in TCP
mode" ». L'enregistrement est trop gros pour les paquets UDP
qu'accepte dig par défaut (il fait huit kilo-octets, dig accepte
quatre par défaut). Notez aussi le bit AD ("Authentic
Data") dans la réponse : celle-ci a bien été validée par DNSSEC.
Avec un dig ancien, qui ne connait pas ce nouveau type (et, cette fois,
on demande directement en TCP, comme le recommande le RFC) :
% dig +tcp -t TYPE61 28182f0a278161989f90f090dabd6cab331663d8509ddbae617bb1e7._openpgpkey.bortzmeyer.org
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19989
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 9, ADDITIONAL: 29
...
;; ANSWER SECTION:
28182f0a278161989f90f090dabd6cab331663d8509ddbae617bb1e7._openpgpkey.bortzmeyer.org. 86206 IN TYPE61 \# 5874 ( 99020D0452F654D001100096B30513D96C42E697FD06
674B1F33DE5F31D97EF8A4131247690ECB1CF4558C04
2D06204DD2F237521D15805CE303ED07DACA9376E258
B6A77C9AB4E1F5C9439C26EB314F2CBDCECC796C645B
...