[PYTHON] Comprendre le protocole de crypto-monnaie (Bitcoin, Monacoin): clés publiques et privées

Aperçu

Ces dernières années, les crypto-monnaies telles que Bitcoin et Litecoin sont devenues un sujet brûlant (pour le meilleur ou pour le pire). La crypto-monnaie est actuellement extrêmement chaotique, et des centaines de devises dérivées (devises Alt) ont été encombrées jusqu'à présent, mais ici au Japon également, une devise dérivée du Japon appelée "Monacoin" est récemment née, et elle est secrète dans le coin de 2ch. Montre de l'excitation. Cet auteur est celui qui exécute la version dite de crypto-monnaie du service Web de gumroad appelé "Monapay".

Auparavant, sur un coup de tête, je proposais le type de jeu suivant et le collais sur 2ch.

Je voudrais jouer à un jeu avec un prix pour la publicité + tuer le temps. Le contenu est "Voler mon monacoin"

M9WJPA8npJQEwcxXwvzKHwWz5msQfEKzf5 Je viens de déposer 10MONA à cette adresse. Vous pouvez vérifier avec Abe si le dépôt a bien été effectué. Et voici la clé privée brute encodée en Base64. 0fHys0+Iy89GnEUfA0ZCJ652Tf8409Yor4ekLdazlXE= Il y a longtemps, quand j'ai copié une carte cadeau Bitcoin à la télévision, elle a été volée par Sokko. La même chose se passe. En exposant la clé privée sur Internet, n'importe qui peut faire déposer le 10MONA à cette adresse.

Le jeu est super facile. Veuillez utiliser cette clé privée dès que possible pour voler 10MONA à cette adresse. Pouvez-vous vraiment être un hacker? ?? Faites ce que le modérateur de la télévision a fait! (S'il s'agit d'un téléviseur, il est volé à la hâte, mais s'il n'est pas volé cette fois, 10 MONA seront ajoutés chaque jour)

10MONA fait référence à la devise Monacoin, qui est une crypto-monnaie. La simple raison de proposer ce «problème de vol» est que je me demandais combien de personnes peuvent réellement résoudre ce problème. Je pensais que cela prendrait plus d'une journée comme prévu, mais le résultat réel est apparu pour la première fois 2 heures après le collage, et Enfin, 3 personnes au total ont réussi à le capturer. Je l'ai fait. 10MONA était bon marché à environ 5,60 yens une fois converti au yen japonais actuel, mais j'étais heureux qu'il y ait des gens qui ont contesté et résolu ce problème (merci à ceux qui ont coopéré). Il y a).

Afin de résoudre ce problème, il est nécessaire de convertir la clé privée dans un format appelé Wallet Import Format et de la faire lire par le client Monacoin, mais pour cela, non seulement des connaissances sur les crypto-monnaies mais aussi [code source Monacoin] Vous devez lire (https://github.com/monacoinproject/monacoin). Le but de cet article est de fournir un commentaire sur ce problème et en même temps d'approfondir vos connaissances sur les protocoles Bitcoin et Monacoin. Actuellement, il n'y a presque pas d'articles techniques japonais sur les crypto-monnaies, donc je pense que cela sera utile dans une certaine mesure.

En outre, cet article comprend également du code Python pour l'explication. De plus, à la fin, un autre jeu de vol est affiché comme exercice. Le prix est petit, mais je n'ai pas beaucoup d'argent en main, alors pardonnez-moi.

Les dons sont toujours acceptés :) MKrJpLy8Dg8mATKdH5zWCYiUVYQGvVW5Sx

Signature électronique

Comme vous le savez, de nombreuses crypto-monnaies, y compris Bitcoin et Monacoin, ont tout l'historique des transactions (Transactions) dans le "[Blockchain]( Il se compose d'une base de données distribuée constituée de "https://en.bitcoin.it/wiki/Block_chain)". L'utilisateur effectue une transaction réelle en écrivant le contenu de la transaction qu'il souhaite exécuter dans cette énorme base de données. En d'autres termes, le versement en crypto-monnaie équivaut à écrire une transaction avec le contenu de «envoyer le montant d'argent de votre compte (A) à B» vers la blockchain.

