Verify blocks signatures with Silkaj/DuniterPy

Sous-section de :

À lancer avec apython, et aioconsole d’installé, pour avoir de l’asynchrone en console :

from duniterpy.api import bma
from duniterpy.api.client import Client
from duniterpy.documents import Block
from duniterpy.key.verifying_key import VerifyingKey

client = Client("BMAS g1.duniter.org 443")
received_block = await client(bma.blockchain.block, 15144)
block = Block.from_signed_raw(received_block["raw"] + received_block["signature"] + "\n")
key = VerifyingKey(block.issuer)
key.verify_document(block)
False                            

Signature du bloc 15144 invalide. Pareil avec la “bonne” signature :

good_signature = "aZusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQ/3+VMaDJ02I795GBBaLgjypZFEKYlPMssJMn/X+F/pxgAw=="
block = Block.from_signed_raw(received_block["raw"] + good_signature + "\n")

Également invalide pour les blocs suivant 1514{5,6} :

received_block = await client(bma.blockchain.block, 15145)
block = Block.from_signed_raw(received_block["raw"] + received_block["signature"] + "\n")
key = VerifyingKey(block.issuer)
key.verify_document(block)
False
ValueError: Failed to validate message

Je sais pas quel est le problème côté DuniterPy. Le code utilisé :

@Moul il n’y a pas de saut de ligne final après la signature :wink:

C’est comme ça qu’est implémenté le parsing de bloc dans DuniterPy. Par exemple dans Sakia :

grep -rn "Block\.from_signed_raw"
src/sakia/data/processors/blockchain.py:202:            block_doc = Block.from_signed_raw("{0}{1}\n".format(block['raw'], block['signature']))
src/sakia/services/transactions.py:136:                        block = Block.from_signed_raw(block_data["raw"] + block_data["signature"] + "\n")

Sinon, ça fait une erreur de ce genre :

>>> block = Block.from_signed_raw(received_block["raw"] + "aZusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQ/3+VMaDJ02I795GBBaLgjypZFEKYlPMssJ
Mn/X+F/pxgAw==")                                                                                                                     
Traceback (most recent call last):
  File "/home/moul/duniterpy/duniterpy/documents/document.py", line 68, in parse_field
    raise AttributeError
AttributeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/moul/.local/lib/python3.7/site-packages/aioconsole/execute.py", line 95, in aexec
    result, new_local = yield from coro
  File "<aexec>", line 2, in __corofn
  File "/home/moul/duniterpy/duniterpy/documents/block.py", line 405, in from_signed_raw                                             
    signature = Block.parse_field("Signature", lines[n])                                                                             
  File "/home/moul/duniterpy/duniterpy/documents/document.py", line 71, in parse_field                                               
    raise MalformedDocumentError(field_name)                                                                                         
duniterpy.documents.document.MalformedDocumentError: Could not parse field Signature                                                 

Il y a un ticket à ce sujet.

Arf c’est peut-être l’occasion de découvrir dans la foulée un bug dans duniterpy :sweat_smile:

Possible :slight_smile:
Il est aussi possible que je ne l’utilise pas correctement.
Autrement, ça ne m’étonnerait pas de découvrir à nouveau des bugs. J’en ai trouvé pas mal depuis que je l’utilise.

Bon, c’est utilisé à un seul endroit dans Sakia :

    def handle_new_node(self, peer):
        key = VerifyingKey(peer.pubkey)
        if key.verify_document(peer):

Du coup, il me semble que je l’utilise correctement par rapport à ce code.

@Moul @vit je viens de vérifier le code et duniterpy ne sais vérifier que les documents utilisateurs + la fiche de peer, il ne sais pas vérifier le document bloc car ce dernier a une particularité : c’est le seul document dont on ne signe pas le format raw mais la partie InnerHash: BC.FE\nNonce: 012..348\n (le format raw sert justement a obtenir le inner hash)

Pour tester coté Duniterpy sans avoir de dev a faire, il vous suffit de rédiger un TU qui appelle directement la méthode verify de l’objet libnacl.sign.Verifier

1 J'aime

J’ai remplacé cette ligne par :

#prepended = signature + bytes(document.raw(), "ascii")
prepended = signature + bytes("InnerHash: {0}\nNonce: {1}\n".format(document.inner_hash, document.nonce), "ascii")

et je n’y arrive toujours pas avec la bonne signature ainsi qu’avec un autre bloc.

1 J'aime

@Moul forcément ça ne peut pas fonctionner tu a rajouté un espace devant Nonce, j’ai pourtant pris soin de ne pas insérer cet espace dans mes précédents post :wink:

2 J'aimes

Ok, top ! Merci pour la typo.
Du coup le bloc #15144 a une signature valide avec la bonne signature que t’as donné plus haut et a une signature invalide avec la signature présente dans le bloc.
Et, sur le bloc #15145, ça passe nickel !

3 J'aimes

Bon ben @moul a été plus rapide que moi ! :blush:

Tout est bien qui finit bien pour Duniterpy, ça me rassure.

[EDIT]
Un ticket vient d’être créé :

3 J'aimes

De même pour ce post : discussion rendue publique pour raison d’archivage.
La commande verify de Silkaj permet de vérifier la validité des blocs.