[dup-crypto-rs] Manage cryptographic operations for the Duniter/Ğ1 eco-system

dup-crypto-rs est une bibliothèque Rust permettant de gérer toutes les opérations de cryptographie utiles dans l’écosystème Duniter/Ğ1.

C’est une externalisation de la crate crypto du dépôt Dunitrust, le but est d’externaliser tous ce qui pourrait servir a d’autres projets, je commence par la crypto, d’autres « briques » suivrons (comme la gestion du format des documents par exemple).

Fonctionnalités

  • Génération de trousseaux de clés Ed25519 via scrypt
  • Calcul de hash sha256
  • Encodage/décodage base58
  • Encodage/décodage base64
  • Génération sécurisée de hasard
  • Encryption/Decryption aes256
  • Lecture/écritrure du format DEWIF

Documentation

La documentation de cette bibliothèque est auto-générée a partir du code source et auto-publiée ici :

https://docs.rs/dup-crypto

Tous les morceaux de codes présent dans la documentation sont automatiquement compilés et testés dans l’intégration continue, vous avez donc la garantie qu’ils sont à jours et fonctionnels :slight_smile:

Projets qui utilisent cette bibliothèque

  • Dunitrust
  • Dup-tools-wasm

Projets qui pourraient peut-être l’utilisée ?

Cette bibliothèque pourrait par exemple être utilisée par :

  • Un éventuel futur client Rust
  • Un éventuel client PWA (via un binding webassembly)
  • Cesium ? (via un binding webassembly)*

Je peut coder des binding en Python/Java/NodeJs, mais je ne le ferai que si des développeurs sont réellement intéressés (pas envie de prendre du temps a coder un binding s’il n’est pas utilisé). Cela pourrait par exemple servir à :

  • DuniterPy ? (via un binding PyO3)*
  • Duniter ? (via un binding Neon)*

Ce n’est bien entendu qu’une mise a disposition, pour ceux qui seraient intéressés, les autres peuvent passer leur chemin :slight_smile:

Avantages :

  • Plus facile a compiler que des lib C/C++ (facilite le build et la livraison pour tous types d’environnement cible)
  • Pas besoin de librairie dynamique -> plus de problèmes chez les utilisateurs pour qui il manquerai une lib sur leur système
  • Meilleurs performances que libsodium : Tests de performances ed25519 : sodium et ring
  • Plus safe (garanties du Rust empêche les failles dues a une mauvaise gestion de la mémoire, ce qui était le cas de la faille heartbleed).

Dépôt git

3 Likes

Beau travail !

Difficile à dire, car pas sûr que les performances en mode webassembly soient comparables à celle de Libsodium (utilisé par l’App Android), via un binding Cordova.
Par ailleurs, cette implémentation ne permettant pas (si j’ai bien compris) de lire autre chose que le seed, les fichiers de clef (PubSec, EWIF, WIF) ne pourraient plus être lu. Je me trompe ?

Par ailleurs, j’utilise pour Cesium+ une dérivation de clef ED25519 vers curve25519 pour le chiffrement des messages privés, et aussi les fonction box et open_box qui chiffrent et déchiffrent avec vérification de l’expéditeur.

1 Like

En effet, on avait testé avec @1000i100 a l’époque et les perf en wasm n’étais pas aussi bonne qu’espérer, mais ça remonte à loin, j’aimerais bien retester pour voir si ça a évolué :slight_smile:

Pour le moment oui, mais ce n’est pas figé, avec les “features” (compilation conditionnelle), je peut implémenter des fonctionnalités optionnelles que je n’utiliserai pas dans Dunitrust mais qui servirait a d’autres projets.
Si du monde souhaiterai utiliser la lib dup-crypto-rs si elle incluais tel ou tel fonctionnalité précise ça me motiverai peut-être a inclure la fonctionnalité demandée :slight_smile:

En effet cette fonctionnalité n’est pas présente. On travaille justement avec @tuxmain sur l’ajout d’une fonctionnalité de chiffrement de messages privés, ce sera inclus dans dup-crypto-rs, mais ça ne se basera pas du tout sur les mêmes concept crypto que Cesium+.

