cryptographie

Chiffrer un message avec OpenPGP.js

Pour ma page de contact, je souhaitais faciliter l'envoi de messages chiffrés avec ma clé publique PGP. Dans l'idéal, une simple case à cocher permettrait de chiffrer le message avant l'envoi au serveur, puis de là dans ma boite mail. C'est pourquoi je me suis penché sur plusieurs implémentations d'OpenPGP en Javascript.

La première, Hanewinkel, était assez légère une fois minifiée (30 ko). Néanmoins, le chiffrement de messages contenant des caractères accentués posait problème. J'aurais pu m'en contenter, mais c'est toujours bon d'arriver à lire des messages directement. Si toutefois ça vous intéresse, voici un petit snippet à intégrer :


function encryptPGP(key, message){
	var pu = new getPublicKey(key);
 	if(pu.vers == -1) 
 		return;

	var keytyp = 0;      // 0=RSA, 1=Elgamal
	var keyid = pu.keyid;
	var pubkey = pu.pkey.replace(/\n/g,'');
	return doEncrypt(keyid, keytyp, pubkey, message);
}

$("#contact form").on("submit", function() {

	// Comme on va peut-être avoir besoin de
	// chiffrer le message, on n'utilise pas
	// le .serialize() habituel d'Ajax, mais
	// un tableau.
	var donnees = $(this).serializeArray();

	// Si la checkbox est cochée
	if($("#contact-crypted")[0].checked) {
		// On récupère la clé publique affichée
		// dans la page
		var key = $("#pgp-key").html();
		// On récupère le message
		var message = $("#contact-message").val();
		// On le chiffre
		message = encryptPGP(key, message);
		// Et on affiche le message chiffré
		$("#contact-message").val(message);

		// Enfin, on remplace le message par
		// son équivalent chiffré dans le tableau
		for(var i = 0; i < donnees.length; i++) {
			if(donnees[i].name == "contact-message")
				donnees[i].value = message;
		}
	}

	// On transforme notre tableau en une suite
	// de paramètres dont Ajax va se charger
	donnees = jQuery.param(donnees);

	// Et on envoie tout ça par Ajax pour
	// éviter un rechargement de la page
	$.ajax({
		url: $(this).attr("action"),
		type: $(this).attr("method"), 
		data: donnees,
		success: function(html) { 
			alert(html);
		},
		error: function(html) {
			alert(html);
		}
	});

    return false;
});

Je suis donc allé voir du côté d'openpgpjs, une solution plus lourde (250 ko), mais qui cette fois fonctionne avec les caractères accentués. Le problème principal qu'on a ici, c'est que openpgp est une promise. C'est cool pour un tas de choses, mais là on aimerait bien attendre la fin du chiffrement avant d'envoyer notre message au serveur. Oh, et au cas où vous vous le demanderiez, openpgp.js embarque un polyfill pour assurer la compatibilité avec les navigateurs qui ne tiennent pas leurs promesses…

Donc je passerai sur mes envies de meurtre lorsque j'ai compris que je devrais refaire tout le code et vous le présente tel quel :


$("#contact form").on("submit", function() {

	// Si la checkbox est cochée
	if($("#contact-crypted")[0].checked) {
		
		var contact = $("#contact form");
	
		// Comme on va peut-être avoir besoin de
		// chiffrer le message, on n'utilise pas
		// le .serialize() habituel d'Ajax, mais
		// un tableau.
		var donnees = $(contact).serializeArray();
	
		// On récupère le message
		var message = $("#contact-message").val();

		// On récupère la clé publique affichée
		// dans la page et on la prépare
		var pubkey = openpgp.key.readArmored($("#pgp-key").html());

		// Enfin, on lance le chiffrement
		openpgp.encryptMessage(pubkey.keys, message).then(function(pgpMessage){
			
			// On récupère le message chiffré
			message = pgpMessage;
			
			// Et on l'affiche
			$("#contact-message").val(message);

			// Enfin, on remplace le message par
			// son équivalent chiffré dans le tableau
			for(var i = 0; i < donnees.length; i++) {
				if(donnees[i].name == "contact-message")
				donnees[i].value = message;
			}

			// On transforme notre tableau en une suite
			// de paramètres dont Ajax va se charger
			donnees = jQuery.param(donnees);

			// Et on envoie tout ça par Ajax pour
			// éviter un rechargement de la page
			$.ajax({
				url: $(contact).attr("action"),
				type: $(contact).attr("method"), 
				data: donnees,
				success: function(html) { 
					alert(html);
				},
				error: function(html) {
					alert(html);
				}
			});
		}).catch(function(error){
			alert(error);
		});
	}
	// Et là la méthode classique,
	// sans chiffrement
	else {
		$.ajax({
			url: $(this).attr("action"),
			type: $(this).attr("method"), 
			data: $(this).serialize(),
			success: function(html) { 
				alert(html);
			},
			error: function(html) {
				alert(html);
			}
		});
	}	

	return false;
});

