As criptomoedas, como o Bitcoin, utilizam-se de tecnologias criptográficas como criptografia de chave publica,e funções de Hash. Neste notebook vamos nos familiarizar com estes conceitos que nos serão úteis em nosso estudo da bitcoin e outras criptomoedas.
As funções de Hash criptográfico são o componentes mais fundamental da maioria das blockchains pois é a "cola" que garante a coesão, correção, imutabilidade e outras características fundamentais das blockchains.
Uma função de Hash é uma função que apresenta algumas características básicas:
A biblioteca padrão do Python nos oferece uma biblioteca com implementações das principais funções de hash, a Hashlib.
import hashlib
hashlib.algorithms_available
{'blake2b', 'blake2s', 'md4', 'md5', 'md5-sha1', 'ripemd160', 'sha1', 'sha224', 'sha256', 'sha384', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'sha512', 'sha512_224', 'sha512_256', 'shake_128', 'shake_256', 'sm3', 'whirlpool'}
A Bitcoin se utiliza de curvas elípticas para suas necessidades criptográficas. Mais precisamente, utiliza o algoritmo de assinatura digital por curvas elipticas (ECDSA). A ECDSA envolve três componentes principais: uma chave pública, uma chave privada e assinatura.
A Bitcoin usa uma curva elíptica específica chamada secp256k1. A função em si parece inofensiva: $$y^2=x^3+7$$ onde $4a^3 +27b^2 \neq 0$ (para excluir curvas singulares. $$\begin{array}{rcl} \left\{(x, y) \in \mathbb{R}^2 \right. & \left. | \right. & \left. y^2 = x^3 + ax + b, \right. \\ & & \left. 4a^3 + 27b^2 \ne 0\right\}\ \cup\ \left\{0\right\} \end{array}$$
Porém, em aplicações criptográficas, esta função não é definida sobre os números reais, mas sobre um campo de números primos: mais precisamente ${\cal Z}$ modulo $2^{256} - 2^{32} - 977$.
\begin{array}{rcl} \left\{(x, y) \in (\mathbb{F}_p)^2 \right. & \left. | \right. & \left. y^2 \equiv x^3 + ax + b \pmod{p}, \right. \\ & & \left. 4a^3 + 27b^2 \not\equiv 0 \pmod{p}\right\}\ \cup\ \left\{0\right\} \end{array}Para um maior aprofundamento sobre a utilização de curvas elítpicas em criptografia leia este material.
A forma mais simples de criptografia é a criptografia simétrica, na qual se utilizando de uma chave gerada aleatóriamente, converte um texto puro em um texto encriptado. então de posse da mesma chave é possível inverter a operação, recuperando o texto original. Quando falamos em texto aqui estamos falando apenas de uma aplicação possível de criptografia. Na verdade o que será aplicado aqui para textos, pode ser aplicado para qualquer sequencia de bytes, ou seja para qualquer objeto digital.
from Crypto.Cipher import DES3
from Crypto import Random
Neste exemplo vamos usar o algoritmo conhecido como "triplo DES" para encriptar e desencriptar um texto. Para este exemplo a chave deve ter um comprimento múltiplo de 8 bytes.
chave = b"chave secreta um"
sal = Random.get_random_bytes(8)
des3 = DES3.new(chave, DES3.MODE_CFB, sal)
Note que adicionamos sal à ao nosso encriptador. o "sal" é uma sequência aleatória de bytes feitar para dificultar ataques.
texto = b"Este e um texto super secreto que precisa ser protegido a qualquer custo de olhares nao autorizados."
enc = des3.encrypt(texto)
enc
b'\xbd\'W\xd7\x1b&\x95\xdd\xa2\x1b 5t\xb7\xfd\x93/Fa\xa4\xc3\x9c"\xcf\x87\xdc\x03\x00\xe5 \x18\xd6)\x0f\xb7\xd6\r\x07\x80PK\x895V\x084?\xd1.cS\'\xec\x02}\xa7j\xbf\x1f\n\x06EA\'s\xf0C\xd0T\x95\xa2]\x10\x19}\xc9\xa5\xdf\xd5\x8b\xa0\xd7\xdd\tlRL\xe1\x0eb\x06\xc4\xd1\x1a\xbckT9\x12l'
des3 = DES3.new(chave, DES3.MODE_CFB, sal)
des3.decrypt(enc)
b'Este e um texto super secreto que precisa ser protegido a qualquer custo de olhares nao autorizados.'
Um dos problemas com esta metodologia de encriptação, é que se você deseja enviar este arquivo encriptado a um amigo, terá que encontrar uma forma segura de lhe transmitir a chave, caso contrário um inimigo mal intencionado poderá desencriptar sua mensagem de posse da chave. Para resolver este problema introduzimos um novo métodos de encriptação:
Nesta metodologia temos duas chaves: uma pública e outra privada.
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
Vamos criar uma chave privada, e também encriptá-la, no caso de termos que mantê-la em algum lugar onde possa ser observada por um terceiro.
senha = "minha senha super secreta."
key = RSA.generate(2048) # Chave privada
print(key.exportKey())
chave_privada_encryptada = key.exportKey(passphrase=senha, pkcs=8, protection="scryptAndAES128-CBC")
b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAxBCxxYLcfPm9AQyvE+58d3ZYtKVZ/o1tbzKOIHFvwDXO6RDS\n8lej3Tt7T4zgMKNNwfLW64IMgWpobgwBpq6mQIFUWoKSE5SDrlPWvbrwe1Kt6ESD\nCsb64DhnXuaqz0rVyAqdRbBHQ4SVcYXOMvubTC5Ff5MgRPNBWiFegc5VRLjk1HEE\nCUiNG0cwrXsMHhyunA1Fa/c5EgQ2kTa8CKoa5vf0ptcSmHa+WaH665BjSdeM9VG7\niMmDu2fASnIoHQ9Kr6KCd545n2d4vuGN9XDqp+Togb2YOUo/PEoFMSOXkuj0HtWB\nMK18+8xc7H+HIubUYg81T2wZbhXc1gOmzZD37wIDAQABAoIBAFUyxfVEgMiEA2gV\n2WyJWSfWUwSox7sQPOoxp0Yc1QlKuI9ZorjxcYD8zIBMgM1R4UOy4Ua0m/eOxDNx\n3zPNt+vW509vZsfAZRpXTzziI4cLbgu83c7MmY7eo7i+9qGebNiBGEeEqusBjakn\nkmtgH2NSxhuCVObxZ8ghMP6qKS5zgI5lEES2Kn3XRwdxVI/m9L6AW8dMnrGpfGT7\n2rbVtXliOwhZIudT5nvq8rULYLZ3yn8exKyuHs1SR6+15B2aFHzx7zLrDWqzfd7n\nhQEapm9O43VLvkOQTO9MBWGnNSSr5dZrqtXohJtg3ZAsszlUHDFJbAVAC4ASDc4Z\nxcvSYT0CgYEAzpB9mEqEOXVcSO9ejjD3s07TSOAygQOifcLpt5i7yHXVDQWzmJI2\nEsYGrTgmdNJ8HSCCrnRmWB0IdjztMNW5pqUEJMidIy6f3VfkL0AFBTqn2gwziqyg\nANMYCSrE94AQZswsglYCzXo3RJnUX5Mb3auHl8jMhgWUW2hfZpHMITsCgYEA8vzz\n6fAoqtNfrlzCVZSfboWHmNlzKOCoBrYby4QDetx4cneu5cBz9Jx9Fu3ySpt4rdzA\nvfhmt4AtOBn0j1ToEfYuqhWfhRv4uT0Pd1MYhnE7Nz3c1Gyy3nV740xlHgp3soo7\nR5JpnekiWRFKvvHXBcjNyFXn1DzRVMSzoMAEWN0CgYEApVDaU2F/xQR6IR0Bjcb+\n1pBFZFOZ18ry5rdxmTAxSVOUeOGRRI/vmsLFYShJDsHN9vmn3LrnlalWtlo4chb6\nh7YVROMRb7DG3LyUsIQKAI9a+pU9QsS5IS/QUrXaAUKK3dqV3JG9mHkxdkOuxfbU\nHGpFEGLx3GjmvOkhQNN6jTUCgYBWikqgvdzuAjwokHbSHg2uQjZp9MA0BdcyFLfP\nguPuZQks031h7Gof64ANo49QjRCs81teDVMf9bGlMnFMfxPsGb7C6tKWiMDL0Hhq\nqhipATjy0sCMk24dFsCZ0oKM8XNyDhNQyU9+YyLNkAAMA3vuXncT66yWhVaUlz3W\neazSrQKBgQCG+x7+q4cvpeFgT8FERTR7XOweBMSOWBQ7myi8NtPnEO3mowcDKfvW\n0L4MrmoIsgiS9/BEnNcukemkpE7oTIFdaf0Ke+x51WMHTMQrkvAcWKxGXnOFp5MB\nXcuBs1aLt23XJKf9bI+6v+ypYY9MCZ5diJ7vR6I/7+aFLugmyEZrHA==\n-----END RSA PRIVATE KEY-----'
publica = key.publickey()
publica.exportKey()
b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxBCxxYLcfPm9AQyvE+58\nd3ZYtKVZ/o1tbzKOIHFvwDXO6RDS8lej3Tt7T4zgMKNNwfLW64IMgWpobgwBpq6m\nQIFUWoKSE5SDrlPWvbrwe1Kt6ESDCsb64DhnXuaqz0rVyAqdRbBHQ4SVcYXOMvub\nTC5Ff5MgRPNBWiFegc5VRLjk1HEECUiNG0cwrXsMHhyunA1Fa/c5EgQ2kTa8CKoa\n5vf0ptcSmHa+WaH665BjSdeM9VG7iMmDu2fASnIoHQ9Kr6KCd545n2d4vuGN9XDq\np+Togb2YOUo/PEoFMSOXkuj0HtWBMK18+8xc7H+HIubUYg81T2wZbhXc1gOmzZD3\n7wIDAQAB\n-----END PUBLIC KEY-----'
De posse da senha podemos recuperar as duas chaves.
key2 = RSA.import_key(chave_privada_encryptada, passphrase=senha)
print(key2==key)
key.publickey().exportKey() == key2.publickey().exportKey()
True
True
Agora podemos encriptar algum documento qualquer. Para máxima segurança, vamos usar o protocolo PKCS#1 OAEP com a algoritmo RSA para encriptar assimetricamente uma chave de sessão AES. Esta chave de sessão pode ser usada para encriptar os dados. Vamos usar o modo EAX para permitir a detecção de modificações não autorizadas.
data = "Minha senha do banco é 123456".encode('utf8')
chave_de_sessão = get_random_bytes(16)
# Encripta a chave de sessão com a a chave RSA pública.
cifra_rsa = PKCS1_OAEP.new(publica)
chave_de_sessão_enc = cifra_rsa.encrypt(chave_de_sessão)
# Encrypta os dados.
cifra_aes = AES.new(chave_de_sessão, AES.MODE_EAX)
texto_cifrado, tag = cifra_aes.encrypt_and_digest(data)
texto_cifrado
b'uB3b\xc6\xe4\x94D\xf2\x1d\xd3\x82\x9f\xcf\xb6-\xdaKi\\a\xd5\xfb\xae\xc8\x8f\xd3\\\xe4\xd1'
O destinatário da mensagem pode então desencriptar a mensagem usando a chave privada para desencriptar a chave da sessão, e com esta a mensagem.
# Desencripta a chave de sessão com a chave privada RSA.
cifra_rsa = PKCS1_OAEP.new(key)
chave_de_sessão = cifra_rsa.decrypt(chave_de_sessão_enc)
# Desencripta os dados com a chave de sessão AES
cifra_aes = AES.new(chave_de_sessão, AES.MODE_EAX, cifra_aes.nonce)
data2 = cifra_aes.decrypt_and_verify(texto_cifrado, tag)
print(data.decode("utf-8"))
Minha senha do banco é 123456