Proposition d'un modèle de portefeuilles "HD"

Je viens proposer une idée qui est pas mal présente dans d’autres crypto mais qui est absente de l’écosystème Duniter.

L’idée est d’utiliser des HD (Hierarchical Deterministic) wallets pour générer des adresses à la volée. Le principal cas d’utilisation est d’avoir une nouvelle adresse à chaque réception d’argent pour rendre plus difficile une analyse de la blockchain dans le but de savoir combien possède chaqu’un.

Ce système pourrait être entièrement compatible avec le modèle actuel et ne nécessiterai qu’une surcouche. Las clés dérivées de la master seed pourraient être utilisées pour générer le couple (secret id, password). Une autre possibilité est d’utiliser la seed comme secret id, et la clé dérivée comme password.

Ce système ne fonctionnerai cependant qu’avec les adresses “simple portefeuille”, vu que les comptes membres sont associés à une clé unique et non modifiable. Ce n’est pas vraiment un problème avec les transactions sans frais de Duniter : le compte membre co-créé le DU, et régulièrement on peut envoyer une certaine somme sur une adresse qui nous servira à faire les paiements. Dès la transaction suivante, il devient compliqué de dire vers qui va l’argent. Pour brouiller encore plus les pistes l’ordre des outputs pourraient être aléatoire, pour ne pas avoir le “reste” de son input toujours en dernière position. Il serait alors difficile de savoir à qui appartient les adresses de destination (une nouvelle adresse unique générée à chaque fois), mais en plus on ne pourrait pas distinguer l’adresse de destination et l’adresse où est envoyé le reste.

En conclusion, ce système permettrais d’augmenter de manière importante l’anonymité des transactions. De plus l’utilisateur n’aurais à retenir que la seed pour accéder à toutes ses adresses. Vu que cette seed devrait être cryptographiquement forte, on pourrait imaginer un système analogue à d’autres cryptomonnaies comme le wallet Coinomi, qui stocke la seed chiffrée avec un mot de passe sur l’appareil. Pour toute transaction, on demande à l’utilisateur de saisir son mot de passe pour déchiffrer la seed et regénérer les clés privés voulues. La méthode de dérivation des HD wallets permettent aussi d’avoir une seed “publique” permettant de générer toutes les clés publiques correspondantes. Cette seed pourrait être stockée en clair et permettrait de voir les montants sans entrer de mot de passe.

Voila le lien du BIP (Bitcoin Improvement Proposal) correspondant aux HD wallets.

Mon explication est peut-être un peu brouillonne, n’hésitez pas à me poser des questions ou à donner votre avis sur cette idée.

EDIT : Ca pourrait aussi être utile pour cette idée de @cgeek au niveau de la clé privée membre pour le calcul de blocs :
https://forum.duniter.org/t/idee-cle-deleguee-au-calcul-de-blocs/2698

2 J'aimes

Quelle différencie fonctionnelle y a-t-il avec un système utilisant scrypt où :

  • p est notre mot de passe
  • i est notre incrément, démarrant à zéro la valeur qu’on veut, par exemple un code PIN à 6 chiffres

Ainsi à partir d’un mot de passe, des millions de clés publiques peuvent être dérivées avec la fonction :

 seed = scrypt(i, p)

On en avait discuté il y a quelques temps ( BIP32 overview 0.1 )

Sur Sakia, le comportement original était de dériver un nombre N de clés à partir du salt/password… En ajoutant “-[N]” à la partie salt :slight_smile:

Mais il se trouve que les utilisateurs n’en avaient pas vraiment besoin dans un premier temps et ne comprenaient pas trop comment s’en servir. C’était trop tot, donc j’ai enlevé la fonction.

J’avais l’impression que les HD Wallet allaient plus loin, et s’appuyaient sur des mécanismes bien plus profond que simplement jouer avec les dérivations scrypt ?

1 J'aime

Oui, c’est certain, et justement j’aimerais savoir quel intérêt y trouve-t-on qu’on aurait pas avec scrypt.

D’où sors-tu la fonction scrypt avec ces paramètres ? script prend un salt et un password, plus des paramètres.

La différence est aussi de pouvoir créer un système hiérarchique.
Avec la seed maître je peux créer 2 clés qui seront elles-mêmes utilisées pour générer des clés.

On pourrait imaginer un portefeuille où je veuille faire 2 “poches” distinctes, et que le logiciel ne pioche pas dans l’autre “poche”. Et ceux avec la même seed.
Avec le système de dérivation, on peux par exemple publier la clé publique mère d’un ensemble de clés pour permettre de voir les sommes, sans exposer les clés du rang supérieur.