Le problème qui se pose ici est qu'il est nécessaire de prouver que "La personne qui a ordonné le transfert depuis le compte de A est-elle vraiment A elle-même?" Pour le prouver, Monacoin utilise un algorithme de chiffrement à clé publique pour vérifier que l'utilisateur qui a dirigé l'écriture de la transaction est bien le propriétaire du compte. Regardons de plus près:

relationship.png

Le chiffrement à clé publique se compose de deux paires de clés: * clé privée * et * clé publique *. Dans le cas de Monacoin, la clé privée est une séquence de 256 bits et la clé publique est une séquence de 512 bits. La clé privée permet de prouver que la personne qui a dirigé l'écriture de la transaction est A. Pensez à quelque chose comme un sceau ou une signature dans le monde réel. En signant la transaction avec une clé privée, vous pouvez prouver que la personne qui a dirigé l'écriture de la transaction est A. Ces informations ne doivent jamais être divulguées à l'extérieur, car n'importe qui peut usurper A et lui demander de transférer de l'argent du compte de A si la clé privée est divulguée en ligne.

La clé privée est généralement exprimée en hexadécimal, mais elle est un peu redondante à transporter car elle comporte 64 caractères en l'état. Vous voudrez également qu'une somme de contrôle indique s'il s'agit vraiment d'une chaîne représentant la clé privée de Monacoin. Par conséquent, Monacoin fournit également un format appelé * Wallet Import Format (WIF) *, qui est une représentation plus concise de la clé privée. Le mot «clé privée» dans Monacoin fait généralement référence à WIF plutôt qu'à la clé privée brute. Étant donné que la clé privée brute et WIF représentent les mêmes informations, vous pouvez obtenir la clé privée brute à partir de WIF et vice versa.

Ensuite, passons à l'explication de la clé publique. La clé publique est l'information publique saisie dans la blockchain et est utilisée pour vérifier la signature avec la clé privée. Tous les utilisateurs connectés au réseau Monacoin vérifient que l'auteur de la transaction est A en comparant cette clé publique avec la signature électronique. Notez que vous pouvez générer une clé publique à partir d'une clé privée, mais pas l'inverse.

Comme la taille de la clé publique est de 512 bits, elle est un peu trop grande pour la coller sur un blog ou un babillard tel quel. Pour résoudre ce problème, Monacoin propose une représentation de clé publique plus courte. Il s'agit de "l'adresse de portefeuille Monacoin" communément connue. Notez que s'il est facile de générer une adresse de portefeuille à partir d'une clé publique, vous ne pouvez pas générer de clé publique à partir d'une adresse de portefeuille.

Méthode de conversion

Voyons maintenant comment générer une clé privée et publique spécifique. La génération d'une adresse Monacoin prend trois étapes principales: (1) Génération d'une clé privée (2) Génération d'une clé publique à partir d'une clé privée (3) Génération d'une adresse à partir d'une clé publique. En outre, les opérations mentionnées dans la figure ci-dessus (A) Générer WIF à partir de la clé privée (B) Générer la clé privée à partir de WIF sont également importantes, nous allons donc vous expliquer comment procéder.

(1) Générer une clé privée

Il n'y a pas de règle particulière pour que ce soit une clé privée, et n'importe quel 256 bits fera l'affaire. En général, de vrais nombres aléatoires obtenus à partir de randomiseurs tels que / dev / random / sont utilisés. Pour des raisons de sécurité, il est souhaitable d'éviter l'utilisation de nombres pseudo-aléatoires qui ne sont pas cryptographiquement sécurisés, comme Mercenne Twister.

def make_private_key():
    return os.urandom(32)  # 32 = 256/8

Le résultat de l'exécution est le suivant:

>>> private_key = make_private_key()
>>> print(private_key.encode("hex_codec"))
3954e0c9a3ce58a8dca793e214232e569ff0cb9da79689ca56d0af614227d540

(2) Générer une clé publique à partir de la clé privée