Merci pour ton retour @kimamila, je vois en effet qu’en l’état Cesium ne pourrais pas se servir de dup-crypto-rs, et le taff a réaliser pour que ce soit possible semble conséquent, pas sur que je sois motivé a le faire (ce n’est pas obligé que ce soit moi d’ailleurs, ça reste un logiciel libre).

Après c’était une idée en l’air comme ça, je partage cette lib pour… partager, si ça peut servir a d’autres tant mieux, sinon tant pis.

Que Libsodium tu veux dire, car je n’ai rien inventé :slight_smile: le concept de box est intégré et parfaitement décrit.
Quelles sera le type de chiffrement des messages du coup ?

quelles fonctions de libsodium utilise tu précisément ? Quel protocole ? Ya t’il une spec quelque part ? :slight_smile:

sodiumoxide me propose une seule façon de faire :

crypto_box_curve25519xsalsa20poly1305 , a particular combination of Curve25519, Salsa20, and Poly1305 specified in Cryptography in NaCl .

Est ce que ça correspond a ce qu’utilise Cesium+ ou pas ?
Par contre il semble que sodiumoxide n’expose pas les fonctions permettant de convertir de ed25519 en curve25519 et vice-versa :confused:

Je ne sais pas encore, je me suis motivé a coder ça car @tuxmain en a besoin pour gmixer-rs. Je me disais que j’irai au plus facile en utilisant les algos proposés par ring, donc probablement Chacha20 au lieu de Salsa20.
Mais je vais être obligé d’utiliser une autre lib pour l’exchange DH, du coup j’investigue justement :slight_smile:

Concernant ce dernier point j’ai trouvé comment faire en pure Rust :slight_smile:

2 Likes

J’utilise la fonction crypto_box définie ici. En fin de page, il est indiqué que d’autres algos sont également supportés, mais je n’ai pas testé.
Voir ici pour le code de Cesium : crypto_box · Search · GitLab

1 Like

A priori, tu peux seulement faire keypair ed25519 → keypair curve25519 , mais pas l’inverse.

1 Like

Merci ça correspond bien au process crypto_box_curve25519xsalsa20poly1305 décrit dans sodiumoxide (le wrapper Rust de libsodium).
Du coup j’ai fait mes recherches entre temps et il existe une ré-implémentation en Rust :smiley:

Donc je pourrais proposer cette fonctionnalité dans dup-crypto-rs relativement facilement :slight_smile:

Merci je viens de regarder et j’ai quelques questions :

  • Comment le cesium du destinataire connaît t’il le nonce, il est ajouté au message ?
  • Aurait tu un exemple ou/et des spec quid écrivent le format d’un message Cesium+ ?

Ça pourrait être intéressant que d’autres logiciels soient capable d’envoyer des messages aux pod Cesium+ :yum:

Pour être plus précis, ed25519 désigne la représentation d’Edwards de la courbe curve25519 souvent raccourcie “ed25519”. Et ce que tu nomme curve25519 correspond en réalité a la représentation dite de Montgomery de la même courbe curve25519 souvent raccourcie “x25519”.

La représentation d’Edwards est utilisée pour signer, et la représentation de Montgomery est utilisée pour l’échange Diffie-Hellman.
Il est possible de passer de la représentation d’Edwards à la représentation de Montgomery dans tous les cas, mais on perd le signe. Si on connaît le signe d’origine (+ ou -) alors on peut faire la conversion inverse dans 99% des cas :slight_smile:

A noter que, contrairement a ce que l’on pourrait croire, la représentation de Montgomery ne sert pas a chiffrer. D’ailleurs, la fonction crypto_box de NaCl réalise un chiffrement symétrique avec l’algo salsa20, mais donc la clé de chiffrement ne peut être connue que de l’émetteur et du destinataire grâce a un échange Diffie-Hellman différé.
Je trouve ça passionnant la crypto, plus on creuse et plus on se rend compte que c’est en fait plus complexe et plus subtils qu’il n’y parait, ça donne le vertige parfois :grin:

1 Like

je viens de livrer dup-crypto-rs v0.13 avec la fonctionnalité de chiffrement des messages dont @tuxmain a besoin pour gmixer-rs :smiley:

La documentation de cette nouvelle fonctionnalité, avec exemples s’il vous plaît, est disponible ici : dup_crypto::private_message - Rust

J’ai oublié de le préciser dans le 1er post, mais tout le code de dup-crypto-rs est testé avec plus de 60 tests unitaires qui couvrent plus de 93% du code : Codecov

Les différences avec crypto_box de NaCl :

  • Utilise ChaCha20Poly1305 au lieu de Salsa20Poly1305. Salsa20 est littéralement “l’ancêtre de chacha20” : Salsa20 - Wikipedia
  • Ajout de la partie Aead (Authenticated Encryption with Associated Data) qui permet de garantir qu’un message d’un autre réseau (autre service ou/et autre monnaie) ne pourra pas être déchiffré par erreur.
  • Permet de choisir la stratégie d’authentification des messages : en effet crypto_box ne signe pas les messages, mais utilise une technique “d’authentification par clé publique” qui permet au destinataire d’avoir la preuve que le message proviens bien de l’expéditeur sans pouvoir le prouver au monde extérieur (car la vérification de cette preuve nécessite la clé privée du destinataire ou de l’expéditeur). Je cite la doc de libsodium sur ce sujet :

The seal() function is not meant to provide non-repudiation. On the contrary: the seal() function guarantees repudiability. A receiver can freely modify a boxed message, and therefore cannot convince third parties that this particular message came from the sender. The sender and receiver are nevertheless protected against forgeries by other parties. In the terminology of http://groups.google.com/group/sci.crypt/msg/ec5c18b23b11d82c, crypto_box uses “public-key authenticators” rather than “public-key signatures.”

Le process de crypto_box n’est donc pas utilisable pour gmixer-rs (car il est nécessaire de pouvoir prouver que tel node a émis tel message pour pouvoir par exemple prouver au réseau qu’un node n’a pas respecté ses engagements et qu’il est donc probablement malhonnête).


Je projette de rajouter dans une prochaine release de dup-crypto-rs la fonction crypto_box de NaCl, ça pourrai permettre par exemple de créer un outils cli capable d’envoyer des messages Cesium+, je crois que ça intéressai @Frederic_Renault a un moment :wink:

2 Likes

Pour ma part, je trouve dommage qu’on ne puisse pas déchiffrer un message avec plusieurs clients.
Le problème de la signature n’en ai pas un, en fait : il suffit de signer le message, ou un hash du message, dans la trame qu’on envoi.
C’est ce que je fais dans Cesium+.
Voici un exemple de message :

{
        "issuer" : "DMwEdBiWuCGkPutfvGs7fAoyaqbiA3ZXeX5grcNsg5x8",
        "recipient" : "FbvRnCM8gQdDig614qR1y1QY7x7sUN2RzXr3a9D9Rzw4",
        "title" : "jBhFV2kDyUvDxmYtoWWO1D9ZpbX+j2vyOjVuMkgWa4vTyvM6VCqZWhutwYpMmXr1vdA=",
        "content" : "sX5/XOwcZe2RUIM2jd7Wlz9NoLTvgxPbNXZtNU8Qa4vT2qApB2rHFloNFdk+mHHo+NOfwk6RZjU3gnmUN0yi3eyUIOr2FYAoltdhx6C4Zmd9JQy5jVMzKg1HfD6K7daYbtN72ZTLfIxwlnAXG8z1+Rf9hZcmRNSIpFJ6lC2IUFcrFbulE8EsZuMPtrfuLlzNoW8HButmBlfbkMlALKcOcGYhVUCFhVAiL5FSgF+sHsUZe9CtubeGPlNT9m1y2joNZ8B4/rBn97XGV5odsaZZBO5gqcRQN4Y9SJSaNEbNFdaeWIFaO4NPT0r48eXKP4OGhYeQl4vCUQGG21U+GmcJiT4hiYJm41Xwp+qyjePlJ+om",
        "time" : 1504199549,
        "nonce" : "CWKAtBXqXu2ZuifenHRBePw15e36gw3v9",
        "hash" : "965E9C4693C0B63C6F4CC6924A93354F0CE2616F91BF0243FB5A355B4222D502",
        "signature" : "f0sPHFKukSbIahwkrYzPis9T5fP73QuH6UB76IdXN0JeWfg3Gh9A0oUc/YL78QmcKaM0FrD8JoK8BqYZNd1YAQ=="
      }