Un exemple déjà proposé en Bitcoin est la création de portefeuilles pro dans une entreprise. Celle-ci dérive des clés pour chaque employés et peut lui donner des fonds (avec du multisig ou pourquoi pas un smart contract). L’entreprise donne alors la paire de clés, l’employé peut à son tour dériver pour faire autant d’addresses qu’il veut, mais ne pourra pas remonter au dessus pour prendre le contrôle des adresses de l’entreprise ou de ses collègues. C’est un premier exemple, mais je suis sûr qu’il y a d’autres cas d’utilisations, et surement des biens meilleures.

Avec les HD wallets, on peut créer une hiérarchie de de paires de clés. Clés privés comme publiques peuvent être dérivées indépendamment de l’autre et donne des paires valides quand on utilise le même chemin de dérivation.

Comme dit précédemment, ça ne changerais absolument rien au niveau du protocole dans le cadre de simples portefeuilles.

Dans le cadre du minage, on peut imaginer diffuser la clé publique mère sur le réseau (clée publique du membre), et garder la clé privée mère pour soit. On pourrait alors “révoquer” une sous-clé privée compromise et en utiliser une autre, la clé publique mère pouvant être utilisée pour générer toutes les clés publiques correspondantes. Quand un bloc est miné, on pourrait imaginer la signature comme
CLE PUBLIQUE MERE + CHEMIN DERIVATION + SIGNATURE AVEC CLE PRIVEE DERIVEE

On a donc bien des clés privés/publiques jetables qui sont dérivées directement de la clé publique du membre. Si clé privée jetable est compromise, je signe un document avec ma clé privée mère (clé privée utilisée actuellement) comme quoi tel chemin de dérivation est compromis et ne dois plus être accepté.

On pourrait même reprendre ce système pour pouvoir utiliser son compte membre sur plusieurs appareils. La clé privée mère ne serait utilisée que sur une machine “sûre” et permettrait de révoquée les clés filles utilisées sur smartphone ou dans une interface web. Et encore une fois avec la clé publique mère et le chemin de dérivation, on peut récupérer la clé publique dérivée et vérifier que c’est bien la clé privée dérivée correspondante qui a signée.

Ce ne sont que quelques exemples d’utilisations parmi un très grand nombre de choses que l’on peut faire avec une hiérarchie de clés.

Mon explication doit être assez brouillonne car c’est quelque chose d’assez complexe, donc n’hésitez pas à poser des questions sur ce qui n’est pas clair pour vous.

Pfiou, ça fait du bien de finir son pavé.

1 J'aime

Pour la partie utilisation, il y a moyen de rendre ça très simple et ergonomique. A chaque fois que la personne veut recevoir de l’argent, une nouvelle clé est dérivée.
On affiche dans l’interface uniquement la somme des montants contenus dans ces adresses.

Quand il fait un paiement, le logiciel regarde les montants sur les adresses et peut en combiner plusieurs.
On pourrait faire comme avec le Bitcoin et pouvoir “payer sur le hash de la clé publique”, dans le sens où il ne faut “dévoiler” la clé publique qu’au moment du paiement, et seul le hash de cette clé est dans les conditions de dépense.
Il devient alors encore plus difficile de chercher la clé privée correspondant à telle adresse (en hash), même avec des ordinateurs quantiques. Dans ce cas là tous l’argent des adresses choisies sont utilisés, et le “reste” non envoyé au destinataire est envoyé sur une nouvelle adresse dérivée. Avec un certain nombre de transactions les fonds vont se déplacer vers de nouvelles adresses, rendant le temps d’exposition d’une adresse à du brute-force beaucoup plus court.

Je vois très bien l’intérêt fonctionnel, mais je ne comprends pas bien le fonctionnement.

Question très concrète (parce que là, j’ai du mal à te suivre) : dans la pratique, comment tu vérifies un chemin de dérivation ?

Soit Kpr la clé privée et Kpu la clé publique racine.

Comment tu crée des clées dérivées à partir de ces deux élements ? Et comment tu valides le chemin de validation ?

Un exemple serait la bienvenue en fait pour que tout s’éclaircisse :slight_smile:

3 J'aimes

C’est assez complexe, mais ça se base sur des opération sur courbes elliptiques. Le côté géométrique de ces opérations ont des propriétés permettant ce genre de choses. J’ai une idée de comment ça fonctionne, mais pas assez détaillée pour vous expliquer le fonctionnement complet. Je vais essayer de retrouver un article que j’avais lu qui expliquait comment ça marchait avec les opérations sur courbes.

