Guia para construir una blockchain. Parte 4

En este post, continuamos y comenzamos la construcción de la blockchain de blancoin. En primer lugar, crearemos la interfaz para las funciones criptograficas que utiliza Blancoin, y a continuación desarrollaremos algunas de las funcionalidad de la blockchain.
Si está interesado principalmente en la teoría de las blockchains y las criptomonedas,
no dude en seguir leyendo. Aviso de que este post va a ser bastante largo por el material de codigo que se expone.

paquetes de encriptacion de python

La blockchain de Blancoin hace un uso extensivo de las funciones criptográficas, especialmente SHA-256, RIPEMD-160 y las firmas digitales. Estas funcionalidades están disponibles en el paquete pycryptodome. Entonces, instalemos este paquete. Asegurate de estar en la raíz de su entorno virtual de Blancoiny que el entorno virtual está activo. Hacer:

>(virtual) pip install pycryptodome

También necesitaremos algunos módulos auxiliares de Python. En primer lugar, usaremos el módulo re. Re está incluido en la distribución estándar de Python, por lo que no es necesario instalarlo. Es el analizador de expresiones regulares de Python. Este módulo examina cadenas para la presencia o ausencia de subcadenas especificadas y también puede realizar sustituciones de texto.
A continuación, debemos instalar el módulo Base58. Base58 convierte una cadena muy grande en una cadena alfanumérica restringida. El juego de caracteres alfanuméricos Base58 es:

123456789ABCDEFGHJKLMNPQRSTUVWXYzabcdefghijkmnopqrstuvwxyz

Una cadena codificada en Base58 solo incluye caracteres del conjunto anterior.

El juego de caracteres Base58 difiere del juego de caracteres alfanuméricos ordinarios, en
que excluye caracteres que pueden confundirse fácilmente. Los caracteres excluidos son:
(I mayúscula), l (L minúscula), O (o mayúscula) y 0 (cero) .
También usaremos el debugger pdb. pdb nos permite establecer puntos de interrupción en el código fuente, recorre nuestro código fuente línea por línea y examine los stack frame y los valores de variables. Pdb es parte de la distribución estándar de Python, por lo que no es necesario instalar. Siempre importaremos el módulo pdb en nuestros archivos de código fuente de Blancoin.

módulo rcrypt

El siguiente archivo de módulo, rcrypt.py contiene las funciones criptográficas que necesitaremos para el proyecto. Usaremos el paquete criptográfico pycryptodome. Copia el siguiente código rcrypt en un archivo llamado rcrypt.py y guarda este archivo en el subdirectorio crypt.
Ahora estamos listos para ver código del módulo de interfaz rcrypt poco a poco:

"""
The rcrypt module implements various cryptographic functions that are
required by
the Blancoin cryptocurrency application
This module requires the pycryptodome package to be installed.
The base58 package encodes strings into base58 format.
This module uses Python's regular expression module re.
This module uses the secrets module in the Python standard library to generate
cryptographically secure hexadecimal encoded strings.
"""


# import the regular expressions module
import re
# imports from the cryptodome package
from Crypto.Hash import SHA3_256
from Crypto.PublicKey import ECC
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
from Crypto.Hash import RIPEMD160
import base58
import secrets
# import Python's debugging and logging modules
import pdb
import logging
"""
 log debugging messages to the file debug.log
"""

El código arriba mostrado importa las librerias necesarias para implementar las funciones criptográficas que usaremos en nuestra criptomoneda.

logging.basicConfig(filename="debug.log",filemode="w", \
 format='%(asctime)s:%(levelname)s:%(message)s', level=logging.DEBUG)

Arriba aplicamos las opciones de debug.

def make_SHA256_hash(msg: 'string') -> 'string':
 message = bytes(msg, 'ascii')
 hash_object = SHA256.new()
 hash_object.update(message)
 return hash_object.hexdigest()

La función make_sha256_hash calcula el hash del mensaje SHA-256 para un argumento de cadena recibido. El hash que se genera es convertido en una secuencia de dígitos hexadecimales. El formato hexadecimal del hash del mensaje tiene una longitud de 64 bytes.

def validate_SHA256_hash(digest: "string") -> bool:
 if len(digest) != 64: return False
 if re.search('[^0-9a-fA-F]', digest) == None: return True
 return False