Monacoin utilise un algorithme appelé * [Elliptical Curve DSA](http://ja.wikipedia.org/wiki/Elliptical Curve DSA) * pour l'algorithme de signature électronique (le paramètre est Secp256k1). Il est difficile de comprendre le mécanisme détaillé, mais si vous voulez simplement l'utiliser, vous n'avez qu'à accéder à une bibliothèque standard telle qu'OpenSSL, il est donc préférable de l'utiliser.

Cette fois, nous utiliserons une bibliothèque Python appelée [ʻecdsa](https://pypi.python.org/pypi/ecdsa). Je pense que c'est facile à installer en utilisant pip`.

$ pip install ecdsa

Le code Python pour générer une clé publique à partir d'une clé privée ressemble à ceci:

def private_to_public_key(private_key):
    signing_key = ecdsa.SigningKey.from_string(
        private_key, curve=ecdsa.SECP256k1)
    verifying_key = signing_key.verifying_key
    return verifying_key.to_string()

Le résultat de l'exécution est le suivant:

>>> public_key = private_to_public_key(private_key)
>>> print(public_key.encode("hex_codec"))
47f272a8dad703f809489dfc9ea3606e206ba6a16ecbde314186a03b68326284eaecd034af5300bb6991ac5897c8163ed67894205bc1b7dd5dac8080dba2fe69

(3) Générer une adresse à partir de la clé publique

Ce processus est un peu plus compliqué qu'avant, mais ce n'est pas difficile, alors décomposons-le un par un. Tout d'abord, ajoutez \ x04 au début de la clé publique donnée (pour des raisons inconnues). Veuillez noter que ce préfixe n'est pas une valeur unique à Monacoin, mais une valeur commune à Bitcoin et Litecoin.

pk_with_prefix = "\x04" + public_key

Ensuite, appliquez deux fonctions unidirectionnelles «sha256» et «ripemd160» à cette clé publique pour la convertir en un hachage de 160 bits.

ripemd160 = hashlib.new('ripemd160')
ripemd160.update(hashlib.sha256(pk_with_prefix).digest())
hash160 = ripemd160.digest()

Puis préfixez ce hachage avec \ x32. Cela permettra à l'adresse de portefeuille de Monacoin de commencer par «M», ce qui facilitera l'identification du type d'adresse de portefeuille.

vhash160 = "\x32" + hash160  # monacoin addresses start with M

La valeur du préfixe dépend de la crypto-monnaie. Par exemple, Bitcoin prend la valeur «\ x00» et Litecoin prend la valeur «\ x30». Si vous voulez connaître les préfixes d'autres devises dérivées, veuillez vous référer à PUBKEY_ADDRESS dans src / base58.h. Par exemple, pour Monacoin, le code source de src / base58.h ressemble à ceci:

src/base58.h


class CBitcoinAddress : public CBase58Data
{
public:
    enum
    {
        PUBKEY_ADDRESS = 50, // Monacoin addresses start with M or N
        SCRIPT_ADDRESS = 5,
        PUBKEY_ADDRESS_TEST = 111,
        SCRIPT_ADDRESS_TEST = 196,
    };
...

Le code source dit «commencez par M ou N», mais par Liste des préfixes d'adresses, «\ x32» pointe toujours vers «M». , Le code source est probablement incorrect.

Ensuite, après le hachage du bit (8 + 160) avec ce \ x32, ajoutez une somme de contrôle utilisée pour vérifier l'adresse. De nombreuses crypto-monnaies traitent la valeur obtenue en découpant 32 bits à partir du début d'une chaîne numérique à laquelle la fonction de hachage «sha256» est appliquée deux fois comme somme de contrôle.

def _make_checksum_for_address(data):
    code = hashlib.sha256(hashlib.sha256(data).digest()).digest()
    return code[:4]

Enfin, la valeur du hachage (8 + 160) bits avec une somme de contrôle ajoutée est Base58 (à l'exclusion des caractères confus tels que 0, O). Il est codé dans un format de codage qui exprime des données avec 58 types de caractères.

checksum = _make_checksum_for_address(vhash160)
raw_address = vhash160 + checksum
return base58_encode(raw_address)

Cela fait longtemps, mais le code Python final ressemble à ceci:

def _make_checksum_for_address(data):
    code = hashlib.sha256(hashlib.sha256(data).digest()).digest()
    return code[:4]

def public_key_to_address(public_key):
    pk_with_prefix = "\x04" + public_key
    ripemd160 = hashlib.new('ripemd160')
    ripemd160.update(hashlib.sha256(pk_with_prefix).digest())
    hash160 = ripemd160.digest()
    vhash160 = "\x32" + hash160  # monacoin addresses start with M
    checksum = _make_checksum_for_address(vhash160)
    raw_address = vhash160 + checksum
    return base58_encode(raw_address)

Le résultat de l'exécution est le suivant:

>>> public_key_to_address(public_key)
MB3D45ngvaWRcACUmAFUf6fzcdXR8bVM6k

(A) Générer WIF à partir de la clé privée

Ce qui suit décrit comment générer un WIF à partir d'une clé privée. Suivez les étapes ci-dessous pour générer un WIF. Tout d'abord, Monacoin préfixe la clé privée avec \ xb2. La valeur du préfixe dépend de la crypto-monnaie. Par exemple, Bitcoin prend la valeur \ x80 et Litecoin prend la valeur \ xb0.

La valeur spécifique du préfixe dépend de PUBKEY_ADDRESS dans src / base58.h. Plus précisément, la valeur de PUBKEY_ADDRESS plus 128 est la valeur du préfixe PRIVKEY_ADDRESS:

src/base58.h


class CBitcoinSecret : public CBase58Data
{
public:
    enum
    {
        PRIVKEY_ADDRESS = CBitcoinAddress::PUBKEY_ADDRESS + 128,
        PRIVKEY_ADDRESS_TEST = CBitcoinAddress::PUBKEY_ADDRESS_TEST + 128,
    };
...

Ensuite, après la valeur de la clé privée avec ce préfixe, ajoutez une somme de contrôle pour vérifier la clé privée. De nombreuses crypto-monnaies traitent la valeur obtenue en découpant 32 bits à partir du début d'une chaîne numérique à laquelle la fonction de hachage «sha256» est appliquée deux fois comme somme de contrôle.

def _make_checksum_for_wif(code):
    return hashlib.sha256(
        hashlib.sha256(code).digest()).digest()[:4]

Enfin, la valeur avec la somme de contrôle est encodée en Base58. Le code Python final ressemble à ceci:

def _make_checksum_for_wif(code):
    return hashlib.sha256(
        hashlib.sha256(code).digest()).digest()[:4]


def private_key_to_wif(private_key):
    assert(len(private_key) == 32)
    checksum = _make_checksum_for_wif("\xb2" + private_key)
    return base58_encode("\xb2" + private_key + checksum)

Le résultat de l'exécution est le suivant:

>>> private_key_to_wif(private_key)
6ySkrpLpwm6gKsWo2aS6EL1SZxidZNdJkKqsKRNjXzv9WSrpHjR

(B) Générer une clé privée à partir de WIF

De même, pour que WIF génère une clé privée, inversez simplement les étapes ci-dessus. Il est fastidieux d'expliquer à nouveau cette étape, il suffit donc de regarder le code Python ci-dessous:

def wif_to_private_key(wif):
    code = base58_decode(wif)
    prefix, private_key, checksum = code[0], code[1:33], code[33:]
    checksum_from_hex = _make_checksum_for_wif(prefix + private_key)
    assert(checksum == checksum_from_hex)
    return private_key

Des exercices

Avec l'explication jusqu'à présent, il est désormais possible de convertir la clé privée au format WIF. Il ne vous reste plus qu'à utiliser ce format WIF pour obtenir la clé privée dans le client Monacoin. Veuillez vous référer à la méthode de Bitcoin wiki pour la méthode d'importation spécifique. Assurez-vous de faire une sauvegarde de wallet.dat.

Enfin, voici un bref exercice de retour sur le passé. La première personne à le résoudre a le privilège de voler le prix :)

Adresse du portefeuille Monacoin MPKWCip9CLawFKwwMBDTTp2ZWJcrkDf7rX Clé privée divulguée par un virus informatique 9a833fd68f30459bd6f7b718818668c5c6d20b5a2b491558cea08be63937f847 S'est avéré être. Utilisez cette clé privée pour voler le montant total stocké à l'adresse ci-dessus.

Recommended Posts

Comprendre le protocole de crypto-monnaie (Bitcoin, Monacoin): clés publiques et privées
Créer une clé privée et une clé publique à l'aide de ssh-keygen (différence entre Windows 10 et Linux)