Au niveau de la “vérification” : si je signe un message avec la clé privée de dérivation 1/4 (dérivation d’index 1, puis de nouveau dérivée d’index 4), la clé publique 1/4 permettra de vérifier la signature.

EDIT : Voila le lien qui propose des schémas et les calculs.

EDIT 2 : Autre utilisation que je viens de voir dans le lien ce dessus : un site commerçant peut générer une nouvelle adresse par client pour facilement identifier le paiement, et ce sans générer de clés privés. De ce fait le serveur web peut être compromis sans risquer de récupérer les clés privés et de voler l’argent. La clé mère est stockée en lieu sûr et peut être utilisée pour générer toutes les clés privées correspondantes au clés publiques proposées par le site.

On peut prendre les paramètres N,r,p que l’on veut, supposons 4096,16,1 par exemple. Pour la concision je donne donc juste les paramètres salt et password.

Ce que tu peux déjà tout à fait faire avec scrypt:

Moi, patron, génère ma clé maîtresse avec mon salt_secret1 et password_secret1, générant alors sk1 (secret key 1) et pk1 (public key 1).

De là je dérive n clés pour mes n employés, à l’aide de la fonction :

Pour i de 0 à n    
    dériveSousControleDuPatron(i,pk1,sk1)
        = scrypt(pk1 + i, sk1)
        = sk_e_i, pk_e_i

De là l’employé peut lui-même continuer à dériver une suite de clés en réutilisant cette fonction (avec ses propres paramètres sk_e_i et pk_e_i), et en respectant le même protocole de dérivation “pour i de 0 à n”.

Les HD wallets permettent de générer une hiérarchie de clés publiques à partir de pk1 sans connaitre sk1, et inversement pour une hiérarchie de clés privées; les 2 correspondant entre elles. Or ici scrypt a bien besoin d’avoir les deux clés d’une paire pour générer la paire fille, non ?

“Fonctionnement” plus formel :
Soit la clé m la clé maître privée, et M la clé maître publique associée.
Avec m tu peux générer m/i pour tout i, sans connaître M.
Avec M tu peux générer M/i pour tout i, sans connaître m.
Et ce de manière récursive.

Il y a un autre composant (le chain code) qui est utilisé en interne et qui peut permettre ou non de dériver un niveau de plus.

Je ne vois toujours pas en quoi le résultat final serait différent d’un point de vue fonctionnel : à la fin, on a une hiérarchie de trousseaux où un détenteur d’une sous partie ne peut pas remonter vers les trousseaux parents.

Je ne dis pas qu’il n’y a pas un intérêt supplémentaire à l’utilisation de scrypt que je ne vois pas, par contre il faudrait le révéler pour qu’on puisse comprendre :slight_smile:

Dans aucune de vos suggestions je ne vois de manière d’implémenter :

Soit m/i la clé dérivée de M
Comment vérifier, avec l’info de dérivation, que m est une clé dérivée de M ?

Je l’ai fait plusieurs fois :

Tu peux générer la partie publique de la hiérarchie sans connaître la clé privée, ce que l’on ne peut pas faire avec les exemples de code que tu me propose avec scrypt. Ce point là permet notamment de générer des clés publiques sur un site web de commerce sans qu’il ne stocke jamais une seule clé privée sur le serveur.

De même pour le minage avec clé dérivée (ou accès à compte membre par clés dérivées), avec la clé publique les utilisateurs pourront générer les clés publiques filles sans connaître la clé privée (heureusement), et du coup vérifier la signature produite par la clé privée dérivée utilisée par le membre. La clé privée dérivée est compromise ? On la révoque avec un nouveau type de document dans ce but, et on en utilise une nouvelle dérivée avec un autre index. Avec la clé publique mère, la signature et le chemin de dérivation, les utilisateurs pourront vérifier le message signé.

Le public ne connait que M et peux calculer M/0, M/1, M/1/0 etc, et du coup vérifier les signatures issues des clés privées m/0, m/1, m/1/0.

En plus de ça il y a la possibilité d’avoir des clés “renforcées” qui empêche certaines dérivations, noté par exemple M/0'. Cela permet d’empecher de remonter ou de decendre dans la hierarchie si on ne connait pas clé publique + chain code (appelée clé étendue). Tout ça est très bien expliqué dans ce lien que j’ai déjà donné au dessus. :wink:

Une clé dérivée de M (publique) se note M/i, m c’est la clé privée.
Mais si tu définie m comme étant M/0 par exemple, alors tu peux dériver M/0 à partir de M, ce qui ne sert pas à grande chose.
Ce qui est beaucoup plus intéressant et non faisable avec scrypt (il me semble) :

  • Dériver m/i à partir de m.
  • Dériver M/i à partir de M.
  • Signer un message avec m/i
  • Vérifier le message avec M/i

Et ce peu importe i, m et M tant que (m;M) est une paire de clés.

Le but ce n’est pas de prouver que A est dérivé de B (il suffit de dériver B pour le savoir), mais de pouvoir vérifier un message signé par a dérivé de b avec la clé A.

Dans ces cas là je ne parle pas des clés “renforcées” (m/i' et M/i') qui permettent justement de restreindre les dérivation pour rajouter des “barrages” en cas de fuites d’informations.

1 J'aime

Qu’est-ce qu’apporte le fait que a soit dérivé de b ?

Par exemple :

  • l’utilisateur Bob, génère une clé a pour Alice.
  • l’utilisateur Bob, dérive une clé a depuis sa clé b pour Alice

Qu’est-ce que ça change, fonctionnellement ? Est-ce que ça apporte quelque chose à un utilisateur Chris qui chercherait à échanger avec Alice ou Bob ?

Cette fonctionnalité m’intéresse fortement, notamment le fait de pouvoir générer a la volée une clé publique pour chaque client sans stocker de clé privée correspondante, c’est un réel besoin pour ceux qui souhaitent vendre des services en Ğ1 payables en ligne et de façon anonyme, hors c’est mon cas, et nul doute que ce sera le cas de beaucoup de monde à terme.

Ça me semble être une raison nécessaire et suffisante pour justifier d’implémenter les portefeuilles HD :slight_smile:

1 J'aime

Je ne comprend pas ton cas exposé qui ne correspond pas à ce que j’ai présenté.

C’est une des possibilités en effet, et c’est une des raisons pour laquelle je pense qu’il serait bien de le développer.
Dans ce cas là c’est simple pour le vendeur de voir que le client correspondant à bien payé, mais en plus chaque client ne peux pas voir directement qui à déjà acheté au vendeur, vu que dans ce cas là le vendeur ne vas pas donner la clé publique mère, ni le chemin de dérivation.


Le cas où le chemin de dérivation peut être utile c’est pour des clés privées dérivées pour les mineurs voir pourquoi pas pour les comptes membres avec un accès révocable sans révoquer le compte en lui même.


Ca me fait penser qu’au vu de la façon dont sont gérée les signatures, ça pourrait être des opérateurs en plus de ceux utilisant scrypt, sans avoir besoin de remplacer scrypt en lui même. C’est juste une autre solution, potentiellement avec un autre format d’adresse.

Révoquer une clé fille ou déclarer une nouvelle clé revient à la même chose (c’est une opération de changement de clé). Donc, on peut tout à fait se passer de ce mécanisme de dérivation, qui tout au plus fait gagner 1 coup sur N (le 1er, car on n’aurait pas besoin de déclarer la clé fille).

Si “avoir en mémoire le temps de la dérivation” tu appelles cela « stocker », alors en effet, mais dans ce cas je trouve l’intérêt très limité.

J’essaie de percevoir les différences entre “dériver à partir de” et “générer pour autrui”.

Et comment est déverrouillée la monnaie de ces clés publiques ensuite ?

Non … Vous n’avez pas besoin de la partie privée pour dériver la partie publique, et inversement. Les hierarchies publiques et privées peuvent être générées sans connaitre l’autre. Tant que (m,M) est une paire de clés valide, alors (m/1,M/1), (m/2,M/2), (m/2/3/18,M/2/3/18) le seront aussi.

Il n’y a pas de “générer pour autrui”. Tu dérive une clé à partir d’une autre, c’est tout. Si c’est une clé publique, tu peux la donner à autrui pour qu’il fasse un paiement dessus, auquel cas tu pourras t’en servir en utilisant la clé privée ayant le même chemin de dérivation.

Du coup je ne vois pas la différence avec des clés classiques ?

Ca me parait équivalent à :

Tu génères une clé publique pour autrui, tu pourras t’en servir en utilisant la clé privée qui correspond

Depuis m/1 ou M/1, peut-on retrouver m et M, ou du moins vérifier que (m/1,M/1) corresponde à (m/M) ?