La función validate_SHA256_hash prueba si una cadena tiene una codificación conforme
a un hash del mensaje en formato de cadena hexadecimal (64 bytes).

def make_RIPEMD160_hash(message: 'byte stream') -> 'string':
 bstr = bytes(message, 'ascii')
 h = RIPEMD160.new()
 h.update(bstr)
 hash = h.hexdigest()
 return hash


RIPEMD-160 es un algoritmo criptográfico que emite un hash de mensaje de 20 bytes.
Esta función calcula el resumen de mensaje RIPEMD-160 de un mensaje y devuelve la representación codificada en cadena hexadecimal del hash del mensaje
(40 bytes).

def validate_RIPEMD160_hash(digest: 'string') -> 'bool':
 if len(digest) != 40: return False
 if re.search('[^0-9a-fA-F]+', digest) == None: return True
 return False

Comprueba que una cadena recibida tiene una codificación conforme a un hash RIPEMD-160 en formato hexadecimal.

def make_ecc_keys():
 ecc_key = ECC.generate(curve='P-256')
 pk_object = ecc_key.public_key()
 p = (ecc_key.export_key(format='PEM'), pk_object.export_key(format='PEM'))
 return p

La función make_ecc_keysun crea un par de claves asimétricas utilizando criptografía de curva elíptiva. Devuelve una tupla con la clave privada y la clave pública en formato PEM.

def sign_message(private_key: 'String', message: 'String') -> 'string':
 priv_key = ECC.import_key(private_key)
 bstr = bytes(message, 'ascii')
 hash = SHA256.new(bstr)
 signer = DSS.new(priv_key, 'fips-186-3')
 signature = signer.sign(hash)
 sig = signature.hex()
 return sig

Esta función firma digitalmente un mensaje usando una clave privada generada usando el
módulo de criptografía de curva elíptica del paquete pycryptodome. Recibe una clave privada en formato PEM y el mensaje que se va a
firmar. Devuelve una cadena de firma codificada en hexadecimal.

def verify_signature(public_key: 'String', msg: 'String', signature:
'string') -> 'bool':
 try:
  msg = bytes(msg, 'ascii')
  msg_hash = SHA256.new(msg)
  signature = bytes.fromhex(signature)
  pub_key = ECC.import_key(public_key)
  verifier = DSS.new(pub_key, 'fips-186-3')
  verifier.verify(msg_hash, signature)
  return True
 except Exception as err:
  logging.debug('verify_signature: exception: ' + str(err))

Esta función prueba si un mensaje está firmado digitalmente por una clave privada a la que corresponde la clave pública que se introduce como primer argumento en formato PEM. También recibe el mensaje y la firma digital del mensaje. Devuelve verdadero o falso.

def make_address(prefix: 'string') -> 'string':
 key = ECC.generate(curve='P-256')
 __private_key = key.export_key(format='PEM')
 public_key = key.public_key().export_key(format='PEM')
 val = make_SHA256_hash(public_key)
 val = make_RIPEMD160_hash(val)
 tmp = prefix + val
 checksum = make_SHA256_hash(tmp)
 checksum = checksum[len(checksum) - 4:]
 address = tmp + checksum
 address = base58.b58encode(address.encode())
 address = address.decode("ascii")
 return address

En esta función se genera una dirección de Blancoin a partir de una clave pública ECC en formato PEM. El prefijo es un carácter numérico único que describe el tipo de
la dirección. Este prefijo debe ser ‘1’.

def validate_address(address: 'string') -> bool:
 addr = address.encode("ascii")
 addr = base58.b58decode(addr)
 addr = addr.decode("ascii")
 if (len(addr) != 45): return False
 if (addr[0] != '1'): return False
 extracted_checksum = addr[len(addr) - 4:]
 tmp = addr[:len(addr)- 4]
 tmp = make_SHA256_hash(tmp)
 checksum = tmp[len(tmp) - 4:]
 if extracted_checksum == checksum: return True
 return False

validate_address valida una dirección de Blancoin utilizando la suma de comprobación de cuatro caracteres adjunta a la dirección. Recibe una dirección codificada en base58.

def make_uuid() -> 'string':
 id = secrets.token_hex(32)
 return id