On y voit donc :

  • le nonce (pour répondre à ta question plus haut)
  • la pubkey ED25215 de l’émetteur et de récepteur
  • le hash du document (sérialisé par Json.stringify(), sans le hash ni la signature)
  • la signature, du hash, avec l’algo de signature ed25519

il est donc possible de vérifier l’authenticité du message sans l’ouvrir la box.

Bon après je suis pas un pro de la crypto. J’ai sans doute fait des erreurs…

@tuxmain générer de tels messages JSON, compatible Cs+, te semble il intéressant ?

2 Likes

Tu me donnes une idée : le nœud de sortie ĞMixer pourrait (en option) envoyer un message Cs+ au destinataire en même temps que la tx, préalablement chiffré par l’expéditeur anonyme (qui pourrait, selon le cas, signer avec sa vraie identité ou pas).

Ça permettrait d’envoyer des métadonnées telles qu’un identifiant de facture, puisque le commentaire de la tx est déjà utilisé pour le protocole ĞMixer. Donc oui, c’est intéressant ! :slight_smile:

1 Like

Bien sûr mais ça rajoute des opérations de crypto supplémentaires, c’est du gaspillage. Certes aujourd’hui on s’en fout vu la charge, mais pour le moyen.long terme il me tient à cœur de concevoir des process qui maximisent les performances (=qui minimise les calculs à faire). Or les traitements les plus coûteux sont les calculs cryptographiques :slight_smile:

En fait l’algo de signature ed25519 signe déjà le hash sha512 du message que tu lui donnes. Donc pré-hasher le message ne sert à rien :wink:

Merci pour le format, cela va me permettre de créer du code Rust capable de générer un tel document, par contre ce sera une option legacy juste pour Cesium+ car :

  • niveau perf c’est pas le top (cf remarques au dessus + l’utilisation de salsa20 au lieu de chacha20)
  • Les méta-données de ton format ne sont pas chiffrées, du coup on sait qui a écrit a qui, quand, quelle taille de message, ainsi que le titre du message (a moins qu’il soit chiffré, cf questions ci-dessous). Ce qui n’est pas satisfaisant en terme de confidentialité. La seule donnée qui nécessite d’être en clair c’est le destinataire du message, tous le reste pourrait (devrait) être chiffrée :slight_smile:

Questions sur ton format :

  • Le titre est en base64 mais est t’il chiffré ? Est ce directement la string end user encodée en base64 ?
  • Le nonce est sur combien d’octets ?
  • Si j’arrive à générer le bon format, quelle est la requête à faire pour envoyer le message a un pod cesium+ ?

Merci :slight_smile:

@kimamila ce document d’exemple est t’il valide ? J’ai essayé de vérifier la signature du message “965E9C4693C0B63C6F4CC6924A93354F0CE2616F91BF0243FB5A355B4222D502” avec la clé “DMwEdBiWuCGkPutfvGs7fAoyaqbiA3ZXeX5grcNsg5x8” mais la signature semble invalide.

La RFC 008 propose aussi un système de signature et de chiffrement compatible avec Cesium, pour signer et/ou chiffrer librement.

C’est implémenté dans DuniterPy et sera utilisé dans Sakia, en vue de promouvoir le système des certifications sécurisées proposé ici.

Je suis vos travaux avec attention et réviserai peut-être la RFC 008 si je vois que l’on a la signature implicitement incluse avec le chiffrement. C’est très intéressant ! :slightly_smiling_face:

2 Likes

@vit alors c’est plus subtil que ça et pourtant très important selon le cas d’usage. En effet signer une correspondance privée peut s’avérer dangereux pour l’utilisateur final :

Imaginons que j’écrive dans une correspondance privée des choses que je ne pourrais pas écrire publiquement pour X raisons, j’aurais de grave problèmes si j’écrivais cela publiquement.
Et bien la personne avec qui je communique peut me faire chanter en me menaçant de dévoiler publiquement ce que je lui est écrit.