Note sur la sécurité : ne déployez pas cette solution autrement que pour un petit formulaire de contact. Il s'agit plus d'un proof of concept. J'entends par là qu'elle se base sur ce qu'on appelle dans le milieu host based security, autrement dit aucune véritable sécurité. Il suffit à quelqu'un (que ce soit l'hébergeur, le FAI, le VPN, le navigateur, etc.) de changer la clé publique, le fichier openpgp.js ou le code javascript pour qu'il soit capable de lire le message. Plus d'informations sur cet article.

Fragilités dans le format OpenPGP

OpenPGP, une fragilité découverte par l'ANSSI. Ça ne touche que les systèmes automatisés qui affichent des messages d'erreur évocateurs, mais bon, il est toujours intéressant de rappeler pourquoi il ne faut pas faire confiance à des webmail chiffrées automatiquement. Si vous utilisez PGP, faites-le manuellement. C'est chiant, mais c'est sécurisé.

Cryptanalyse de messages OpenPGP par l'exploitation de fragilités cryptographiques du format

CipherSaber.js

Monsieur Urvoas souhaite pouvoir empêcher le chiffrement, à ce qu'il en ressort des débats pour le ‪#‎PJLRenseignement‬. Le problème, c'est qu'il est impossible d'arrêter le chiffrement de données.

CipherSaber en est l'exemple parfait. Cet algorithme est si simple que n'importe quel développeur amateur peut en faire une implémentation de tête. Il se base sur l'idée qu'un Jedi devait fabriquer son propre sabre laser avant d'être admis comme chevalier, le rendant indépendant de tout Empire totalitaire. Voici une implémentation en javascript de mon cru, très basique, que vous pouvez tester sur JSFiddle ou bien en suivant le lien de la démo.

Dépôt github : https://github.com/yomli/ciphersaber.js/

Une image au PRISM

Une petite pensée sur le « scandale » du PRISM : voici une photo de chaton tout ce qu'il y a de plus banal. Pourtant, elle contient une donnée sensible : la recette de cookies que j'ai minutieusement mise au point pour ravir petits et grands. Le problème, c'est qu'elle est protégée par un chiffrement à clé asymétrique qui la rend uniquement lisible les destinataires que j'aurais choisi.

Quoi que vous fassiez, quand bien même vous trouveriez la clé privée, vous ne pourrez pas y accéder sans connaître le mot de passe de 32 caractères. Vous pouvez utiliser n'importe quel logiciel en votre possession pour le craquer, mais cela vous prendra au minimum 1.59e28 années, soit presque 3 fois l'âge de l'Univers. Peut-être que dans 10 ou 20 ans la technologie aura avancé et permettra de le craquer en quelques secondes, mais j'en doute : ce système de chiffrement existe depuis 22 ans et aucune avancée significative n'a été faite dans le domaine. Dans le pire des cas, je n'aurais qu'à augmenter le mot de passe à 4096 caractères…

Si je suis capable de rendre illisible pour un tiers tout ce que je peux échanger sur un réseau, y compris téléphonique, croyez bien que les terroristes le sont également. Mais également : si vous ne voulez pas être espionné, vous le pouvez.

Et non, la NSA n'a pas de backdoor dans PGP (et encore moins son pendant libre GPG), c'est notamment l'une des raisons pour lesquelles son créateur a eu quelques problèmes avec le gouvernement américain.

Haut de page