Esta función crea un ID de 256 bits como una cadena hexadecimal para ser utilizado como identificador de transacción. Utiliza la biblioteca estándar de Python secrets para generar una cadena criptográfica fuerte de tipo aleatorio de 32 bytes codificada como cadena hexadecimal de 64 bytes.

Todo el código de nuestro módulo es éste:

"""
The rcrypt module implements various cryptographic functions that are
required by
the Blancoin cryptocurrency application
This module requires the pycryptodome package to be installed.
The base58 package encodes strings into base58 format.
This module uses Python's regular expression module re.
This module uses the secrets module in the Python standard library to generate
cryptographically secure hexadecimal encoded strings.
"""


# import the regular expressions module
import re
# imports from the cryptodome package
from Crypto.Hash import SHA3_256
from Crypto.PublicKey import ECC
from Crypto.PublicKey import DSA
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
from Crypto.Hash import RIPEMD160
import base58
import secrets
# import Python's debugging and logging modules
import pdb
import logging
"""
 log debugging messages to the file debug.log
"""
logging.basicConfig(filename="debug.log",filemode="w", \
 format='%(asctime)s:%(levelname)s:%(message)s', level=logging.DEBUG)

def make_SHA256_hash(msg: 'string') -> 'string':
 message = bytes(msg, 'ascii')
 hash_object = SHA256.new()
 hash_object.update(message)
 return hash_object.hexdigest()

def validate_SHA256_hash(digest: "string") -> bool:
 if len(digest) != 64: return False
 if re.search('[^0-9a-fA-F]', digest) == None: return True
 return False

def make_RIPEMD160_hash(message: 'byte stream') -> 'string':
 bstr = bytes(message, 'ascii')
 h = RIPEMD160.new()
 h.update(bstr)
 hash = h.hexdigest()
 return hash

def validate_RIPEMD160_hash(digest: 'string') -> 'bool':
 if len(digest) != 40: return False
 if re.search('[^0-9a-fA-F]+', digest) == None: return True
 return False

def make_ecc_keys():
 ecc_key = ECC.generate(curve='P-256')
 pk_object = ecc_key.public_key()
 p = (ecc_key.export_key(format='PEM'), pk_object.export_key(format='PEM'))
 return p

def sign_message(private_key: 'String', message: 'String') -> 'string':
 priv_key = ECC.import_key(private_key)
 bstr = bytes(message, 'ascii')
 hash = SHA256.new(bstr)
 signer = DSS.new(priv_key, 'fips-186-3')
 signature = signer.sign(hash)
 sig = signature.hex()
 return sig

def verify_signature(public_key: 'String', msg: 'String', signature:
'string') -> 'bool':
 try:
  msg = bytes(msg, 'ascii')
  msg_hash = SHA256.new(msg)
  signature = bytes.fromhex(signature)
  pub_key = ECC.import_key(public_key)
  verifier = DSS.new(pub_key, 'fips-186-3')
  verifier.verify(msg_hash, signature)
  return True
 except Exception as err:
  logging.debug('verify_signature: exception: ' + str(err))

def make_address(prefix: 'string') -> 'string':
 key = ECC.generate(curve='P-256')
 __private_key = key.export_key(format='PEM')
 public_key = key.public_key().export_key(format='PEM')
 val = make_SHA256_hash(public_key)
 val = make_RIPEMD160_hash(val)
 tmp = prefix + val
 checksum = make_SHA256_hash(tmp)
 checksum = checksum[len(checksum) - 4:]
 address = tmp + checksum
 address = base58.b58encode(address.encode())
 address = address.decode("ascii")
 return address

def validate_address(address: 'string') -> bool:
 addr = address.encode("ascii")
 addr = base58.b58decode(addr)
 addr = addr.decode("ascii")
 if (len(addr) != 45): return False
 if (addr[0] != '1'): return False
 extracted_checksum = addr[len(addr) - 4:]
 tmp = addr[:len(addr)- 4]
 tmp = make_SHA256_hash(tmp)
 checksum = tmp[len(tmp) - 4:]
 if extracted_checksum == checksum: return True
 return False

def make_uuid() -> 'string':
 id = secrets.token_hex(32)
 return id

Para el próximo post continuaremos con el código.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s