C’est la raison pour laquelle la crypto_box de NaCl n’utilise pas la signature mais un autre procédé nommé “authentification par clé publique” que je nomme perso “authentification privée” (je trouve que c’est plus clair).
Le destinataire du message a toujours la preuve que le message proviens bien de l’expéditeur, mais pour vérifier cette preuve il a besoin de sa clé privée. Il ne peut donc pas prouver au monde extérieur que l’expéditeur lui a envoyé ce message.
Mieux encore, si le destinataire décide de dévoiler sa clé privée, cela ne permettra toujours pas de prouver au monde extérieur que c’est l’expéditeur qui lui a envoyé ce message, car la preuve a pu être générée par le destinataire directement.

Lorsque tu déchiffre le message avec NaCl, NaCl vérifie déjà la preuve d’authenticité, si elle est invalide NaCl retourne une erreur. Donc si tu récupère bien le message déchiffré tu est déjà certain que le message proviens bien de l’expéditeur, dans le cas d’'usage “correspondance privée entre humains”, ajouter un procédé de signature est inutile et dangereux.

Il serait préférable pour l’utilisateur final que Cesium+ ne signe pas les messages privés (qui sont déjà authentifiés par NaCl donc ça n’apporte rien à l’utilisateur a part le risque de chantage).

@kimamila @vit Je vous invite a méditer ce post de D. J. Bernstein, le mathématicien cryptologue qui a inventé l’algo Salsa20 et qui a participé a la conception du process crypto_box de NaCl :

Anyone can verify a public-key signature.

This is the whole point of public-key signatures. Verification isn’t
limited to the people who can create signatures.

This is, however, a useless feature for private email. Sometimes it’s
downright dangerous.

In contrast, with public-key authenticators, verification is limited to
the sender and the receiver. The receiver can’t convince anyone else
that the message was created by the sender; the receiver could have
computed the same authenticator.

What is a public-key authenticator? It’s a secret-key authenticator,
with a key derived from g^xy, where g^x and g^y are the public keys of
the sender and the receiver.

If you were already planning to encrypt the message, using another key
derived from g^xy, then you don’t have to do any extra public-key work.
A secret-key authenticator is easier to implement than a public-key
signature, and it takes less CPU time to compute.

4 Likes

Tout juste ! C’est pour ça que je tenais à les séparer. Et puis là, je ne sais pas pourquoi, je suis retombé dans le piège du confort “all inclusive”, à la lecture des possibilités des box.

La RFC 008 est donc sur la bonne voie, si on désire l’anonymisation. Cette authentification « authentification par clé publique » est vraiment maline !

Merci pour l’explication très claire !

2 Likes

Autant pour moi, il s’agissait d’un document JSON du début de Cs+ (sans la version: 2), et l’époque la signature était sur tout le document, ce qui était plus couteux, pour rien. Tu peux trouver des exemples valides ici : http://g1.data.duniter.fr/message/inbox/_search?pretty&q=version:2

La signature est utile car les pods eux aussi vérifient la signature des messages privés, afin d’éviter tout spam de documents invalides, et aussi pour permettre d’avoir leur propre règles de modération (sur la clef publique : quota, limitation aux abonnés, etc.), sans pour autant pouvoir lire le contenu du message. Impossible de faire cela sans la signature.

J’utilise aussi cela dans les pod gchange : la modération configuré par défaut empêche tout document invalide ET émis par une pubkey qui n’aurait pas de profile gchange.

voilou :slight_smile:

2 Likes

La fonction de lecture de DEWIF ne fournit pas la version, la monnaie, ni log_n, et la fonction d’écriture ne permet pas d’écrire plusieurs trousseaux. Est-ce voulu ou juste en travaux ?

Aussi, est-il prévu d’utiliser Schnorr un jour ? C’est un des formats que peut retourner la lecture d’un DEWIF mais il est vide et on ne peut l’écrire…

Et pourquoi as-tu supprimé la méthode Ed25519KeyPair::seed ? Cela fait qu’on ne peut plus du tout obtenir la seed proprement, ce qui peut être gênant.

Il y a aussi trois notations différentes utilisées pour la clé publique, Pubkey, PubKey et PublicKey. Dans le binding Python j’ai tout harmonisé en Pubkey, c’est le plus simple et naturel.